From 10264ad8939f1756029907d0fcd77f785cf59097 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 16:03:29 -0700 Subject: [PATCH 01/21] [Gardening] Tweak comment. --- lib/SIL/IR/SILModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 532b9ea251118..a39b5589304fa 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -253,7 +253,7 @@ SILDefaultWitnessTable * SILModule::lookUpDefaultWitnessTable(const ProtocolDecl *Protocol, bool deserializeLazily) { // Note: we only ever look up default witness tables in the translation unit - // that is currently being compiled, since they SILGen generates them when it + // that is currently being compiled, since SILGen generates them when it // visits the protocol declaration, and IRGen emits them when emitting the // protocol descriptor metadata for the protocol. From e6c03bd278d8c33da45d6f188f0085b7b798471f Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 17:49:00 -0700 Subject: [PATCH 02/21] [Gardening] Tweak comment. --- lib/SIL/Parser/ParseSIL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 22044bc4b33e1..6cb9fa365478c 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -8085,7 +8085,7 @@ ProtocolConformanceRef SILParser::parseProtocolConformanceHelper( return retVal; } -/// Parser a single SIL vtable entry and add it to either \p witnessEntries +/// Parser a single SIL wtable entry and add it to either \p witnessEntries /// or \c conditionalConformances. static bool parseSILWitnessTableEntry( Parser &P, From fba93fc207dc69dc6a634341a9d545706339f1a9 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 15:58:08 -0700 Subject: [PATCH 03/21] [Test] Reenable tbd validation. This was fixed previously. --- .../coroutine_accessors_default_implementations.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/Interpreter/coroutine_accessors_default_implementations.swift b/test/Interpreter/coroutine_accessors_default_implementations.swift index 87d4e3653a1d5..0367016f25ef7 100644 --- a/test/Interpreter/coroutine_accessors_default_implementations.swift +++ b/test/Interpreter/coroutine_accessors_default_implementations.swift @@ -9,8 +9,7 @@ // RUN: -module-name Library \ // RUN: -parse-as-library \ // RUN: -enable-library-evolution \ -// RUN: -emit-module-path %t/Library.swiftmodule \ -// RUN: -validate-tbd-against-ir=none +// RUN: -emit-module-path %t/Library.swiftmodule // RUN: %target-swift-frontend \ // RUN: %t/Executable.swift \ @@ -18,8 +17,7 @@ // RUN: -parse-as-library \ // RUN: -module-name Executable \ // RUN: -I %t \ -// RUN: -o %t/Executable.o \ -// RUN: -validate-tbd-against-ir=none +// RUN: -o %t/Executable.o // RUN: %target-build-swift-dylib(%t/%target-library-name(Library)) \ // RUN: %t/Library.swift \ @@ -28,8 +26,7 @@ // RUN: -enable-library-evolution \ // RUN: -enable-experimental-feature CoroutineAccessors \ // RUN: -emit-module-path %t/Library.swiftmodule \ -// RUN: -module-name Library \ -// RUN: -Xfrontend -validate-tbd-against-ir=none +// RUN: -module-name Library // RUN: %target-build-swift \ // RUN: %t/Executable.o \ From 83192bc41700993065fbdc6ee44ed000c0955d75 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 15:39:49 -0700 Subject: [PATCH 04/21] [NFC] SIL: Delete dead function. --- include/swift/SIL/SILDefaultWitnessTable.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/swift/SIL/SILDefaultWitnessTable.h b/include/swift/SIL/SILDefaultWitnessTable.h index 8fc0072174ffc..e459bf39383fb 100644 --- a/include/swift/SIL/SILDefaultWitnessTable.h +++ b/include/swift/SIL/SILDefaultWitnessTable.h @@ -98,9 +98,6 @@ class SILDefaultWitnessTable : public llvm::ilist_node, /// Get the linkage of the default witness table. SILLinkage getLinkage() const { return Linkage; } - /// Set the linkage of the default witness table. - void setLinkage(SILLinkage l) { Linkage = l; } - void convertToDefinition(ArrayRef entries); ~SILDefaultWitnessTable(); From d31718f7d48959b46b35fb424c307b351d3fd3f2 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 7 Mar 2025 12:28:31 -0800 Subject: [PATCH 05/21] [NFC] SIL: Replace two forward decls with imports. The type layouts are required to use the type in a PointerUnion. --- include/swift/SIL/SILDeclRef.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 5f687108b3bf8..d19a66d34659f 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -19,6 +19,8 @@ #ifndef SWIFT_SIL_SILDECLREF_H #define SWIFT_SIL_SILDECLREF_H +#include "swift/AST/Attr.h" +#include "swift/AST/AutoDiff.h" #include "swift/AST/AvailabilityRange.h" #include "swift/AST/ClangNode.h" #include "swift/AST/GenericSignature.h" @@ -36,7 +38,6 @@ namespace swift { enum class EffectsKind : uint8_t; class AbstractFunctionDecl; class AbstractClosureExpr; - class AutoDiffDerivativeFunctionIdentifier; class ValueDecl; class FuncDecl; class ClosureExpr; @@ -53,7 +54,6 @@ namespace swift { enum class SILLinkage : uint8_t; class AnyFunctionRef; class GenericSignature; - class CustomAttr; /// How a method is dispatched. enum class MethodDispatch { From 5d53d3bdaaad9d9e419436b70b35fe02dc3ed472 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 15:59:10 -0700 Subject: [PATCH 06/21] [NFC] Runtime: Mark parameter const. The implementation doesn't depend on it being non-const and a forthcoming caller will require it be const. --- stdlib/public/runtime/Metadata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 3123c25431e7c..c3a5954425764 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -4007,7 +4007,7 @@ getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self) { SWIFT_CC(swift) static std::pair -getSuperclassMetadata(ClassMetadata *self, bool allowDependency) { +getSuperclassMetadata(const ClassMetadata *self, bool allowDependency) { MetadataRequest request(allowDependency ? MetadataState::NonTransitiveComplete : /*FIXME*/ MetadataState::Abstract, /*non-blocking*/ allowDependency); From 4bfdaed78abbab6d1a91e5edf2a916a24e5b61c5 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 19 Mar 2025 13:22:40 -0700 Subject: [PATCH 07/21] [NFC] Runtime: Extract function. Break this chunk of functionality out into a separate function in preparation for adding another caller. --- stdlib/public/runtime/Metadata.cpp | 69 ++++++++++++++++-------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index c3a5954425764..e93abe04603b6 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3644,6 +3644,43 @@ static void copySuperclassMetadataToSubclass(ClassMetadata *theClass, #endif } +static void installOverrideInVTable( + ClassDescriptor::MethodOverrideDescriptor const &descriptor, + ClassDescriptor::OverrideTableHeader const *overrideTable, + void **classWords) { + // Get the base class and method. + auto *baseClass = cast_or_null(descriptor.Class.get()); + auto *baseMethod = descriptor.Method.get(); + + // If the base method is null, it's an unavailable weak-linked + // symbol. + if (baseClass == nullptr || baseMethod == nullptr) + return; + + // Calculate the base method's vtable offset from the + // base method descriptor. The offset will be relative + // to the base class's vtable start offset. + auto baseClassMethods = baseClass->getMethodDescriptors(); + + // If the method descriptor doesn't land within the bounds of the + // method table, abort. + if (baseMethod < baseClassMethods.begin() || + baseMethod >= baseClassMethods.end()) { + fatalError(0, + "resilient vtable at %p contains out-of-bounds " + "method descriptor %p\n", + overrideTable, baseMethod); + } + + // Install the method override in our vtable. + auto baseVTable = baseClass->getVTableDescriptor(); + auto offset = (baseVTable->getVTableOffset(baseClass) + + (baseMethod - baseClassMethods.data())); + swift_ptrauth_init_code_or_data(&classWords[offset], descriptor.getImpl(), + baseMethod->Flags.getExtraDiscriminator(), + !baseMethod->Flags.isData()); +} + /// Using the information in the class context descriptor, fill in in the /// immediate vtable entries for the class and install overrides of any /// superclass vtable entries. @@ -3667,40 +3704,10 @@ static void initClassVTable(ClassMetadata *self) { if (description->hasOverrideTable()) { auto *overrideTable = description->getOverrideTable(); auto overrideDescriptors = description->getMethodOverrideDescriptors(); - for (unsigned i = 0, e = overrideTable->NumEntries; i < e; ++i) { auto &descriptor = overrideDescriptors[i]; - // Get the base class and method. - auto *baseClass = cast_or_null(descriptor.Class.get()); - auto *baseMethod = descriptor.Method.get(); - - // If the base method is null, it's an unavailable weak-linked - // symbol. - if (baseClass == nullptr || baseMethod == nullptr) - continue; - - // Calculate the base method's vtable offset from the - // base method descriptor. The offset will be relative - // to the base class's vtable start offset. - auto baseClassMethods = baseClass->getMethodDescriptors(); - - // If the method descriptor doesn't land within the bounds of the - // method table, abort. - if (baseMethod < baseClassMethods.begin() || - baseMethod >= baseClassMethods.end()) { - fatalError(0, "resilient vtable at %p contains out-of-bounds " - "method descriptor %p\n", - overrideTable, baseMethod); - } - - // Install the method override in our vtable. - auto baseVTable = baseClass->getVTableDescriptor(); - auto offset = (baseVTable->getVTableOffset(baseClass) + - (baseMethod - baseClassMethods.data())); - swift_ptrauth_init_code_or_data(&classWords[offset], descriptor.getImpl(), - baseMethod->Flags.getExtraDiscriminator(), - !baseMethod->Flags.isData()); + installOverrideInVTable(descriptor, overrideTable, classWords); } } } From c576c61122adf7433a2d6b71a720e27c8d78b933 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 16:07:25 -0700 Subject: [PATCH 08/21] [NFC] Runtime: Extract fields from param aggregate Rather than pass a MethodOverrideDescriptor directly, instead pass the fields from it that are needed by the callee. In preparation for adding another caller which doesn't have a MethodOverrideDescriptor. --- stdlib/public/runtime/Metadata.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index e93abe04603b6..f9a4ecf7d20cf 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3644,13 +3644,13 @@ static void copySuperclassMetadataToSubclass(ClassMetadata *theClass, #endif } -static void installOverrideInVTable( - ClassDescriptor::MethodOverrideDescriptor const &descriptor, - ClassDescriptor::OverrideTableHeader const *overrideTable, - void **classWords) { +template +static void installOverrideInVTable(ContextDescriptor const *baseContext, + MethodDescriptor const *baseMethod, + GetImpl getImpl, void const *table, + void **classWords) { // Get the base class and method. - auto *baseClass = cast_or_null(descriptor.Class.get()); - auto *baseMethod = descriptor.Method.get(); + auto *baseClass = cast_or_null(baseContext); // If the base method is null, it's an unavailable weak-linked // symbol. @@ -3669,14 +3669,14 @@ static void installOverrideInVTable( fatalError(0, "resilient vtable at %p contains out-of-bounds " "method descriptor %p\n", - overrideTable, baseMethod); + table, baseMethod); } // Install the method override in our vtable. auto baseVTable = baseClass->getVTableDescriptor(); auto offset = (baseVTable->getVTableOffset(baseClass) + (baseMethod - baseClassMethods.data())); - swift_ptrauth_init_code_or_data(&classWords[offset], descriptor.getImpl(), + swift_ptrauth_init_code_or_data(&classWords[offset], getImpl(), baseMethod->Flags.getExtraDiscriminator(), !baseMethod->Flags.isData()); } @@ -3707,7 +3707,10 @@ static void initClassVTable(ClassMetadata *self) { for (unsigned i = 0, e = overrideTable->NumEntries; i < e; ++i) { auto &descriptor = overrideDescriptors[i]; - installOverrideInVTable(descriptor, overrideTable, classWords); + installOverrideInVTable( + descriptor.Class.get(), descriptor.Method.get(), + [&descriptor]() { return descriptor.getImpl(); }, overrideTable, + classWords); } } } From 9a9c2ffee8533d1704cdaebdbdabb07c9fffd45f Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 16:15:47 -0700 Subject: [PATCH 09/21] [NFC] Runtime: Use a range-based for loop. Replace this direct use of the count in a table header as the upper bound of a for loop with a range-based for loop over the range which was being indexed into. That range was constructed using that count to begin with. --- stdlib/public/runtime/Metadata.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index f9a4ecf7d20cf..1c81fd8f27c8c 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3704,9 +3704,7 @@ static void initClassVTable(ClassMetadata *self) { if (description->hasOverrideTable()) { auto *overrideTable = description->getOverrideTable(); auto overrideDescriptors = description->getMethodOverrideDescriptors(); - for (unsigned i = 0, e = overrideTable->NumEntries; i < e; ++i) { - auto &descriptor = overrideDescriptors[i]; - + for (auto &descriptor : overrideDescriptors) { installOverrideInVTable( descriptor.Class.get(), descriptor.Method.get(), [&descriptor]() { return descriptor.getImpl(); }, overrideTable, From 5eacbfd9d97308f8c0be5314980ce167d9c2f842 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 13:28:57 -0700 Subject: [PATCH 10/21] [DefaultOverrides] Mangling. --- docs/ABI/Mangling.rst | 1 + include/swift/Demangling/DemangleNodes.def | 1 + lib/Demangling/Demangler.cpp | 3 +++ lib/Demangling/NodePrinter.cpp | 4 ++++ lib/Demangling/OldRemangler.cpp | 5 +++++ lib/Demangling/Remangler.cpp | 6 ++++++ test/Demangle/Inputs/manglings.txt | 3 ++- 7 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 2dcdba7a80658..1dba89461ded3 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -239,6 +239,7 @@ types where the metadata itself has unknown layout.) global ::= global 'Twb' // back deployment thunk global ::= global 'TwB' // back deployment fallback function global ::= global 'Twc' // coro function pointer of a function + global ::= global 'Twd' // default override of a function global ::= entity entity 'TV' // vtable override thunk, derived followed by base global ::= type label-list? 'D' // type mangling for the debugger with label list for function types. global ::= type 'TC' // continuation prototype (not actually used for real symbols) diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 519f839d7e9bd..3ca1a17bae9cb 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -411,6 +411,7 @@ NODE(Integer) NODE(NegativeInteger) NODE(DependentGenericParamValueMarker) NODE(CoroFunctionPointer) +NODE(DefaultOverride) #undef CONTEXT_NODE #undef NODE diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 338b23b130ccb..d60def1087a54 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -156,6 +156,7 @@ bool swift::Demangle::isFunctionAttr(Node::Kind kind) { case Node::Kind::BackDeploymentFallback: case Node::Kind::HasSymbolQuery: case Node::Kind::CoroFunctionPointer: + case Node::Kind::DefaultOverride: return true; default: return false; @@ -3142,6 +3143,8 @@ NodePointer Demangler::demangleThunkOrSpecialization() { case 'B': return createNode(Node::Kind::BackDeploymentFallback); case 'c': return createNode(Node::Kind::CoroFunctionPointer); + case 'd': + return createNode(Node::Kind::DefaultOverride); case 'S': return createNode(Node::Kind::HasSymbolQuery); default: return nullptr; diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index cc221f2938e6b..26d8b0f5cf99c 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -660,6 +660,7 @@ class NodePrinter { case Node::Kind::DependentGenericInverseConformanceRequirement: case Node::Kind::DependentGenericParamValueMarker: case Node::Kind::CoroFunctionPointer: + case Node::Kind::DefaultOverride: return false; } printer_unreachable("bad node kind"); @@ -3494,6 +3495,9 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, case Node::Kind::CoroFunctionPointer: Printer << "coro function pointer to "; return nullptr; + case Node::Kind::DefaultOverride: + Printer << "default override of "; + return nullptr; } printer_unreachable("bad node kind!"); diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 84a51c39740b0..b6a9c3cd3ac70 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1203,6 +1203,11 @@ ManglingError Remangler::mangleCoroFunctionPointer(Node *node, unsigned depth) { return ManglingError::Success; } +ManglingError Remangler::mangleDefaultOverride(Node *node, unsigned depth) { + Buffer << "Twd"; + return ManglingError::Success; +} + ManglingError Remangler::mangleDeallocator(Node *node, EntityContext &ctx, unsigned depth) { return mangleSimpleEntity(node, 'F', "D", ctx, depth + 1); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 020b7804f97a0..a5b630cc4f6f1 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1056,6 +1056,11 @@ ManglingError Remangler::mangleCoroFunctionPointer(Node *node, unsigned depth) { return ManglingError::Success; } +ManglingError Remangler::mangleDefaultOverride(Node *node, unsigned depth) { + Buffer << "Twd"; + return ManglingError::Success; +} + ManglingError Remangler::mangleDependentAssociatedTypeRef(Node *node, unsigned depth) { RETURN_IF_ERROR(mangleIdentifier(node->getFirstChild(), depth)); @@ -1842,6 +1847,7 @@ ManglingError Remangler::mangleGlobal(Node *node, unsigned depth) { case Node::Kind::BackDeploymentFallback: case Node::Kind::HasSymbolQuery: case Node::Kind::CoroFunctionPointer: + case Node::Kind::DefaultOverride: mangleInReverseOrder = true; break; default: diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 6e012a78bb4fd..4818f9a2808fc 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -489,9 +489,10 @@ $s7ToolKit10TypedValueOACs5Error_pIgHTnTrzo_A2CsAD_pIegHiTrzr_TR ---> {T:} reabs $s16sending_mangling16NonSendableKlassCACIegTiTr_A2CIegTxTo_TR ---> {T:} reabstraction thunk helper from @escaping @callee_guaranteed (@in sending sending_mangling.NonSendableKlass) -> sending (@out sending_mangling.NonSendableKlass) to @escaping @callee_guaranteed (@owned sending sending_mangling.NonSendableKlass) -> sending (@owned sending_mangling.NonSendableKlass) $s3red7MyActorC3runyxxyYaKYCXEYaKlFZ ---> static red.MyActor.run(@execution(caller) () async throws -> A) async throws -> A $s5thing1PP1sAA1SVvxTwc ---> coro function pointer to thing.P.s.modify2 : thing.S - _$s15raw_identifiers0020foospace_liaADEDGcjayyF ---> raw_identifiers.`foo space`() -> () _$s15raw_identifiers0018_3times_pgaIGJCFbhayyF ---> raw_identifiers.`3 times`() -> () _$s15raw_identifiers0019test_yeaIIBCEapkagayyF ---> raw_identifiers.`test +`() -> () _$s15raw_identifiers0020pathfoo_yuEHaaCiJskayyF ---> raw_identifiers.`path://foo`() -> () _$s15raw_identifiers10FontWeightO009_100_FpEpdyyFZ ---> static raw_identifiers.FontWeight.`100`() -> () +$s7Library1BC1iSivxTwd ---> default override of Library.B.i.modify2 : Swift.Int +$s7Library1BC1iSivxTwdTwc ---> coro function pointer to default override of Library.B.i.modify2 : Swift.Int From 89e5e3dfca85406f353089e46d06d0fe7dbf6f72 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 15:47:19 -0700 Subject: [PATCH 11/21] [DefaultOverrides] SIL representation. See the comment in SILDefaultOverrideTable.h for details. --- include/swift/SIL/SILDefaultOverrideTable.h | 333 ++++++++++++++++++++ include/swift/SIL/SILModule.h | 37 +++ lib/SIL/IR/CMakeLists.txt | 1 + lib/SIL/IR/SILDefaultOverrideTable.cpp | 96 ++++++ lib/SIL/IR/SILModule.cpp | 26 ++ 5 files changed, 493 insertions(+) create mode 100644 include/swift/SIL/SILDefaultOverrideTable.h create mode 100644 lib/SIL/IR/SILDefaultOverrideTable.cpp diff --git a/include/swift/SIL/SILDefaultOverrideTable.h b/include/swift/SIL/SILDefaultOverrideTable.h new file mode 100644 index 0000000000000..a20d63c3ecc55 --- /dev/null +++ b/include/swift/SIL/SILDefaultOverrideTable.h @@ -0,0 +1,333 @@ +//===--- SILDefaultOverrideTable.h ------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// TLDR: class : protocol :: SILDefaultOverrideTable : SILDefaultWitnessTable +// +// This file defines the SILDefaultOverrideTable class, which is used to +// provide default override implementations of class routines which have been +// come to implement the same semantic class member that was previously +// implemented by a different routine. As with SILDefaultWitnessTable, this +// type enables IRGen to generate metadata which in turn allows the runtime to +// instantiate vtables which contain these default overrides when they are +// needed: in the vtables of subclasses which were emitted prior to the +// replacement of the routine that implements the semantic member AND which +// already provided an override of the routine that previously implemented the +// semantic member. +// +// +--Example--------------------------------------------------------------{{ -+ +// +// ResilientFramework v1: +// open class C { +// open var x: X { +// _read { +// ... +// } +// } +// } +// +// func useCOrDerived(_ c: C) { +// // calls C.x._read +// let x = C.x +// } +// +// ResilientFramework v2: +// open class C { +// open var: x: X { +// read { +// ... +// } +// // compiler generated thunk +// _read { +// yield x.read() // this isn't actually expressible in Swift +// } +// } +// } +// +// func useCOrDerived(_ c: C) { +// // calls C.x.read (!!!) +// let x = C.x +// pass(x) +// } +// +// The "semantic class member" here is the "reader" for C.x. In 1, this member +// was implemented by C.x._read. In 2.0, this was implemented by C.x.read. In +// other words, when C.x is read from some instance `Instance` of C or a +// subclass (e.g. in useCOrDerived), the C.x.read routine of `Instance`'s class +// is used. More concretely, the routine stored in the C.x.read slot from +// class(`Instance`)'s vtable is dispatched to. +// +// Without the solution provided SILDefaultOverrideTable, that slot could +// contain the wrong routine!, namely C's implementation of C.x.read when the +// subclass overrode C.x._read, as illustrated below. +// +// ClientBinary, built against version 1: +// class D : C { +// override var x: X { +// _read { +// ... +// } +// } +// } +// +// When C.x._read is called on an instance of D, D's override of that routine is +// called. When running ClientBinary against ResilientFramework v1, this is +// sufficient: useCOrDerived's reading of x dispatches to the routine in the +// x._read slot of the vtable, which D's vtable contains an override of. +// +// In detail, here's what happens at runtime: {{ // Runtime situation with v1 +// +// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived. +// func useCOrDerived(_ c: C) { +// let vtable = typeof(c).VTable +// let reader = vtable[#slot(x._read)] +// let x = reader(c) +// pass(x) +// } +// +// +- C.VTable ----------+ +// | slot | impl | +// +---------+-----------+ +// | x._read | C.x._read | +// +---------+-----------+ +// +// +- D.VTable ----------+ +// | slot | impl | +// +---------+-----------+ +// | x._read | D.x._read | +// +---------+-----------+ +// +// When an instance d of D is passed, execution will proceed thus: +// +// func useCOrDerived(_ c: C = d: D) { +// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable +// let reader = vtable[#slot(x._read)] = D.VTable[#slot(x._read)] = D.x._read +// let x = reader(c) = D.x._read(d) +// pass (x) +// } +// +// In other words, D's override of x._read will be looked up in D's vtable and +// invoked. +// +// }} // Runtime situation with v1 +// +// Now, suppose no additional machinery were added to the runtime, and consider +// the same situation when running against ResilientFramework v2. When running +// ClientBinary against ResilientFramework v2, useCOrDerived's reading of x +// dispatches to the routine in the x.read slot of the vtable (NOT the x._read +// slot). And D's vtable contains no override of that routine! +// +// Here's the v2 runtime situation WITHOUT additional machinery: {{ // BAD runtime situation with v2 +// +// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived. +// func useCOrDerived(_ c: C) { +// let vtable = typeof(c).VTable +// let reader = vtable[#slot(x.read)] // NOTE! This is now x.read NOT x._read! +// let x = reader(c) +// pass(x) +// } +// +// +- C.VTable ----------+ +// | slot | impl | +// +---------+-----------+ +// | x._read | C.x._read | +// | x.read | C.x.read | +// +---------+-----------+ +// +// +- D.VTable ----------+ +// | slot | impl | +// +---------+-----------+ +// | x._read | D.x._read | +// | x.read | C.x.read | <- The bad entry! +// +---------+-----------+ +// +// When an instance d of D is passed, execution will proceed thus: +// +// func useCOrDerived(_ c: C = d: D) { +// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable +// // The wrong implementation is looked up! +// let reader = vtable[#slot(x.read)] = D.VTable[#slot(x.read)] = C.x.read +// // The wrong implementation is called! +// let x = reader(c) = C.x._read(d) +// pass (x) +// } +// +// In other words, D's override of x._read is ignored, and C's implementation is +// used. +// +// }} // BAD runtime situation with v2 +// +// As described so far, this problem has a few solutions. Those not taken are +// mentioned now with their reason for rejection. +// +// Rejected solutions: +// +// (a) Don't change the slot dispatched to by useCOrDerived. +// +// Rejection rationale: The new routine C.x.read has an improved ABI. +// +// (b) Reuse D.x._read in the x.read slot. +// +// Rejection rationale: D.x._read has the ABI appropriate for x._read and +// not the ABI appropriate for x.read. +// +// Accepted solution: +// +// Provide an implementation of x.read for use by D. The implementation is as +// follows, in pseudo-code that can't be written in Swift: +// +// ResilientFrameworkv2: +// func C_x_read_default_override(self: C) yields_once -> X { +// yield self.x._read // vtable dispatch +// } +// +// At runtime, when the VTable for D is assembled (which occurs at runtime +// because D's is a subclass of C which is defined in a resilient framework), +// this default override is slotted into D's VTable for x.read: +// +// +- D.VTable --------------------------+ +// | slot | impl | +// +---------+---------------------------+ +// | x._read | D.x._read | +// | x.read | C_x_read_default_override | +// +---------+---------------------------+ +// +// +-}} Example ---------------------------------------------------------------+ +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H +#define SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H + +#include "swift/SIL/SILAllocated.h" +#include "swift/SIL/SILDeclRef.h" +#include "llvm/ADT/ilist.h" + +namespace swift { + +struct PrintOptions; + +/// Map the pair of overridable member and semantically equivalent overridable +/// member to the SIL-level entity used to override the former for subclasses +/// which do override the latter. +/// +/// In the example above, an entry in the table would be +/// +/// (C.x.read, C.x._read) -> C_x_read_default_override. +class SILDefaultOverrideTable + : public llvm::ilist_node, + public SILAllocated { +public: + struct Entry { + /// The method for which a default implementation may be needed by + /// subclasses in old clients. + SILDeclRef method; + /// The method the presence of whose override in subclasses in old clients + /// necessitates the provision of the default override. + SILDeclRef original; + /// The default override of `method` to be provided when `original` is + /// present. + SILFunction *impl; + }; + +private: + friend class SILModule; + + /// The containing module. + SILModule &module; + + /// The linkage of the override table. + SILLinkage linkage; + + /// The ClassDecl this table pertains to. + const ClassDecl *decl; + + /// The contents of the table. + MutableArrayRef entries; + + enum class State { + Declared, + Defined, + }; + /// The table's current state. This must eventually become ::Defined. + State state; + + /// Private constructor for making SILDefaultOverrideTable declarations. + SILDefaultOverrideTable(SILModule &M, SILLinkage Linkage, + const ClassDecl *decl); + + void registerWithModule(); + +public: + /// Declare a new table. + static SILDefaultOverrideTable *declare(SILModule &module, SILLinkage linkage, + const ClassDecl *decl); + + /// Define a new table. + static SILDefaultOverrideTable *define(SILModule &module, SILLinkage linkage, + const ClassDecl *decl, + ArrayRef entries); + + /// A name unique among override tables but not symbols. + std::string getUniqueName() const; + + /// Get the linkage of the default override table. + SILLinkage getLinkage() const { return linkage; } + + /// Set the linkage of the default override table. + void setLinkage(SILLinkage linkage) { this->linkage = linkage; } + + /// Promote the current table from a declaration to a definition consisting of + /// the specified entries. + void define(ArrayRef entries); + + ~SILDefaultOverrideTable(); + + SILModule &getModule() const { return module; } + + /// Return true if this is a declaration with no body. + bool isDeclaration() const { return state == State::Declared; } + + /// Return the ClassDecl this table pertains to. + const ClassDecl *getClass() const { return decl; } + + /// Return all of the default override table entries. + ArrayRef getEntries() const { return entries; } + + /// Verify that the default override table is well-formed. + void verify(const SILModule &M) const; +}; + +} // namespace swift + +//===----------------------------------------------------------------------===// +// ilist_traits for SILDefaultOverrideTable +//===----------------------------------------------------------------------===// + +namespace llvm { + +template <> +struct ilist_traits<::swift::SILDefaultOverrideTable> + : public ilist_node_traits<::swift::SILDefaultOverrideTable> { + using SILDefaultOverrideTable = ::swift::SILDefaultOverrideTable; + +public: + static void deleteNode(SILDefaultOverrideTable *WT) { + WT->~SILDefaultOverrideTable(); + } + +private: + void createNode(const SILDefaultOverrideTable &); +}; + +} // namespace llvm + +#endif diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index d265ff6c9e00b..1ea387fcbd523 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -29,6 +29,7 @@ #include "swift/SIL/Notifications.h" #include "swift/SIL/SILCoverageMap.h" #include "swift/SIL/SILDeclRef.h" +#include "swift/SIL/SILDefaultOverrideTable.h" #include "swift/SIL/SILDefaultWitnessTable.h" #include "swift/SIL/SILDifferentiabilityWitness.h" #include "swift/SIL/SILFunction.h" @@ -160,6 +161,7 @@ class SILModule { using PropertyListType = llvm::ilist; using WitnessTableListType = llvm::ilist; using DefaultWitnessTableListType = llvm::ilist; + using DefaultOverrideTableListType = llvm::ilist; using DifferentiabilityWitnessListType = llvm::ilist; using SILMoveOnlyDeinitListType = llvm::ArrayRef; @@ -184,6 +186,7 @@ class SILModule { friend SILBasicBlock; friend SILCoverageMap; friend SILDefaultWitnessTable; + friend SILDefaultOverrideTable; friend SILDifferentiabilityWitness; friend SILFunction; friend SILGlobalVariable; @@ -265,6 +268,12 @@ class SILModule { /// The list of SILDefaultWitnessTables in the module. DefaultWitnessTableListType defaultWitnessTables; + /// Lookup table for SIL default override tables from classes. + llvm::DenseMap + DefaultOverrideTableMap; + + DefaultOverrideTableListType defaultOverrideTables; + /// Lookup table for SIL differentiability witnesses, keyed by mangled name. llvm::StringMap DifferentiabilityWitnessMap; @@ -717,6 +726,22 @@ class SILModule { return {defaultWitnessTables.begin(), defaultWitnessTables.end()}; } + using default_override_table_iterator = DefaultOverrideTableListType::iterator; + using default_override_table_const_iterator = DefaultOverrideTableListType::const_iterator; + DefaultOverrideTableListType &getDefaultOverrideTableList() { return defaultOverrideTables; } + const DefaultOverrideTableListType &getDefaultOverrideTableList() const { return defaultOverrideTables; } + default_override_table_iterator default_override_table_begin() { return defaultOverrideTables.begin(); } + default_override_table_iterator default_override_table_end() { return defaultOverrideTables.end(); } + default_override_table_const_iterator default_override_table_begin() const { return defaultOverrideTables.begin(); } + default_override_table_const_iterator default_override_table_end() const { return defaultOverrideTables.end(); } + iterator_range getDefaultOverrideTables() { + return {defaultOverrideTables.begin(), defaultOverrideTables.end()}; + } + iterator_range + getDefaultOverrideTables() const { + return {defaultOverrideTables.begin(), defaultOverrideTables.end()}; + } + using differentiability_witness_iterator = DifferentiabilityWitnessListType::iterator; using differentiability_witness_const_iterator = DifferentiabilityWitnessListType::const_iterator; DifferentiabilityWitnessListType &getDifferentiabilityWitnessList() { return differentiabilityWitnesses; } @@ -871,6 +896,12 @@ class SILModule { SILDeclRef Requirement, bool deserializeLazily=true); + /// Look up the SILDefaultOverrideTable containing the default overrides to be + /// applied to subclasses of a resilient class. + SILDefaultOverrideTable * + lookUpDefaultOverrideTable(const ClassDecl *decl, + bool deserializeLazily = true); + /// Look up the VTable mapped to the given ClassDecl. Returns null on failure. SILVTable *lookUpVTable(const ClassDecl *C, bool deserializeLazily = true); @@ -914,6 +945,12 @@ class SILModule { /// Deletes a dead witness table. void deleteWitnessTable(SILWitnessTable *Wt); + /// Define a default override table for the indicated resilient class \p decl + /// consisting of the specified \p entries. + SILDefaultOverrideTable *createDefaultOverrideTableDefinition( + const ClassDecl *decl, SILLinkage linkage, + ArrayRef entries); + /// Return the stage of processing this module is at. SILStage getStage() const { return Stage; } diff --git a/lib/SIL/IR/CMakeLists.txt b/lib/SIL/IR/CMakeLists.txt index 336529098ab2f..0a570f0b0056a 100644 --- a/lib/SIL/IR/CMakeLists.txt +++ b/lib/SIL/IR/CMakeLists.txt @@ -14,6 +14,7 @@ target_sources(swiftSIL PRIVATE SILDebugInfoExpression.cpp SILDebugScope.cpp SILDeclRef.cpp + SILDefaultOverrideTable.cpp SILDefaultWitnessTable.cpp SILDifferentiabilityWitness.cpp SILFunction.cpp diff --git a/lib/SIL/IR/SILDefaultOverrideTable.cpp b/lib/SIL/IR/SILDefaultOverrideTable.cpp new file mode 100644 index 0000000000000..65ca08ce4f530 --- /dev/null +++ b/lib/SIL/IR/SILDefaultOverrideTable.cpp @@ -0,0 +1,96 @@ +//===--- SILDefaultOverrideTable.cpp --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the SILDefaultOverrideTable class, which is used to +// provide default override implementations of class routines which have been +// come to implement the same semantic class member that was previously +// implemented by a different routine. As with SILDefaultWitnessTable, this +// type enables IRGen to generate metadata which in turn allows the runtime to +// instantiate vtables which contain these default overrides when they are +// needed: in the vtables of subclasses which were emitted prior to the +// replacement of the routine that implements the semantic member AND which +// already provided an override of the routine that previously implemented the +// semantic member. +// +//===----------------------------------------------------------------------===// + +#include "swift/SIL/SILDefaultOverrideTable.h" +#include "swift/AST/ASTMangler.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILModule.h" + +using namespace swift; + +SILDefaultOverrideTable::SILDefaultOverrideTable(SILModule &M, + SILLinkage Linkage, + const ClassDecl *decl) + : module(M), linkage(Linkage), decl(decl), entries({}), + state(State::Declared) {} + +SILDefaultOverrideTable * +SILDefaultOverrideTable::declare(SILModule &module, SILLinkage linkage, + const ClassDecl *decl) { + auto *buffer = module.allocate(1); + SILDefaultOverrideTable *table = + ::new (buffer) SILDefaultOverrideTable(module, linkage, decl); + + table->registerWithModule(); + + return table; +} + +SILDefaultOverrideTable *SILDefaultOverrideTable::define( + SILModule &module, SILLinkage linkage, const ClassDecl *decl, + ArrayRef entries) { + auto *buffer = module.allocate(1); + SILDefaultOverrideTable *table = + ::new (buffer) SILDefaultOverrideTable(module, linkage, decl); + table->define(entries); + + table->registerWithModule(); + + // Return the resulting default witness table. + return table; +} + +void SILDefaultOverrideTable::registerWithModule() { + assert(module.DefaultOverrideTableMap.find(decl) == + module.DefaultOverrideTableMap.end()); + + module.DefaultOverrideTableMap[decl] = this; + module.defaultOverrideTables.push_back(this); +} + +std::string SILDefaultOverrideTable::getUniqueName() const { + Mangle::ASTMangler Mangler(decl->getASTContext()); + return Mangler.mangleTypeWithoutPrefix( + decl->getDeclaredInterfaceType()->getCanonicalType()); +} + +void SILDefaultOverrideTable::define(ArrayRef entries) { + assert(state == State::Declared); + state = State::Defined; + + this->entries = module.allocateCopy(entries); + + // Retain referenced functions. + for (auto entry : getEntries()) { + entry.impl->incrementRefCount(); + } +} + +SILDefaultOverrideTable::~SILDefaultOverrideTable() { + // Release referenced functions. + for (auto entry : getEntries()) { + entry.impl->decrementRefCount(); + } +} diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index a39b5589304fa..35170a5c85958 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -23,6 +23,7 @@ #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/Notifications.h" #include "swift/SIL/SILDebugScope.h" +#include "swift/SIL/SILDefaultOverrideTable.h" #include "swift/SIL/SILMoveOnlyDeinit.h" #include "swift/SIL/SILRemarkStreamer.h" #include "swift/SIL/SILValue.h" @@ -290,6 +291,31 @@ void SILModule::deleteWitnessTable(SILWitnessTable *Wt) { witnessTables.erase(Wt); } +SILDefaultOverrideTable *SILModule::createDefaultOverrideTableDefinition( + const ClassDecl *decl, SILLinkage linkage, + ArrayRef entries) { + return SILDefaultOverrideTable::define(*this, linkage, decl, entries); +} + +SILDefaultOverrideTable * +SILModule::lookUpDefaultOverrideTable(const ClassDecl *decl, + bool deserializeLazily) { + // Note: we only ever look up default override tables in the translation unit + // that is currently being compiled, since SILGen generates them when it + // visits the class declaration, and IRGen emits them when emitting the + // class descriptor metadata for the class. + + auto found = DefaultOverrideTableMap.find(decl); + if (found == DefaultOverrideTableMap.end()) { + if (deserializeLazily) { + } + + return nullptr; + } + + return found->second; +} + const IntrinsicInfo &SILModule::getIntrinsicInfo(Identifier ID) { unsigned OldSize = IntrinsicIDCache.size(); IntrinsicInfo &Info = IntrinsicIDCache[ID]; From 317a37969399e6f30d9038d918110c9340c60a90 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 18:04:55 -0700 Subject: [PATCH 12/21] [DefaultOverrides] SIL printing/parsing. --- include/swift/AST/DiagnosticsParse.def | 8 ++ include/swift/AST/TokenKinds.def | 1 + include/swift/Parse/ParseSILSupport.h | 1 + include/swift/SIL/SILDefaultOverrideTable.h | 12 ++ lib/Parse/ParseDecl.cpp | 2 + lib/SIL/IR/SILPrinter.cpp | 66 +++++++++++ lib/SIL/Parser/ParseSIL.cpp | 115 ++++++++++++++++++++ lib/SIL/Parser/SILParserState.h | 1 + test/SIL/Parser/default_override.sil | 44 ++++++++ 9 files changed, 250 insertions(+) create mode 100644 test/SIL/Parser/default_override.sil diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index b4f3be3adb315..538ac66085532 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -763,6 +763,14 @@ ERROR(sil_witness_assoc_conf_not_found,none, ERROR(sil_witness_protocol_conformance_not_found,none, "sil protocol conformance not found", ()) +// SIL default override table +ERROR(sil_default_override_func_not_found,none, + "sil function not found %0", (Identifier)) +ERROR(sil_default_override_class_not_found,none, + "sil class not found %0", (Identifier)) +ERROR(sil_default_override_decl_not_class,none, + "sil decl found for %0 is not a class", (Identifier)) + // SIL differentiability witnesses ERROR(sil_diff_witness_expected_token,PointsToFirstBadToken, "expected '%0' in differentiability witness", (StringRef)) diff --git a/include/swift/AST/TokenKinds.def b/include/swift/AST/TokenKinds.def index 7ec71bee19744..11fc1469cb32d 100644 --- a/include/swift/AST/TokenKinds.def +++ b/include/swift/AST/TokenKinds.def @@ -272,6 +272,7 @@ SIL_KEYWORD(sil_moveonlydeinit) SIL_KEYWORD(sil_global) SIL_KEYWORD(sil_witness_table) SIL_KEYWORD(sil_default_witness_table) +SIL_KEYWORD(sil_default_override_table) SIL_KEYWORD(sil_differentiability_witness) SIL_KEYWORD(sil_coverage_map) SIL_KEYWORD(sil_scope) diff --git a/include/swift/Parse/ParseSILSupport.h b/include/swift/Parse/ParseSILSupport.h index 4a6fce7a38907..326205ecae08e 100644 --- a/include/swift/Parse/ParseSILSupport.h +++ b/include/swift/Parse/ParseSILSupport.h @@ -33,6 +33,7 @@ namespace swift { virtual bool parseSILGlobal(Parser &P) = 0; virtual bool parseSILWitnessTable(Parser &P) = 0; virtual bool parseSILDefaultWitnessTable(Parser &P) = 0; + virtual bool parseSILDefaultOverrideTable(Parser &P) = 0; virtual bool parseSILDifferentiabilityWitness(Parser &P) = 0; virtual bool parseSILCoverageMap(Parser &P) = 0; virtual bool parseSILProperty(Parser &P) = 0; diff --git a/include/swift/SIL/SILDefaultOverrideTable.h b/include/swift/SIL/SILDefaultOverrideTable.h index a20d63c3ecc55..aecdac3a9b6a9 100644 --- a/include/swift/SIL/SILDefaultOverrideTable.h +++ b/include/swift/SIL/SILDefaultOverrideTable.h @@ -236,6 +236,12 @@ class SILDefaultOverrideTable /// The default override of `method` to be provided when `original` is /// present. SILFunction *impl; + + /// Print the entry. + void print(llvm::raw_ostream &os, bool verbose = false) const; + + /// Dump the entry to stderr. + void dump() const; }; private: @@ -304,6 +310,12 @@ class SILDefaultOverrideTable /// Verify that the default override table is well-formed. void verify(const SILModule &M) const; + + /// Print the default override table. + void print(llvm::raw_ostream &os, bool verbose = false) const; + + /// Dump the default override table to stderr. + void dump() const; }; } // namespace swift diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f2f378f6c7e9a..7b770e0717c5a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -289,6 +289,7 @@ bool Parser::parseTopLevelSIL() { CASE_SIL(sil_global, SILGlobal) CASE_SIL(sil_witness_table, SILWitnessTable) CASE_SIL(sil_default_witness_table, SILDefaultWitnessTable) + CASE_SIL(sil_default_override_table, SILDefaultOverrideTable) CASE_SIL(sil_differentiability_witness, SILDifferentiabilityWitness) CASE_SIL(sil_coverage_map, SILCoverageMap) CASE_SIL(sil_property, SILProperty) @@ -5944,6 +5945,7 @@ bool Parser::isStartOfSILDecl() { case tok::kw_sil_global: case tok::kw_sil_witness_table: case tok::kw_sil_default_witness_table: + case tok::kw_sil_default_override_table: case tok::kw_sil_differentiability_witness: case tok::kw_sil_coverage_map: case tok::kw_sil_scope: diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 357eb0039d8aa..2258b85dce3db 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3876,6 +3876,28 @@ printSILDefaultWitnessTables(SILPrintContext &Ctx, wt->print(Ctx.OS(), Ctx.printVerbose()); } +static void printSILDefaultOverrideTables( + SILPrintContext &Ctx, + const SILModule::DefaultOverrideTableListType &tables) { + if (!Ctx.sortSIL()) { + for (const auto &table : tables) + table.print(Ctx.OS(), Ctx.printVerbose()); + return; + } + + std::vector sorted; + sorted.reserve(tables.size()); + for (const auto &table : tables) + sorted.push_back(&table); + std::sort(sorted.begin(), sorted.end(), + [](const auto *left, const auto *right) -> bool { + return left->getClass()->getName().compare( + right->getClass()->getName()) == -1; + }); + for (const auto *table : sorted) + table->print(Ctx.OS(), Ctx.printVerbose()); +} + static void printSILDifferentiabilityWitnesses( SILPrintContext &Ctx, const SILModule::DifferentiabilityWitnessListType &diffWitnesses) { @@ -4111,6 +4133,7 @@ void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, printSILVTables(PrintCtx, getVTables()); printSILWitnessTables(PrintCtx, getWitnessTableList()); printSILDefaultWitnessTables(PrintCtx, getDefaultWitnessTableList()); + printSILDefaultOverrideTables(PrintCtx, getDefaultOverrideTableList()); printSILCoverageMaps(PrintCtx, getCoverageMaps()); printSILProperties(PrintCtx, getPropertyList()); printSILMoveOnlyDeinits(PrintCtx, getMoveOnlyDeinits()); @@ -4365,6 +4388,49 @@ void SILDefaultWitnessTable::dump() const { print(llvm::errs()); } +void SILDefaultOverrideTable::Entry::print(llvm::raw_ostream &out, + bool verbose) const { + PrintOptions QualifiedSILTypeOptions = PrintOptions::printQualifiedSILType(); + out << " "; + // #replacement.declref : #original.declref : @function + method.print(out); + out << ": "; + original.print(out); + out << ": "; + + auto *decl = method.getDecl(); + QualifiedSILTypeOptions.CurrentModule = + decl->getDeclContext()->getParentModule(); + decl->getInterfaceType().print(out, QualifiedSILTypeOptions); + out << " : "; + impl->printName(out); + out << "\t// " << demangleSymbol(impl->getName()); + out << '\n'; +} + +void SILDefaultOverrideTable::Entry::dump() const { + print(llvm::errs(), /*verbose=*/false); +} + +void SILDefaultOverrideTable::print(llvm::raw_ostream &OS, bool Verbose) const { + // sil_default_override_table [] + PrintOptions QualifiedSILTypeOptions = PrintOptions::printQualifiedSILType(); + OS << "sil_default_override_table "; + printLinkage(OS, getLinkage(), ForDefinition); + OS << decl->getName() << " {\n"; + + PrintOptions options = PrintOptions::printSIL(); + options.GenericSig = decl->getGenericSignatureOfContext().getPointer(); + + for (auto &entry : getEntries()) { + entry.print(OS, Verbose); + } + + OS << "}\n\n"; +} + +void SILDefaultOverrideTable::dump() const { print(llvm::errs()); } + void SILDifferentiabilityWitness::print(llvm::raw_ostream &OS, bool verbose) const { OS << "// differentiability witness for " diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 6cb9fa365478c..ce77bc4d9dfce 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -7857,6 +7857,27 @@ bool SILParserState::parseSILMoveOnlyDeinit(Parser &parser) { return false; } +static ClassDecl *parseClassDecl(Parser &P, SILParser &SP) { + Identifier DeclName; + SourceLoc DeclLoc; + if (SP.parseSILIdentifier(DeclName, DeclLoc, diag::expected_sil_value_name)) + return nullptr; + + // Find the protocol decl. The protocol can be imported. + llvm::PointerUnion Res = + lookupTopDecl(P, DeclName, /*typeLookup=*/true); + assert(Res.is() && "Protocol look-up should return a Decl"); + ValueDecl *VD = Res.get(); + if (!VD) { + P.diagnose(DeclLoc, diag::sil_default_override_class_not_found, DeclName); + return nullptr; + } + auto *decl = dyn_cast(VD); + if (!decl) + P.diagnose(DeclLoc, diag::sil_default_override_decl_not_class, DeclName); + return decl; +} + static ProtocolDecl *parseProtocolDecl(Parser &P, SILParser &SP) { Identifier DeclName; SourceLoc DeclLoc; @@ -8444,6 +8465,100 @@ bool SILParserState::parseSILDefaultWitnessTable(Parser &P) { return false; } +/// Parser a single SIL default override table entry and add it to \p entries. +/// sil-default-override-entry: +/// SILDeclRef ':' SILDeclRef ':' @SILFunctionName +static bool parseSILDefaultOverrideTableEntry( + Parser &P, SILModule &M, ClassDecl *proto, GenericSignature witnessSig, + GenericParamList *witnessParams, SILParser &parser, + std::vector &entries) { + Identifier EntryKeyword; + SourceLoc KeywordLoc; + + SILDeclRef replacement; + if (parser.parseSILDeclRef(replacement, true) || + P.parseToken(tok::colon, diag::expected_sil_witness_colon)) + return true; + + SILDeclRef original; + if (parser.parseSILDeclRef(original, true) || + P.parseToken(tok::colon, diag::expected_sil_witness_colon)) + return true; + + SILFunction *impl = nullptr; + Identifier implName; + SourceLoc implLoc; + if (P.parseToken(tok::at_sign, diag::expected_sil_function_name) || + parser.parseSILIdentifier(implName, implLoc, + diag::expected_sil_value_name)) + return true; + + impl = M.lookUpFunction(implName.str()); + if (!impl) { + P.diagnose(implLoc, diag::sil_default_override_func_not_found, implName); + return true; + } + + entries.push_back( + SILDefaultOverrideTable::Entry{replacement, original, impl}); + + return false; +} + +/// decl-sil-default-override ::= 'sil_default_override_table' +/// sil-linkage identifier +/// decl-sil-default-override-body +/// decl-sil-default-override-body: +/// '{' sil-default-override-entry* '}' +/// sil-default-override-entry: +/// SILDeclRef ':' SILDeclRef ':' @SILFunctionName +bool SILParserState::parseSILDefaultOverrideTable(Parser &P) { + P.consumeToken(tok::kw_sil_default_override_table); + SILParser OverrideState(P); + + // Parse the linkage. + std::optional Linkage; + parseSILLinkage(Linkage, P); + + // Parse the class. + ClassDecl *decl = parseClassDecl(P, OverrideState); + if (!decl) + return true; + + OverrideState.ContextGenericSig = decl->getGenericSignature(); + OverrideState.ContextGenericParams = decl->getGenericParams(); + + // Parse the body. + SourceLoc LBraceLoc = P.Tok.getLoc(); + P.consumeToken(tok::l_brace); + + // We need to turn on InSILBody to parse SILDeclRef. + Lexer::SILBodyRAII Tmp(*P.L); + + // Parse the entry list. + std::vector entries; + + if (P.Tok.isNot(tok::r_brace)) { + do { + if (parseSILDefaultOverrideTableEntry( + P, M, decl, decl->getGenericSignature(), decl->getGenericParams(), + OverrideState, entries)) + return true; + } while (P.Tok.isNot(tok::r_brace) && P.Tok.isNot(tok::eof)); + } + + SourceLoc RBraceLoc; + P.parseMatchingToken(tok::r_brace, RBraceLoc, diag::expected_sil_rbrace, + LBraceLoc); + + // Default to public linkage. + if (!Linkage) + Linkage = SILLinkage::Public; + + SILDefaultOverrideTable::define(M, *Linkage, decl, entries); + return false; +} + /// decl-sil-differentiability-witness ::= /// 'sil_differentiability_witness' /// ('[' 'serialized' ']')? diff --git a/lib/SIL/Parser/SILParserState.h b/lib/SIL/Parser/SILParserState.h index 4d18295451c2d..c18d2865cf112 100644 --- a/lib/SIL/Parser/SILParserState.h +++ b/lib/SIL/Parser/SILParserState.h @@ -58,6 +58,7 @@ class SILParserState : public SILParserStateBase { bool parseSILGlobal(Parser &P) override; bool parseSILWitnessTable(Parser &P) override; bool parseSILDefaultWitnessTable(Parser &P) override; + bool parseSILDefaultOverrideTable(Parser &P) override; bool parseSILDifferentiabilityWitness(Parser &P) override; bool parseSILCoverageMap(Parser &P) override; bool parseSILProperty(Parser &P) override; diff --git a/test/SIL/Parser/default_override.sil b/test/SIL/Parser/default_override.sil new file mode 100644 index 0000000000000..19cc6b35bfd27 --- /dev/null +++ b/test/SIL/Parser/default_override.sil @@ -0,0 +1,44 @@ +// RUN: %target-sil-opt %s \ +// RUN: -enable-library-evolution \ +// RUN: -enable-experimental-feature CoroutineAccessors \ +// RUN: | %FileCheck %s + +// REQUIRES: swift_feature_CoroutineAccessors + +sil_stage canonical + +import Builtin + +struct Int { + var _value: Builtin.Int64 +} + +open class B { + open var i: Int { read set } +} + +sil @B_i_read : $@yield_once @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_read2 : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_set : $@convention(method) (Int, @guaranteed B) -> () +sil @B_i_modify : $@yield_once @convention(method) (@guaranteed B) -> @yields @inout Int +sil @B_i_modify2 : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields @inout Int + +sil_vtable B { + #B.i!read: (B) -> () -> () : @B_i_read + #B.i!read2: (B) -> () -> () : @B_i_read2 + #B.i!setter: (B) -> (Int) -> () : @B_i_set + #B.i!modify: (B) -> () -> () : @B_i_modify + #B.i!modify2: (B) -> () -> () : @B_i_modify2 +} + +sil @B_i_read2_default_override : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_modify2_default_override : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields @inout Int + +// CHECK-LABEL: sil_default_override_table B { +// CHECK-NEXT: #B.i!read2: #B.i!read: (B) -> () -> () : @B_i_read2_default_override +// CHECK-NEXT: #B.i!modify2: #B.i!modify: (B) -> () -> () : @B_i_modify2_default_override +// CHECK-NEXT: } +sil_default_override_table B { + #B.i!read2: #B.i!read: (B) -> () -> () : @B_i_read2_default_override + #B.i!modify2: #B.i!modify: (B) -> () -> () : @B_i_modify2_default_override +} From a3ba93609ead11df6fd4a6ce60484fa05e8cb2fe Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 16:24:03 -0700 Subject: [PATCH 13/21] [DefaultOverrides] SIL de/serialization. --- include/swift/SIL/Notifications.h | 23 +++ .../swift/Serialization/SerializedSILLoader.h | 5 + lib/SIL/IR/Notifications.cpp | 4 + lib/SIL/IR/SILModule.cpp | 7 + lib/Serialization/DeserializeSIL.cpp | 195 +++++++++++++++++- lib/Serialization/DeserializeSIL.h | 17 ++ lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SILFormat.h | 21 +- lib/Serialization/Serialization.cpp | 3 + lib/Serialization/SerializeSIL.cpp | 75 ++++++- lib/Serialization/SerializedSILLoader.cpp | 14 ++ test/SIL/Serialization/default_override.sil | 46 +++++ 12 files changed, 401 insertions(+), 11 deletions(-) create mode 100644 test/SIL/Serialization/default_override.sil diff --git a/include/swift/SIL/Notifications.h b/include/swift/SIL/Notifications.h index 11e77096897f2..d06e471348da1 100644 --- a/include/swift/SIL/Notifications.h +++ b/include/swift/SIL/Notifications.h @@ -27,6 +27,7 @@ class SILNode; class ModuleDecl; class SILFunction; class SILWitnessTable; +class SILDefaultOverrideTable; class SILDefaultWitnessTable; class SILGlobalVariable; class SILVTable; @@ -50,6 +51,12 @@ class DeserializationNotificationHandlerBase { virtual void didDeserializeWitnessTableEntries(ModuleDecl *mod, SILWitnessTable *wt) = 0; + /// Observe that we successfully deserialized a default override table's + /// entries. + virtual void + didDeserializeDefaultOverrideTableEntries(ModuleDecl *mod, + SILDefaultOverrideTable *ot) = 0; + /// Observe that we successfully deserialized a default witness table's /// entries. virtual void @@ -69,6 +76,10 @@ class DeserializationNotificationHandlerBase { virtual void didDeserialize(ModuleDecl *mod, SILDefaultWitnessTable *wtable) = 0; + /// Observe that we deserialized a default override table declaration. + virtual void didDeserialize(ModuleDecl *mod, + SILDefaultOverrideTable *otable) = 0; + /// Observe that we deserialized a move only deinit declaration. virtual void didDeserialize(ModuleDecl *mod, SILMoveOnlyDeinit *deinit) = 0; @@ -93,6 +104,10 @@ class DeserializationNotificationHandler SILWitnessTable *wt) override { } + /// Observe that we successfully deserialized a override table's entries. + virtual void didDeserializeDefaultOverrideTableEntries( + ModuleDecl *mod, SILDefaultOverrideTable *ot) override {} + /// Observe that we successfully deserialized a default witness table's /// entries. virtual void didDeserializeDefaultWitnessTableEntries( @@ -113,6 +128,10 @@ class DeserializationNotificationHandler virtual void didDeserialize(ModuleDecl *mod, SILDefaultWitnessTable *wtable) override {} + /// Observe that we deserialized a default override table declaration. + virtual void didDeserialize(ModuleDecl *mod, + SILDefaultOverrideTable *otable) override {} + /// Observe that we deserialized a move only deinit declaration. virtual void didDeserialize(ModuleDecl *mod, SILMoveOnlyDeinit *deinit) override {} @@ -242,11 +261,15 @@ class DeserializationNotificationHandlerSet final void didDeserializeDefaultWitnessTableEntries(ModuleDecl *mod, SILDefaultWitnessTable *wt) override; + void didDeserializeDefaultOverrideTableEntries( + ModuleDecl *mod, SILDefaultOverrideTable *ot) override; void didDeserialize(ModuleDecl *mod, SILGlobalVariable *var) override; void didDeserialize(ModuleDecl *mod, SILVTable *vtable) override; void didDeserialize(ModuleDecl *mod, SILWitnessTable *wtable) override; void didDeserialize(ModuleDecl *mod, SILDefaultWitnessTable *wtable) override; + void didDeserialize(ModuleDecl *mod, + SILDefaultOverrideTable *otable) override; void didDeserialize(ModuleDecl *mod, SILMoveOnlyDeinit *deinit) override; }; } // namespace swift diff --git a/include/swift/Serialization/SerializedSILLoader.h b/include/swift/Serialization/SerializedSILLoader.h index 021c51c9ef239..e82e52bed7f1f 100644 --- a/include/swift/Serialization/SerializedSILLoader.h +++ b/include/swift/Serialization/SerializedSILLoader.h @@ -70,6 +70,8 @@ class SerializedSILLoader { SILMoveOnlyDeinit *lookupMoveOnlyDeinit(const NominalTypeDecl *nomDecl); SILWitnessTable *lookupWitnessTable(SILWitnessTable *C); SILDefaultWitnessTable *lookupDefaultWitnessTable(SILDefaultWitnessTable *C); + SILDefaultOverrideTable * + lookupDefaultOverrideTable(SILDefaultOverrideTable *); SILDifferentiabilityWitness * lookupDifferentiabilityWitness(SILDifferentiabilityWitnessKey key); @@ -106,6 +108,9 @@ class SerializedSILLoader { /// Deserialize all DefaultWitnessTables in all SILModules. void getAllDefaultWitnessTables(); + /// Deserialize all DefaultOverrideTables in all SILModules. + void getAllDefaultOverrideTables(); + /// Deserialize all Properties in all SILModules. void getAllProperties(); diff --git a/lib/SIL/IR/Notifications.cpp b/lib/SIL/IR/Notifications.cpp index 907f06aa2da26..e0eb7a9c16d50 100644 --- a/lib/SIL/IR/Notifications.cpp +++ b/lib/SIL/IR/Notifications.cpp @@ -13,6 +13,7 @@ #define DEBUG_TYPE "sil-notifications" #include "swift/SIL/Notifications.h" +#include "swift/SIL/SILDefaultOverrideTable.h" #include "swift/SIL/SILMoveOnlyDeinit.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -49,9 +50,12 @@ DNS_CHAIN_METHOD(DeserializeWitnessTableEntries, ModuleDecl *, SILWitnessTable *) DNS_CHAIN_METHOD(DeserializeDefaultWitnessTableEntries, ModuleDecl *, SILDefaultWitnessTable *) +DNS_CHAIN_METHOD(DeserializeDefaultOverrideTableEntries, ModuleDecl *, + SILDefaultOverrideTable *) DNS_CHAIN_METHOD(Deserialize, ModuleDecl *, SILGlobalVariable *) DNS_CHAIN_METHOD(Deserialize, ModuleDecl *, SILVTable *) DNS_CHAIN_METHOD(Deserialize, ModuleDecl *, SILMoveOnlyDeinit *) DNS_CHAIN_METHOD(Deserialize, ModuleDecl *, SILWitnessTable *) DNS_CHAIN_METHOD(Deserialize, ModuleDecl *, SILDefaultWitnessTable *) +DNS_CHAIN_METHOD(Deserialize, ModuleDecl *, SILDefaultOverrideTable *) #undef DNS_CHAIN_METHOD diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 35170a5c85958..13a7c66f655c9 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -308,6 +308,13 @@ SILModule::lookUpDefaultOverrideTable(const ClassDecl *decl, auto found = DefaultOverrideTableMap.find(decl); if (found == DefaultOverrideTableMap.end()) { if (deserializeLazily) { + SILLinkage linkage = getSILLinkage(getDeclLinkage(decl), ForDefinition); + SILDefaultOverrideTable *otable = + SILDefaultOverrideTable::declare(*this, linkage, decl); + otable = getSILLoader()->lookupDefaultOverrideTable(otable); + if (otable) + DefaultOverrideTableMap[decl] = otable; + return otable; } return nullptr; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 4ab0f6d1840b0..0e7eb3869bdd8 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -193,6 +193,7 @@ SILDeserializer::SILDeserializer( kind == sil_index_block::SIL_GLOBALVAR_NAMES || kind == sil_index_block::SIL_WITNESS_TABLE_NAMES || kind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_NAMES || + kind == sil_index_block::SIL_DEFAULT_OVERRIDE_TABLE_NAMES || kind == sil_index_block::SIL_PROPERTY_OFFSETS || kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES)) && "Expect SIL_FUNC_NAMES, SIL_VTABLE_NAMES, SIL_GLOBALVAR_NAMES, \ @@ -212,6 +213,8 @@ SILDeserializer::SILDeserializer( WitnessTableList = readFuncTable(scratch, blobData); else if (kind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_NAMES) DefaultWitnessTableList = readFuncTable(scratch, blobData); + else if (kind == sil_index_block::SIL_DEFAULT_OVERRIDE_TABLE_NAMES) + DefaultOverrideTableList = readFuncTable(scratch, blobData); else if (kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES) DifferentiabilityWitnessList = readFuncTable(scratch, blobData); else if (kind == sil_index_block::SIL_PROPERTY_OFFSETS) { @@ -256,6 +259,11 @@ SILDeserializer::SILDeserializer( offKind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_OFFSETS) && "Expect a SIL_DEFAULT_WITNESS_TABLE_OFFSETS record."); MF->allocateBuffer(DefaultWitnessTables, scratch); + } else if (kind == sil_index_block::SIL_DEFAULT_OVERRIDE_TABLE_NAMES) { + assert((next.Kind == llvm::BitstreamEntry::Record && + offKind == sil_index_block::SIL_DEFAULT_OVERRIDE_TABLE_OFFSETS) && + "Expect a SIL_DEFAULT_OVERRIDE_TABLE_OFFSETS record."); + MF->allocateBuffer(DefaultOverrideTables, scratch); } else if (kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES) { assert((next.Kind == llvm::BitstreamEntry::Record && offKind == @@ -1121,6 +1129,7 @@ llvm::Expected SILDeserializer::readSILFunctionChecked( Builder.setCurrentDebugScope(fn->getDebugScope()); while (kind != SIL_FUNCTION && kind != SIL_VTABLE && kind != SIL_GLOBALVAR && kind != SIL_MOVEONLY_DEINIT && kind != SIL_WITNESS_TABLE && + kind != SIL_DEFAULT_OVERRIDE_TABLE && kind != SIL_DIFFERENTIABILITY_WITNESS) { if (kind == SIL_BASIC_BLOCK) // Handle a SILBasicBlock record. @@ -4091,6 +4100,7 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) { while (kind != SIL_FUNCTION && kind != SIL_VTABLE && kind != SIL_GLOBALVAR && kind != SIL_MOVEONLY_DEINIT && kind != SIL_WITNESS_TABLE && + kind != SIL_DEFAULT_OVERRIDE_TABLE && kind != SIL_DIFFERENTIABILITY_WITNESS) { if (readSILInstruction(nullptr, Builder, kind, scratch)) MF->fatal("readSILInstruction returns error"); @@ -4219,7 +4229,8 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) { std::vector vtableEntries; // Another SIL_VTABLE record means the end of this VTable. while (kind != SIL_VTABLE && kind != SIL_WITNESS_TABLE && - kind != SIL_DEFAULT_WITNESS_TABLE && kind != SIL_FUNCTION && + kind != SIL_DEFAULT_WITNESS_TABLE && + kind != SIL_DEFAULT_OVERRIDE_TABLE && kind != SIL_FUNCTION && kind != SIL_PROPERTY && kind != SIL_MOVEONLY_DEINIT) { assert(kind == SIL_VTABLE_ENTRY && "Content of Vtable should be in SIL_VTABLE_ENTRY."); @@ -4434,10 +4445,9 @@ void SILDeserializer::readWitnessTableEntries( unsigned kind = maybeKind.get(); // Another record means the end of this WitnessTable. - while (kind != SIL_WITNESS_TABLE && - kind != SIL_DEFAULT_WITNESS_TABLE && - kind != SIL_DIFFERENTIABILITY_WITNESS && - kind != SIL_FUNCTION) { + while (kind != SIL_WITNESS_TABLE && kind != SIL_DEFAULT_WITNESS_TABLE && + kind != SIL_DEFAULT_OVERRIDE_TABLE && + kind != SIL_DIFFERENTIABILITY_WITNESS && kind != SIL_FUNCTION) { if (kind == SIL_DEFAULT_WITNESS_TABLE_NO_ENTRY) { witnessEntries.push_back(SILDefaultWitnessTable::Entry()); } else if (kind == SIL_WITNESS_BASE_ENTRY) { @@ -4825,6 +4835,181 @@ SILDeserializer::lookupDefaultWitnessTable(SILDefaultWitnessTable *existingWt) { return Wt; } +void SILDeserializer::readDefaultOverrideTableEntries( + llvm::BitstreamEntry &entry, + std::vector &entries) { + SmallVector scratch; + llvm::Expected maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); + + // Another record means the end of this DefaultOverrideTable. + while (kind == SIL_DEFAULT_OVERRIDE_TABLE_ENTRY) { + ArrayRef ListOfValues; + DeclID NameID; + DefaultOverrideTableEntryLayout::readRecord(scratch, NameID, ListOfValues); + SILFunction *impl = nullptr; + if (NameID != 0) { + impl = getFuncForReference(MF->getIdentifierText(NameID)); + } + if (impl || NameID == 0) { + unsigned NextValueIndex = 0; + auto method = getSILDeclRef(MF, ListOfValues, NextValueIndex); + auto original = getSILDeclRef(MF, ListOfValues, NextValueIndex); + entries.push_back(SILDefaultOverrideTable::Entry{method, original, impl}); + } + + // Fetch the next record. + scratch.clear(); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); + if (entry.Kind == llvm::BitstreamEntry::EndBlock) + // EndBlock means the end of this DefaultOverrideTable. + break; + maybeKind = SILCursor.readRecord(entry.ID, scratch); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + kind = maybeKind.get(); + } +} + +SILDefaultOverrideTable * +SILDeserializer::readDefaultOverrideTable(DeclID tableID, + SILDefaultOverrideTable *existingOt) { + if (tableID == 0) + return nullptr; + assert(tableID <= DefaultOverrideTables.size() && + "invalid DefaultOverrideTable ID"); + + auto &oTableOrOffset = DefaultOverrideTables[tableID - 1]; + + if (oTableOrOffset.isFullyDeserialized()) + return oTableOrOffset.get(); + + BCOffsetRAII restoreOffset(SILCursor); + if (llvm::Error Err = SILCursor.JumpToBit(oTableOrOffset.getOffset())) + MF->fatal(std::move(Err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + llvm::BitstreamEntry entry = maybeEntry.get(); + if (entry.Kind == llvm::BitstreamEntry::Error) { + LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in " + "readDefaultOverrideTable.\n"); + return nullptr; + } + + SmallVector scratch; + StringRef blobData; + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); + assert(kind == SIL_DEFAULT_OVERRIDE_TABLE && + "expect a sil default override table"); + (void)kind; + + unsigned rawLinkage; + DeclID classID; + DefaultOverrideTableLayout::readRecord(scratch, classID, rawLinkage); + + auto Linkage = fromStableSILLinkage(rawLinkage); + if (!Linkage) { + LLVM_DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage + << " for SILFunction\n"); + MF->fatal("invalid linkage code"); + } + + ClassDecl *decl = cast(MF->getDecl(classID)); + if (decl == nullptr) { + LLVM_DEBUG(llvm::dbgs() << "invalid class code " << classID << "\n"); + MF->fatal("invalid class code"); + } + + PrettyStackTraceDecl trace("deserializing default override table for", decl); + + if (!existingOt) + existingOt = + SILMod.lookUpDefaultOverrideTable(decl, /*deserializeLazily=*/false); + auto oT = existingOt; + + // If we have an existing default override table, verify that the class + // matches up. + if (oT) { + if (oT->getClass() != decl) { + MF->fatal("Protocol mismatch"); + } + + // Don't override the linkage of a default override table with an existing + // declaration. + + } else { + // Otherwise, create a new override table declaration. + oT = SILDefaultOverrideTable::declare(SILMod, *Linkage, decl); + if (Callback) + Callback->didDeserialize(MF->getAssociatedModule(), oT); + } + + // Fetch the next record. + scratch.clear(); + maybeEntry = SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + entry = maybeEntry.get(); + if (entry.Kind == llvm::BitstreamEntry::EndBlock) + return nullptr; + + std::vector entries; + readDefaultOverrideTableEntries(entry, entries); + + oT->define(entries); + oTableOrOffset.set(oT, /*fully deserialized*/ true); + if (Callback) + Callback->didDeserializeDefaultOverrideTableEntries( + MF->getAssociatedModule(), oT); + return oT; +} + +SILDefaultOverrideTable *SILDeserializer::lookupDefaultOverrideTable( + SILDefaultOverrideTable *existingOt) { + assert(existingOt && + "Cannot deserialize a null default override table declaration."); + assert(existingOt->isDeclaration() && + "Cannot deserialize a default override table " + "definition."); + + // If we don't have a default override table list, we can't look anything up. + if (!DefaultOverrideTableList) + return nullptr; + + // Use the mangled name of the class to lookup the partially deserialized + // value from the default override table list. + auto iter = DefaultOverrideTableList->find(existingOt->getUniqueName()); + if (iter == DefaultOverrideTableList->end()) + return nullptr; + + // Attempt to read the default override table. + auto Ot = readDefaultOverrideTable(*iter, existingOt); + if (Ot) + LLVM_DEBUG(llvm::dbgs() << "Deserialize SIL:\n"; Ot->dump()); + + return Ot; +} + +void SILDeserializer::getAllDefaultOverrideTables() { + if (!DefaultOverrideTableList) + return; + for (unsigned index = 0, size = DefaultOverrideTables.size(); index < size; + ++index) + readDefaultOverrideTable(index + 1, nullptr); +} + SILDifferentiabilityWitness * SILDeserializer::readDifferentiabilityWitness(DeclID DId) { if (DId == 0) diff --git a/lib/Serialization/DeserializeSIL.h b/lib/Serialization/DeserializeSIL.h index f2ac9ef7bd195..8fa5d79b92ab5 100644 --- a/lib/Serialization/DeserializeSIL.h +++ b/lib/Serialization/DeserializeSIL.h @@ -71,6 +71,10 @@ namespace swift { MutableArrayRef> DefaultWitnessTables; + std::unique_ptr DefaultOverrideTableList; + MutableArrayRef> + DefaultOverrideTables; + MutableArrayRef> Properties; @@ -181,6 +185,12 @@ namespace swift { SILDefaultWitnessTable * readDefaultWitnessTable(serialization::DeclID, SILDefaultWitnessTable *existingWt); + void readDefaultOverrideTableEntries( + llvm::BitstreamEntry &entry, + std::vector &entries); + SILDefaultOverrideTable * + readDefaultOverrideTable(serialization::DeclID, + SILDefaultOverrideTable *existingOt); SILDifferentiabilityWitness * readDifferentiabilityWitness(serialization::DeclID); @@ -205,6 +215,8 @@ namespace swift { SILWitnessTable *lookupWitnessTable(SILWitnessTable *wt); SILDefaultWitnessTable * lookupDefaultWitnessTable(SILDefaultWitnessTable *wt); + SILDefaultOverrideTable * + lookupDefaultOverrideTable(SILDefaultOverrideTable *ot); SILDifferentiabilityWitness * lookupDifferentiabilityWitness(StringRef mangledDiffWitnessKey); @@ -277,6 +289,7 @@ namespace swift { getAllVTables(); getAllWitnessTables(); getAllDefaultWitnessTables(); + getAllDefaultOverrideTables(); getAllProperties(); getAllDifferentiabilityWitnesses(); getAllMoveOnlyDeinits(); @@ -303,6 +316,10 @@ namespace swift { /// to SILMod. void getAllDefaultWitnessTables(); + /// Deserialize all DefaultOverrideTables inside the module and add them to + /// SILMod. + void getAllDefaultOverrideTables(); + /// Deserialize all Property descriptors inside the module and add them /// to SILMod. void getAllProperties(); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 0eb17d7370c4a..3aa7d63caea20 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 929; // OriginallyDefinedInAttr::LinkerModuleName +const uint16_t SWIFTMODULE_VERSION_MINOR = 930; // default override table /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 1ee0ee944f6b8..5eeefeb60d517 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -102,19 +102,21 @@ namespace sil_index_block { SIL_WITNESS_TABLE_OFFSETS, SIL_DEFAULT_WITNESS_TABLE_NAMES, SIL_DEFAULT_WITNESS_TABLE_OFFSETS, + SIL_DEFAULT_OVERRIDE_TABLE_NAMES, + SIL_DEFAULT_OVERRIDE_TABLE_OFFSETS, SIL_PROPERTY_OFFSETS, SIL_DIFFERENTIABILITY_WITNESS_NAMES, SIL_DIFFERENTIABILITY_WITNESS_OFFSETS, }; using ListLayout = BCGenericRecordLayout< - BCFixed<4>, // record ID + BCFixed<5>, // record ID BCVBR<16>, // table offset within the blob BCBlob // map from identifier strings to IDs. >; using OffsetLayout = BCGenericRecordLayout< - BCFixed<4>, // record ID + BCFixed<5>, // record ID BCArray >; @@ -155,6 +157,8 @@ namespace sil_block { SIL_WITNESS_CONDITIONAL_CONFORMANCE, SIL_DEFAULT_WITNESS_TABLE, SIL_DEFAULT_WITNESS_TABLE_NO_ENTRY, + SIL_DEFAULT_OVERRIDE_TABLE, + SIL_DEFAULT_OVERRIDE_TABLE_ENTRY, SIL_DIFFERENTIABILITY_WITNESS, SIL_INST_WITNESS_METHOD, SIL_SPECIALIZE_ATTR, @@ -272,6 +276,19 @@ namespace sil_block { SIL_DEFAULT_WITNESS_TABLE_NO_ENTRY >; + using DefaultOverrideTableLayout = BCRecordLayout< + SIL_DEFAULT_OVERRIDE_TABLE, + DeclIDField, // ID of ClassDecl + SILLinkageField // Linkage + // Default override table entries will be serialized after. + >; + + using DefaultOverrideTableEntryLayout = BCRecordLayout< + SIL_DEFAULT_OVERRIDE_TABLE_ENTRY, + DeclIDField, // SILFunction name + BCArray // SILDeclRef(method) + SILDeclRef(original) + >; + using SILGlobalVarLayout = BCRecordLayout< SIL_GLOBALVAR, SILLinkageField, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 22c3be5964199..ec3c12c136f7f 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -945,6 +945,7 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(sil_block, SIL_WITNESS_CONDITIONAL_CONFORMANCE); BLOCK_RECORD(sil_block, SIL_DEFAULT_WITNESS_TABLE); BLOCK_RECORD(sil_block, SIL_DEFAULT_WITNESS_TABLE_NO_ENTRY); + BLOCK_RECORD(sil_block, SIL_DEFAULT_OVERRIDE_TABLE); BLOCK_RECORD(sil_block, SIL_INST_WITNESS_METHOD); BLOCK_RECORD(sil_block, SIL_SPECIALIZE_ATTR); BLOCK_RECORD(sil_block, SIL_ARG_EFFECTS_ATTR); @@ -986,6 +987,8 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(sil_index_block, SIL_PROPERTY_OFFSETS); BLOCK_RECORD(sil_index_block, SIL_DIFFERENTIABILITY_WITNESS_NAMES); BLOCK_RECORD(sil_index_block, SIL_DIFFERENTIABILITY_WITNESS_OFFSETS); + BLOCK_RECORD(sil_index_block, SIL_DEFAULT_OVERRIDE_TABLE_NAMES); + BLOCK_RECORD(sil_index_block, SIL_DEFAULT_OVERRIDE_TABLE_OFFSETS); BLOCK(INCREMENTAL_INFORMATION_BLOCK); BLOCK_RECORD(fine_grained_dependencies::record_block, METADATA); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 424582a252b53..ce44a0da5933e 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -217,7 +217,13 @@ namespace { /// Holds the list of DefaultWitnessTables. std::vector DefaultWitnessTableOffset; uint32_t /*DeclID*/ NextDefaultWitnessTableID = 1; - + + /// Maps default witness table identifier to an ID. + Table DefaultOverrideTableList; + /// Holds the list of DefaultOverrideTables. + std::vector DefaultOverrideTableOffset; + uint32_t /*DeclID*/ NextDefaultOverrideTableID = 1; + /// Holds the list of Properties. std::vector PropertyOffset; @@ -294,6 +300,10 @@ namespace { void writeSILWitnessTableEntry(const SILWitnessTable::Entry &entry, SerializedKind_t serializedKind); void writeSILDefaultWitnessTable(const SILDefaultWitnessTable &wt); + void writeSILDefaultOverrideTableEntry( + const SILDefaultOverrideTable::Entry &entry, + SerializedKind_t serializedKind); + void writeSILDefaultOverrideTable(const SILDefaultOverrideTable &ot); void writeSILDifferentiabilityWitness(const SILDifferentiabilityWitness &dw); void writeSILProperty(const SILProperty &prop); @@ -2983,9 +2993,10 @@ static void writeIndexTable(Serializer &S, kind == sil_index_block::SIL_GLOBALVAR_NAMES || kind == sil_index_block::SIL_WITNESS_TABLE_NAMES || kind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_NAMES || + kind == sil_index_block::SIL_DEFAULT_OVERRIDE_TABLE_NAMES || kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES) && - "SIL function table, global, vtable, (default) witness table, and " - "differentiability witness table are supported"); + "SIL function table, global, vtable, (default) witness table, default " + "override table and differentiability witness table are supported"); llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { @@ -3048,6 +3059,14 @@ void SILSerializer::writeIndexTables() { DefaultWitnessTableOffset); } + if (!DefaultOverrideTableList.empty()) { + writeIndexTable(S, List, sil_index_block::SIL_DEFAULT_OVERRIDE_TABLE_NAMES, + DefaultOverrideTableList); + Offset.emit(ScratchRecord, + sil_index_block::SIL_DEFAULT_OVERRIDE_TABLE_OFFSETS, + DefaultOverrideTableOffset); + } + if (!PropertyOffset.empty()) { Offset.emit(ScratchRecord, sil_index_block::SIL_PROPERTY_OFFSETS, PropertyOffset); @@ -3427,6 +3446,47 @@ writeSILDefaultWitnessTable(const SILDefaultWitnessTable &wt) { } } +void SILSerializer::writeSILDefaultOverrideTableEntry( + const SILDefaultOverrideTable::Entry &entry, + SerializedKind_t serializedKind) { + SmallVector ListOfValues; + handleSILDeclRef(S, entry.method, ListOfValues); + handleSILDeclRef(S, entry.original, ListOfValues); + + IdentifierID implID = 0; + SILFunction *impl = entry.impl; + if (impl && serializedKind != IsNotSerialized && + impl->hasValidLinkageForFragileRef(serializedKind)) { + addReferencedSILFunction(impl, true); + implID = S.addUniquedStringRef(impl->getName()); + } + DefaultOverrideTableEntryLayout::emitRecord( + Out, ScratchRecord, SILAbbrCodes[DefaultOverrideTableEntryLayout::Code], + // SILFunction name + implID, ListOfValues); +} + +void SILSerializer::writeSILDefaultOverrideTable( + const SILDefaultOverrideTable &ot) { + if (ot.isDeclaration()) + return; + + StringRef name = S.addUniquedString(ot.getUniqueName()).first; + DefaultOverrideTableList[name] = NextDefaultOverrideTableID++; + DefaultOverrideTableOffset.push_back(Out.GetCurrentBitNo()); + + DefaultOverrideTableLayout::emitRecord( + Out, ScratchRecord, SILAbbrCodes[DefaultOverrideTableLayout::Code], + S.addDeclRef(ot.getClass()), toStableSILLinkage(ot.getLinkage())); + + for (auto &entry : ot.getEntries()) { + // Default override table is not serialized. The IsSerialized + // argument is passed here to call hasValidLinkageForFragileRef + // to keep the behavior consistent with or without any optimizations. + writeSILDefaultOverrideTableEntry(entry, IsSerialized); + } +} + void SILSerializer::writeSILDifferentiabilityWitness( const SILDifferentiabilityWitness &dw) { Mangle::ASTMangler mangler(dw.getOriginalFunction()->getASTContext()); @@ -3551,6 +3611,8 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); + registerSILAbbr(); + registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); @@ -3603,6 +3665,13 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { writeSILDefaultWitnessTable(wt); } + for (const SILDefaultOverrideTable &ot : SILMod->getDefaultOverrideTables()) { + if (!SILMod->shouldSerializeEntitiesAssociatedWithDeclContext( + ot.getClass())) + continue; + writeSILDefaultOverrideTable(ot); + } + // Add global variables that must be emitted to the list. for (const SILGlobalVariable &g : SILMod->getSILGlobals()) { if (g.isAnySerialized() || ShouldSerializeAll) diff --git a/lib/Serialization/SerializedSILLoader.cpp b/lib/Serialization/SerializedSILLoader.cpp index cb20fc575bbc7..0c21ad5203fd0 100644 --- a/lib/Serialization/SerializedSILLoader.cpp +++ b/lib/Serialization/SerializedSILLoader.cpp @@ -144,6 +144,14 @@ lookupDefaultWitnessTable(SILDefaultWitnessTable *WT) { return nullptr; } +SILDefaultOverrideTable * +SerializedSILLoader::lookupDefaultOverrideTable(SILDefaultOverrideTable *OT) { + for (auto &Des : LoadedSILSections) + if (auto oT = Des->lookupDefaultOverrideTable(OT)) + return oT; + return nullptr; +} + SILDifferentiabilityWitness * SerializedSILLoader::lookupDifferentiabilityWitness( SILDifferentiabilityWitnessKey key) { @@ -252,6 +260,12 @@ void SerializedSILLoader::getAllDefaultWitnessTables() { Des->getAllDefaultWitnessTables(); } +/// Deserialize all DefaultOverrideTables in all SILModules. +void SerializedSILLoader::getAllDefaultOverrideTables() { + for (auto &Des : LoadedSILSections) + Des->getAllDefaultOverrideTables(); +} + /// Deserialize all Properties in all SILModules. void SerializedSILLoader::getAllProperties() { for (auto &Des : LoadedSILSections) diff --git a/test/SIL/Serialization/default_override.sil b/test/SIL/Serialization/default_override.sil new file mode 100644 index 0000000000000..2ee938086a4cf --- /dev/null +++ b/test/SIL/Serialization/default_override.sil @@ -0,0 +1,46 @@ +// sil (this file) -> sib -> sib -> sil | FileCheck + +// RUN: %empty-directory(%t) +// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name default_override -enable-library-evolution -enable-experimental-feature CoroutineAccessors +// RUN: %target-sil-opt %t/tmp.sib -emit-sib -o %t/tmp.2.sib -module-name default_override -enable-library-evolution -enable-experimental-feature CoroutineAccessors +// RUN: %target-sil-opt %t/tmp.2.sib -module-name default_override -emit-sorted-sil -enable-library-evolution -enable-experimental-feature CoroutineAccessors | %FileCheck %s + +// REQUIRES: swift_feature_CoroutineAccessors + +sil_stage canonical + +import Builtin + +struct Int { + var _value: Builtin.Int64 +} + +open class B { + open var i: Int { read set } +} + +sil @B_i_read : $@yield_once @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_read2 : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_set : $@convention(method) (Int, @guaranteed B) -> () +sil @B_i_modify : $@yield_once @convention(method) (@guaranteed B) -> @yields @inout Int +sil @B_i_modify2 : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields @inout Int + +sil_vtable B { + #B.i!read: (B) -> () -> () : @B_i_read + #B.i!read2: (B) -> () -> () : @B_i_read2 + #B.i!setter: (B) -> (Int) -> () : @B_i_set + #B.i!modify: (B) -> () -> () : @B_i_modify + #B.i!modify2: (B) -> () -> () : @B_i_modify2 +} + +sil @B_i_read2_default_override : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_modify2_default_override : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields @inout Int + +// CHECK-LABEL: sil_default_override_table B { +// CHECK-NEXT: #B.i!read2: #B.i!read: (B) -> () -> () : @B_i_read2_default_override +// CHECK-NEXT: #B.i!modify2: #B.i!modify: (B) -> () -> () : @B_i_modify2_default_override +// CHECK-NEXT: } +sil_default_override_table B { + #B.i!read2: #B.i!read: (B) -> () -> () : @B_i_read2_default_override + #B.i!modify2: #B.i!modify: (B) -> () -> () : @B_i_modify2_default_override +} From ea0387ad9dc1966362bd3e8d71c85de366b705cd Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 17:53:19 -0700 Subject: [PATCH 14/21] [NFC] SILGen: Extract typealias. --- lib/SILGen/SILGenType.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index cc851e2881a76..589f086d85241 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -261,8 +261,11 @@ class SILGenVTable : public SILVTableVisitor { // Map a base SILDeclRef to the corresponding element in vtableMethods. llvm::DenseMap baseToIndexMap; + // A base method and a corresponding override. + using VTableMethod = std::pair; + // For each base method, store the corresponding override. - SmallVector, 8> vtableMethods; + SmallVector vtableMethods; SILGenVTable(SILGenModule &SGM, ClassDecl *theClass) : SGM(SGM), theClass(theClass) { From 71412d35663a0328df17759a1686557cb68e6d12 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 17:56:02 -0700 Subject: [PATCH 15/21] [NFC] SILGen: Name operation. And prepare to reuse it. --- lib/SILGen/SILGenType.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 589f086d85241..7d50e6d86ec38 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -272,11 +272,13 @@ class SILGenVTable : public SILVTableVisitor { isResilient = theClass->isResilient(); } + /// Populate our list of base methods and overrides. + void collectMethods() { visitAncestor(theClass); } + void emitVTable() { PrettyStackTraceDecl("silgen emitVTable", theClass); - // Populate our list of base methods and overrides. - visitAncestor(theClass); + collectMethods(); SmallVector vtableEntries; vtableEntries.reserve(vtableMethods.size() + 2); From cc4c4584bfa25559fd5396fe6222fba901b301cf Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 20 Mar 2025 18:00:20 -0700 Subject: [PATCH 16/21] [NFC] SILGen: Extract base class. Add new SILGenVTableBase class as a parent of SILGenVTable in preparation to add another subclass. --- lib/SILGen/SILGenType.cpp | 98 +++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 7d50e6d86ec38..598d528d93e60 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -252,7 +252,10 @@ bool SILGenModule::requiresObjCMethodEntryPoint(ConstructorDecl *constructor) { namespace { /// An ASTVisitor for populating SILVTable entries from ClassDecl members. -class SILGenVTable : public SILVTableVisitor { +template +class SILGenVTableBase : public SILVTableVisitor { + T &asDerived() { return *static_cast(this); } + public: SILGenModule &SGM; ClassDecl *theClass; @@ -267,14 +270,61 @@ class SILGenVTable : public SILVTableVisitor { // For each base method, store the corresponding override. SmallVector vtableMethods; - SILGenVTable(SILGenModule &SGM, ClassDecl *theClass) - : SGM(SGM), theClass(theClass) { + SILGenVTableBase(SILGenModule &SGM, ClassDecl *theClass) + : SGM(SGM), theClass(theClass) { isResilient = theClass->isResilient(); } /// Populate our list of base methods and overrides. void collectMethods() { visitAncestor(theClass); } + void visitAncestor(ClassDecl *ancestor) { + // Imported types don't have vtables right now. + if (ancestor->hasClangNode()) + return; + + auto *superDecl = ancestor->getSuperclassDecl(); + if (superDecl) + visitAncestor(superDecl); + + asDerived().addVTableEntries(ancestor); + } + + // Try to find an overridden entry. + void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) { + auto found = baseToIndexMap.find(baseRef); + assert(found != baseToIndexMap.end()); + auto &method = vtableMethods[found->second]; + assert(method.first == baseRef); + method.second = declRef; + } + + // Add an entry to the vtable. + void addMethod(SILDeclRef member) { + unsigned index = vtableMethods.size(); + vtableMethods.push_back(std::make_pair(member, member)); + auto result = baseToIndexMap.insert(std::make_pair(member, index)); + assert(result.second); + (void)result; + } + + void addPlaceholder(MissingMemberDecl *m) { +#ifndef NDEBUG + auto *classDecl = cast(m->getDeclContext()); + bool isResilient = classDecl->isResilient(SGM.M.getSwiftModule(), + ResilienceExpansion::Maximal); + assert(isResilient || + m->getNumberOfVTableEntries() == 0 && + "Should not be emitting fragile class with missing members"); +#endif + } +}; + +class SILGenVTable : public SILGenVTableBase { +public: + SILGenVTable(SILGenModule &SGM, ClassDecl *theClass) + : SILGenVTableBase(SGM, theClass) {} + void emitVTable() { PrettyStackTraceDecl("silgen emitVTable", theClass); @@ -326,49 +376,7 @@ class SILGenVTable : public SILVTableVisitor { // Finally, create the vtable. SILVTable::create(SGM.M, theClass, serialized, vtableEntries); } - - void visitAncestor(ClassDecl *ancestor) { - // Imported types don't have vtables right now. - if (ancestor->hasClangNode()) - return; - - auto *superDecl = ancestor->getSuperclassDecl(); - if (superDecl) - visitAncestor(superDecl); - - addVTableEntries(ancestor); - } - - // Try to find an overridden entry. - void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) { - auto found = baseToIndexMap.find(baseRef); - assert(found != baseToIndexMap.end()); - auto &method = vtableMethods[found->second]; - assert(method.first == baseRef); - method.second = declRef; - } - - // Add an entry to the vtable. - void addMethod(SILDeclRef member) { - unsigned index = vtableMethods.size(); - vtableMethods.push_back(std::make_pair(member, member)); - auto result = baseToIndexMap.insert(std::make_pair(member, index)); - assert(result.second); - (void) result; - } - - void addPlaceholder(MissingMemberDecl *m) { -#ifndef NDEBUG - auto *classDecl = cast(m->getDeclContext()); - bool isResilient = - classDecl->isResilient(SGM.M.getSwiftModule(), - ResilienceExpansion::Maximal); - assert(isResilient || m->getNumberOfVTableEntries() == 0 && - "Should not be emitting fragile class with missing members"); -#endif - } }; - } // end anonymous namespace static void emitTypeMemberGlobalVariable(SILGenModule &SGM, From 1c3073b8042be35f21ebab0127a996e5bc8e0b83 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 21 Mar 2025 08:43:11 -0700 Subject: [PATCH 17/21] [DefaultOverrides] SILGen. Emit tables for open members of open classes. --- lib/SILGen/SILGen.h | 4 + lib/SILGen/SILGenType.cpp | 225 ++++++++++++++++++++ test/SILGen/default_override.swift | 327 +++++++++++++++++++++++++++++ 3 files changed, 556 insertions(+) create mode 100644 test/SILGen/default_override.swift diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index aee42f2f7a8ee..cde8072220109 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -389,6 +389,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Emit the default witness table for a resilient protocol. void emitDefaultWitnessTable(ProtocolDecl *protocol); + void emitDefaultOverrideTable(ClassDecl *decl); + + SILFunction *emitDefaultOverride(SILDeclRef replacement, SILDeclRef original); + /// Emit the self-conformance witness table for a protocol. void emitSelfConformanceWitnessTable(ProtocolDecl *protocol); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 598d528d93e60..828cb271adad1 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -34,6 +34,7 @@ #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILDefaultOverrideTable.h" #include "swift/SIL/SILVTableVisitor.h" #include "swift/SIL/SILWitnessVisitor.h" #include "swift/SIL/TypeLowering.h" @@ -1146,6 +1147,225 @@ void SILGenModule::emitDefaultWitnessTable(ProtocolDecl *protocol) { defaultWitnesses->convertToDefinition(builder.DefaultWitnesses); } +namespace { + +std::optional +originalAccessorKindForReplacementKind(AccessorKind kind) { + switch (kind) { + case AccessorKind::Read2: + return {AccessorKind::Read}; + case AccessorKind::Modify2: + return {AccessorKind::Modify}; + case AccessorKind::Get: + case AccessorKind::DistributedGet: + case AccessorKind::Set: + case AccessorKind::Read: + case AccessorKind::Modify: + case AccessorKind::WillSet: + case AccessorKind::DidSet: + case AccessorKind::Address: + case AccessorKind::MutableAddress: + case AccessorKind::Init: + return std::nullopt; + } +} + +/// Emit a default witness table for a resilient protocol definition. +class SILGenDefaultOverrideTable + : public SILGenVTableBase { + using super = SILGenVTableBase; + +public: + SILLinkage linkage; + SILGenDefaultOverrideTable(SILGenModule &SGM, ClassDecl *decl, + SILLinkage linkage) + : super(SGM, decl), linkage(linkage) {} + + std::optional + entryForMethod(VTableMethod method) { + // Determine whether `method` semantically "replaces" some other member (in + // the sense that calls that previously resolved to that other member will + // now resolve to that new member). If it does, produce a default override + // table entry which describes that replacement, including a thunk (to be + // installed at runtime) in subclasses which overrode only that other + // replaced member. + auto declRef = method.first; + auto *decl = declRef.getAbstractFunctionDecl(); + if (decl->getEffectiveAccess() != AccessLevel::Open) { + // Only methods which can be overridden in different resilience domains + // need an entry. + return std::nullopt; + } + // Currently, only accessors can be replacements. + auto *accessor = dyn_cast_or_null(decl); + if (!accessor) { + return std::nullopt; + } + // Specifically, read2 can replace _read and modify2 can replace _modify. + auto originalKind = + originalAccessorKindForReplacementKind(accessor->getAccessorKind()); + if (!originalKind) { + return std::nullopt; + } + auto *originalDecl = accessor->getStorage()->getAccessor(*originalKind); + if (!originalDecl) { + return std::nullopt; + } + auto original = SILDeclRef(originalDecl); + auto *impl = SGM.emitDefaultOverride(declRef, original); + return {SILDefaultOverrideTable::Entry{declRef, original, impl}}; + } + + void emitTable() { + PrettyStackTraceDecl("silgen emitDefaultOverrideTable", theClass); + + if (!theClass->isResilient()) { + // Only resilient classes need such tables. + return; + } + if (theClass->getEffectiveAccess() != AccessLevel::Open) { + // Only classes whose methods could be overridden in different resilience + // domains need an entry. + return; + } + + collectMethods(); + + SmallVector entries; + + for (auto method : vtableMethods) { + auto entry = entryForMethod(method); + if (!entry) { + continue; + } + entries.push_back(*entry); + } + + if (entries.size() == 0) { + // Don't emit empty tables. + return; + } + + SGM.M.createDefaultOverrideTableDefinition(theClass, linkage, entries); + } +}; + +} // end anonymous namespace + +void SILGenModule::emitDefaultOverrideTable(ClassDecl *decl) { + SILLinkage linkage = getSILLinkage(getDeclLinkage(decl), ForDefinition); + + SILGenDefaultOverrideTable builder(*this, decl, linkage); + builder.emitTable(); +} + +SILFunction *SILGenModule::emitDefaultOverride(SILDeclRef replacement, + SILDeclRef original) { + SILGenFunctionBuilder builder(*this); + // Add the "default override of" suffix. + auto name = replacement.mangle() + "Twd"; + auto replacementTy = + Types.getConstantInfo(TypeExpansionContext::minimal(), replacement) + .SILFnType; + auto loc = replacement.getAsRegularLocation(); + auto *function = builder.getOrCreateFunction( + loc, name, SILLinkage::Shared, replacementTy, IsBare, IsNotTransparent, + IsSerialized, IsNotDynamic, IsNotDistributed, IsNotRuntimeAccessible, + ProfileCounter(), IsNotThunk); + + if (!function->empty()) + return function; + + // A shim from yield_once_2 (replacement) to yield_once (original). + // sil @shim : $@convention(method) @yield_once_2 (As...) -> (@yields Ys...) { + // entry(%as... : $As...): + // %method = class_method + // (%ys... : $Ys..., %token) = begin_apply %method(%as...) + // : $@convention(method) @yield_once (As...) -> (@yields Ys...) + // yield %ys : $Ys..., normal normal_block, error error_block + // normal_block: + // %retval = end_apply %token + // return %retval : $() + // error_block: + // abort_apply %token + // unwind + // } + + auto sig = replacementTy->getSubstGenericSignature(); + auto *env = sig.getGenericEnvironment(); + auto subs = env ? env->getForwardingSubstitutionMap() : SubstitutionMap(); + function->setGenericEnvironment(env); + SILGenFunction SGF(*this, *function, SwiftModule); + SmallVector params; + SmallVector indirectResults; + SmallVector indirectErrors; + SGF.collectThunkParams(replacement.getDecl(), params, &indirectResults, + &indirectErrors); + + auto self = params.back(); + + auto originalTy = + Types.getConstantInfo(TypeExpansionContext::minimal(), original) + .SILFnType; + auto originalFn = + SGF.emitClassMethodRef(loc, self.getValue(), original, originalTy); + auto originalConvention = SILFunctionConventions(originalTy, M); + assert(indirectErrors.size() == 0 && + "coroutine accessor with indirect error!?"); + SmallVector args; + for (auto result : indirectResults) { + args.push_back(result.forward(SGF)); + } + for (auto param : params) { + args.push_back(param.forward(SGF)); + } + auto *bai = SGF.B.createBeginApply(loc, originalFn, subs, args); + auto *token = bai->getTokenResult(); + auto yieldedValues = bai->getYieldedValues(); + auto *normalBlock = function->createBasicBlockAfter(SGF.B.getInsertionBB()); + auto *errorBlock = function->createBasicBlockAfter(normalBlock); + SmallVector yields; + for (auto yielded : yieldedValues) { + yields.push_back(yielded); + } + SGF.B.createYield(loc, yields, normalBlock, errorBlock); + + SGF.B.setInsertionPoint(normalBlock); + llvm::SmallVector directResultTypes; + + for (auto result : originalConvention.getDirectSILResults()) { + auto ty = originalConvention.getSILType( + result, function->getTypeExpansionContext()); + ty = function->mapTypeIntoContext(ty); + directResultTypes.push_back(ty.getASTType()); + } + SILType resultTy; + switch (directResultTypes.size()) { + case 0: + resultTy = SILType::getEmptyTupleType(getASTContext()); + break; + case 1: + resultTy = SILType::getPrimitiveObjectType( + directResultTypes.front().getType()->getCanonicalType()); + break; + default: { + ASSERT(directResultTypes.size() > 1); + auto tupleTy = + TupleType::get(directResultTypes, getASTContext())->getCanonicalType(); + resultTy = SILType::getPrimitiveObjectType(tupleTy); + break; + } + } + SGF.B.createEndApply(loc, token, resultTy); + auto *retval = SGF.B.createTuple(loc, {}); + SGF.B.createReturn(loc, retval); + + SGF.B.setInsertionPoint(errorBlock); + SGF.B.createAbortApply(loc, token); + SGF.B.createUnwind(loc); + return function; +} + void SILGenModule::emitNonCopyableTypeDeinitTable(NominalTypeDecl *nom) { auto *dd = nom->getValueTypeDestructor(); if (!dd) @@ -1189,6 +1409,11 @@ class SILGenType : public TypeMemberVisitor { SILGenVTable genVTable(SGM, theClass); genVTable.emitVTable(); } + if (!theClass->hasClangNode() && theClass->isResilient()) { + auto *sourceFile = theClass->getParentSourceFile(); + if (!sourceFile || sourceFile->Kind != SourceFileKind::Interface) + SGM.emitDefaultOverrideTable(theClass); + } } // If this is a nominal type that is move only, emit a deinit table for it. diff --git a/test/SILGen/default_override.swift b/test/SILGen/default_override.swift new file mode 100644 index 0000000000000..f2d74d2bf6ba8 --- /dev/null +++ b/test/SILGen/default_override.swift @@ -0,0 +1,327 @@ +// RUN: %target-swift-emit-silgen \ +// RUN: %s \ +// RUN: -enable-library-evolution \ +// RUN: -enable-experimental-feature BuiltinModule \ +// RUN: -enable-experimental-feature CoroutineAccessors \ +// RUN: | %FileCheck %s + +// RUN: %target-swift-emit-silgen \ +// RUN: %s \ +// RUN: -enable-experimental-feature BuiltinModule \ +// RUN: -enable-experimental-feature CoroutineAccessors \ +// RUN: | %FileCheck %s --check-prefix=CHECK-FRAGILE + +// REQUIRES: swift_feature_CoroutineAccessors +// REQUIRES: swift_feature_BuiltinModule + +import Builtin + +public enum Open {} +public enum Public {} +public enum Internal {} + +open class OpenBase { + open var openField: T { +// CHECK-LABEL: sil shared [serialized] [ossa] @$s16default_override8OpenBaseC9openFieldxvyTwd +// CHECK-SAME: : $@yield_once_2 +// CHECK-SAME: @convention(method) +// CHECK-SAME: <τ_0_0> +// CHECK-SAME: ( +// CHECK-SAME: @guaranteed OpenBase<τ_0_0> +// CHECK-SAME: ) +// CHECK-SAME: -> +// CHECK-SAME: @yields @in_guaranteed τ_0_0 +// CHECK-SAME: { +// CHECK: {{bb[0-9]+}}( +// CHECK-SAME: [[SELF:%[^,]+]] : +// CHECK-SAME: ): +// CHECK: [[ORIGINAL:%[^,]+]] = class_method [[SELF]], #OpenBase.openField!read +// CHECK-SAME: (OpenBase) -> () -> () +// CHECK-SAME: $@yield_once @convention(method) <τ_0_0> (@guaranteed OpenBase<τ_0_0>) -> @yields @in_guaranteed τ_0_0 +// CHECK: ([[ADDR:%[^,]+]], [[TOKEN:%[^,]+]]) = begin_apply [[ORIGINAL]]<τ_0_0>([[SELF]]) +// CHECK: yield [[ADDR]] +// CHECK-SAME: resume [[NORMAL:bb[0-9]+]] +// CHECK-SAME: unwind [[UNWIND:bb[0-9]+]] +// CHECK: [[NORMAL]]: +// CHECK: end_apply [[TOKEN]] +// CHECK: [[RETVAL:%[^,]+]] = tuple () +// CHECK: return [[RETVAL]] +// CHECK: [[UNWIND]]: +// CHECK: abort_apply [[TOKEN]] +// CHECK: unwind +// CHECK-LABEL: } // end sil function '$s16default_override8OpenBaseC9openFieldxvyTwd' + read { + Builtin.int_trap() + } +// CHECK-LABEL: sil shared [serialized] [ossa] @$s16default_override8OpenBaseC9openFieldxvxTwd +// CHECK-SAME: : $@yield_once_2 +// CHECK-SAME: @convention(method) +// CHECK-SAME: <τ_0_0> +// CHECK-SAME: ( +// CHECK-SAME: @guaranteed OpenBase<τ_0_0> +// CHECK-SAME: ) +// CHECK-SAME: -> +// CHECK-SAME: @yields @inout τ_0_0 +// CHECK-SAME: { +// CHECK: {{bb[0-9]+}}( +// CHECK-SAME: [[SELF:%[^,]+]] : +// CHECK-SAME: ): +// CHECK: [[ORIGINAL:%[^,]+]] = class_method [[SELF]], #OpenBase.openField!modify +// CHECK: (OpenBase) -> () -> () +// CHECK: $@yield_once @convention(method) <τ_0_0> (@guaranteed OpenBase<τ_0_0>) -> @yields @inout τ_0_0 +// CHECK: ([[ADDR:%[^,]+]], [[TOKEN:%[^,]+]]) = begin_apply [[ORIGINAL]]<τ_0_0>([[SELF]]) +// CHECK: yield [[ADDR]] +// CHECK: resume [[NORMAL:bb[0-9]+]] +// CHECK: unwind [[UNWIND:bb[0-9]+]] +// CHECK: [[NORMAL]]: +// CHECK: end_apply [[TOKEN]] +// CHECK: [[RETVAL:%[^,]+]] = tuple () +// CHECK: return [[RETVAL]] +// CHECK: [[UNWIND]]: +// CHECK: abort_apply [[TOKEN]] +// CHECK: unwind +// CHECK-LABEL: } // end sil function '$s16default_override8OpenBaseC9openFieldxvxTwd' + modify { + Builtin.int_trap() + } + } + open subscript(_ u: U, _ _: Open.Type) -> T { +// CHECK-LABEL: sil shared [serialized] [ossa] @$s16default_override8OpenBaseCyxqd___AA0C0OmtcluiyTwd +// CHECK-SAME: : $@yield_once_2 +// CHECK-SAME: @convention(method) +// CHECK-SAME: <τ_0_0><τ_1_0> +// CHECK-SAME: ( +// CHECK-SAME: @in_guaranteed τ_1_0 +// CHECK-SAME: @thin Open.Type +// CHECK-SAME: @guaranteed OpenBase<τ_0_0> +// CHECK-SAME: ) +// CHECK-SAME: -> +// CHECK-SAME: @yields @in_guaranteed τ_0_0 +// CHECK-SAME: { +// CHECK: {{bb[0-9]+}}( +// CHECK-SAME: [[KEY:%[^,]+]] : +// CHECK-SAME: [[OPEN_TY:%[^,]+]] : +// CHECK-SAME: [[SELF:%[^,]+]] : +// CHECK-SAME: ): +// CHECK: [[ORIGINAL:%[^,]+]] = class_method [[SELF]] +// CHECK: #OpenBase.subscript!read +// CHECK: (OpenBase) -> (U, Open.Type) -> () +// CHECK: $@yield_once @convention(method) <τ_0_0><τ_1_0> (@in_guaranteed τ_1_0, @thin Open.Type, @guaranteed OpenBase<τ_0_0>) -> @yields @in_guaranteed τ_0_0 +// CHECK: ([[ADDR:%[^,]+]], [[TOKEN:%[^,]+]]) = begin_apply [[ORIGINAL]]<τ_0_0, τ_1_0>([[KEY]], [[OPEN_TY]], [[SELF]]) +// CHECK: yield [[ADDR]] +// CHECK: resume bb1 +// CHECK: unwind bb2 +// CHECK: bb1: +// CHECK: end_apply [[TOKEN]] +// CHECK: [[RETVAL:%[^,]+]] = tuple () +// CHECK: return [[RETVAL]] +// CHECK: bb2: +// CHECK: abort_apply [[TOKEN]] +// CHECK: unwind +// CHECK-LABEL: } // end sil function '$s16default_override8OpenBaseCyxqd___AA0C0OmtcluiyTwd' + read { + Builtin.int_trap() + } +// CHECK-LABEL: sil shared [serialized] [ossa] @$s16default_override8OpenBaseCyxqd___AA0C0OmtcluixTwd +// CHECK-SAME: : $@yield_once_2 +// CHECK-SAME: @convention(method) +// CHECK-SAME: <τ_0_0><τ_1_0> +// CHECK-SAME: ( +// CHECK-SAME: @in_guaranteed τ_1_0 +// CHECK-SAME: @thin Open.Type +// CHECK-SAME: @guaranteed OpenBase<τ_0_0> +// CHECK-SAME: ) +// CHECK-SAME: -> +// CHECK-SAME: @yields @inout τ_0_0 +// CHECK-SAME: { +// CHECK: {{bb[0-9]+}}( +// CHECK-SAME: [[KEY:%[^,]+]] : +// CHECK-SAME: [[OPEN_TY:%[^,]+]] : +// CHECK-SAME: [[SELF:%[^,]+]] : +// CHECK-SAME: ): +// CHECK: [[ORIGINAL:%[^,]+]] = class_method [[SELF]] +// CHECK-SAME: #OpenBase.subscript!modify +// CHECK-SAME: (OpenBase) -> (U, Open.Type) -> () +// CHECK-SAME: $@yield_once @convention(method) <τ_0_0><τ_1_0> (@in_guaranteed τ_1_0, @thin Open.Type, @guaranteed OpenBase<τ_0_0>) -> @yields @inout τ_0_0 +// CHECK: ([[ADDR:%[^,]+]], [[TOKEN:%[^,]+]]) = begin_apply [[ORIGINAL]]<τ_0_0, τ_1_0>([[KEY]], [[OPEN_TY]], [[SELF]]) +// CHECK: yield [[ADDR]] +// CHECK: resume [[NORMAL:bb[0-9]+]] +// CHECK: unwind [[UNWIND:bb[0-9]+]] +// CHECK: [[NORMAL]]: +// CHECK: end_apply [[TOKEN]] +// CHECK: [[RETVAL:%[^,]+]] = tuple () +// CHECK: return [[RETVAL]] +// CHECK: [[UNWIND]]: +// CHECK: abort_apply [[TOKEN]] +// CHECK: unwind +// CHECK-LABEL: } // end sil function '$s16default_override8OpenBaseCyxqd___AA0C0OmtcluixTwd' + modify { + Builtin.int_trap() + } + } + public var publicField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + public subscript(_ u: U, _ _: Public.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + var internalField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + subscript(_ u: U, _ _: Internal.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } +} + +public class PublicBase { + open var openField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + open subscript(_ u: U, _ _: Open.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + public var publicField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + public subscript(_ u: U, _ _: Public.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + var internalField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + subscript(_ u: U, _ _: Internal.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } +} + +class InternalBase { + open var openField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + open subscript(_ u: U, _ _: Open.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + public var publicField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + public subscript(_ u: U, _ _: Public.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + var internalField: T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } + subscript(_ u: U, _ _: Internal.Type) -> T { + read { + Builtin.int_trap() + } + modify { + Builtin.int_trap() + } + } +} + +// CHECK-LABEL: sil_default_override_table OpenBase { +// CHECK-NEXT: #OpenBase.openField!read2 +// CHECK-SAME: #OpenBase.openField!read +// CHECK-SAME: (OpenBase) -> () -> () +// CHECK-SAME: @$s16default_override8OpenBaseC9openFieldxvyTwd +// CHECK-NEXT: #OpenBase.openField!modify2 +// CHECK-SAME: #OpenBase.openField!modify +// CHECK-SAME: (OpenBase) -> () -> () +// CHECK-SAME: @$s16default_override8OpenBaseC9openFieldxvxTwd +// CHECK-NEXT: #OpenBase.subscript!read2 +// CHECK-SAME: #OpenBase.subscript!read +// CHECK-SAME: (OpenBase) -> (U, Open.Type) -> () +// CHECK-SAME: @$s16default_override8OpenBaseCyxqd___AA0C0OmtcluiyTwd +// CHECK-NEXT: #OpenBase.subscript!modify2 +// CHECK-SAME: #OpenBase.subscript!modify +// CHECK-SAME: (OpenBase) -> (U, Open.Type) -> () +// CHECK-SAME: @$s16default_override8OpenBaseCyxqd___AA0C0OmtcluixTwd +// CHECK-NOT: #OpenBase.publicField!read2 +// CHECK-NOT: #OpenBase.publicField!modify2 +// CHECK-NOT: #OpenBase.subscript!read2: #OpenBase.subscript!read: (OpenBase) -> (U, Public.Type) -> () +// CHECK-NOT: #OpenBase.subscript!modify2: #OpenBase.subscript!modify: (OpenBase) -> (U, Public.Type) -> () +// CHECK-NEXT: } + +// CHECK-NOT: sil_default_override_table PublicBase { + +// CHECK-NOT: sil_default_override_table InternalBase { + +// CHECK-FRAGILE-NOT: sil_default_override_table OpenBase { +// CHECK-FRAGILE-NOT: sil_default_override_table PublicBase { +// CHECK-FRAGILE-NOT: sil_default_override_table InternalBase { From 4951e9d0914695b316d65ae9fe4e1c654383ed74 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 24 Mar 2025 08:31:21 -0700 Subject: [PATCH 18/21] [DFE] Default overrides are alive. They are used externally and can't be eliminated as dead when there are no uses from SIL (of which in fact there never are any). --- lib/SILOptimizer/IPO/DeadFunctionElimination.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index e45dfe2c89285..cfa93bde2983c 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -643,6 +643,14 @@ class DeadFunctionAndGlobalElimination { } } } + + // Check default override tables. + for (auto &table : Module->getDefaultOverrideTableList()) { + for (auto &entry : table.getEntries()) { + ensureAlive(entry.impl); + } + } + // Check property descriptor implementations. for (SILProperty &P : Module->getPropertyList()) { if (auto component = P.getComponent()) { From 2cdbfa77bf9b26cdff28a53ceb0d6143e6f907fb Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 21 Mar 2025 11:47:16 -0700 Subject: [PATCH 19/21] [DefaultOverrides] IRGen. --- include/swift/ABI/MetadataValues.h | 7 +++ lib/IRGen/GenClass.cpp | 10 +++++ lib/IRGen/GenDecl.cpp | 3 ++ lib/IRGen/GenMeta.cpp | 69 ++++++++++++++++++++++++++++++ lib/IRGen/IRGenModule.cpp | 4 ++ lib/IRGen/IRGenModule.h | 4 ++ test/IRGen/default_override.sil | 62 +++++++++++++++++++++++++++ 7 files changed, 159 insertions(+) create mode 100644 test/IRGen/default_override.sil diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 1ea9ffb8366d6..f258ffcc7f582 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1955,8 +1955,12 @@ class TypeContextDescriptorFlags : public FlagSet { /// Set if the metadata contains a pointer to a layout string HasLayoutString = 4, + /// WARNING: 5 is the last bit! + // Type-specific flags: + Class_HasDefaultOverrideTable = 6, + /// Set if the class is an actor. /// /// Only meaningful for class descriptors. @@ -2062,6 +2066,9 @@ class TypeContextDescriptorFlags : public FlagSet { FLAGSET_DEFINE_FLAG_ACCESSORS(Class_IsActor, class_isActor, class_setIsActor) + FLAGSET_DEFINE_FLAG_ACCESSORS(Class_HasDefaultOverrideTable, + class_hasDefaultOverrideTable, + class_setHasDefaultOverrideTable) FLAGSET_DEFINE_FIELD_ACCESSORS(Class_ResilientSuperclassReferenceKind, Class_ResilientSuperclassReferenceKind_width, diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index b5609be0ec228..1b7f1356eac76 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -32,6 +32,7 @@ #include "swift/Basic/Defer.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/IRGen/Linking.h" +#include "swift/SIL/SILDefaultOverrideTable.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILType.h" #include "swift/SIL/SILVTableVisitor.h" @@ -3157,3 +3158,12 @@ irgen::emitVirtualMethodValue(IRGenFunction &IGF, return emitVirtualMethodValue(IGF, metadata, method, methodType); } + +void IRGenerator::ensureRelativeSymbolCollocation(SILDefaultOverrideTable &ot) { + if (!CurrentIGM) + return; + + for (auto &entry : ot.getEntries()) { + forceLocalEmitOfLazyFunction(entry.impl); + } +} diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index b05cf43ae4968..731c9ce58b2aa 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1193,6 +1193,9 @@ void IRGenerator::emitGlobalTopLevel( CurrentIGMPtr IGM = getGenModule(wt.getProtocol()->getDeclContext()); ensureRelativeSymbolCollocation(wt); } + for (auto &ot : PrimaryIGM->getSILModule().getDefaultOverrideTableList()) { + ensureRelativeSymbolCollocation(ot); + } for (auto &directive: linkerDirectives) { createLinkerDirectiveVariable(*PrimaryIGM, directive); } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 57b0fe755e5b7..7da5dcfa22506 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -2036,6 +2036,7 @@ namespace { maybeAddCanonicalMetadataPrespecializations(); addInvertedProtocols(); maybeAddSingletonMetadataPointer(); + maybeAddDefaultOverrideTable(); } void addIncompleteMetadataOrRelocationFunction() { @@ -2077,6 +2078,9 @@ namespace { if (getType()->isDefaultActor(IGM.getSwiftModule(), ResilienceExpansion::Maximal)) flags.class_setIsDefaultActor(true); + + if (getDefaultOverrideTable()) + flags.class_setHasDefaultOverrideTable(true); } if (ResilientSuperClassRef) { @@ -2286,6 +2290,71 @@ namespace { descriptor.finishAndAddTo(B); } + SILDefaultOverrideTable *getDefaultOverrideTable() { + auto *table = IGM.getSILModule().lookUpDefaultOverrideTable(getType()); + if (!table) + return nullptr; + + if (table->getEntries().size() == 0) + return nullptr; + + return table; + } + + void maybeAddDefaultOverrideTable() { + auto *table = getDefaultOverrideTable(); + if (!table) + return; + + LLVM_DEBUG(llvm::dbgs() << "Default Override Table entries for " + << getType()->getName() << ":\n"; + for (auto entry + : table->getEntries()) { + llvm::dbgs() << " "; + llvm::dbgs() << "original(" << entry.original << ")"; + llvm::dbgs() << " -> "; + llvm::dbgs() << "replacement(" << entry.method << ")"; + llvm::dbgs() << " -> "; + llvm::dbgs() << "impl(" << entry.impl->getName() << ")"; + llvm::dbgs() << '\n'; + }); + + B.addInt32(table->getEntries().size()); + + for (auto entry : table->getEntries()) + emitDefaultOverrideDescriptor(entry.method, entry.original, entry.impl); + } + + void emitDefaultOverrideDescriptor(SILDeclRef replacement, + SILDeclRef original, SILFunction *impl) { + auto descriptor = + B.beginStruct(IGM.MethodDefaultOverrideDescriptorStructTy); + + auto replacementEntity = LinkEntity::forMethodDescriptor(replacement); + auto replacementDescriptor = + IGM.getAddrOfLLVMVariableOrGOTEquivalent(replacementEntity); + descriptor.addRelativeAddress(replacementDescriptor); + + auto originalEntity = LinkEntity::forMethodDescriptor(original); + auto originalDescriptor = + IGM.getAddrOfLLVMVariableOrGOTEquivalent(originalEntity); + descriptor.addRelativeAddress(originalDescriptor); + + if (impl->isAsync()) { + llvm::Constant *implFn = IGM.getAddrOfAsyncFunctionPointer(impl); + descriptor.addRelativeAddress(implFn); + } else if (impl->getLoweredFunctionType()->isCalleeAllocatedCoroutine()) { + llvm::Constant *implFn = IGM.getAddrOfCoroFunctionPointer(impl); + descriptor.addRelativeAddress(implFn); + } else { + llvm::Function *implFn = + IGM.getAddrOfSILFunction(impl, NotForDefinition); + descriptor.addCompactFunctionReference(implFn); + } + + descriptor.finishAndAddTo(B); + } + void addPlaceholder(MissingMemberDecl *MMD) { llvm_unreachable("cannot generate metadata with placeholders in it"); } diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 24e8af57fbd5d..7a54c0c0c8bb7 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -502,6 +502,10 @@ IRGenModule::IRGenModule(IRGenerator &irgen, RelativeAddressTy }); + MethodDefaultOverrideDescriptorStructTy = createStructType( + *this, "swift.method_default_override_descriptor", + {RelativeAddressTy, RelativeAddressTy, RelativeAddressTy}); + TypeMetadataRecordTy = createStructType(*this, "swift.type_metadata_record", { RelativeAddressTy diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index d356799379e49..3a3e4c70f2b73 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -112,6 +112,7 @@ namespace swift { class SILDifferentiabilityWitness; class SILGlobalVariable; class SILModule; + class SILDefaultOverrideTable; class SILProperty; class SILType; class SILVTable; @@ -464,6 +465,8 @@ class IRGenerator { void ensureRelativeSymbolCollocation(SILDefaultWitnessTable &wt); + void ensureRelativeSymbolCollocation(SILDefaultOverrideTable &ot); + llvm::SmallVector, 4> metadataPrespecializationsForType(NominalTypeDecl *type) { return MetadataPrespecializationsForGenericTypes.lookup(type); @@ -789,6 +792,7 @@ class IRGenModule { llvm::StructType *ClassContextDescriptorTy; llvm::StructType *MethodDescriptorStructTy; /// %swift.method_descriptor llvm::StructType *MethodOverrideDescriptorStructTy; /// %swift.method_override_descriptor + llvm::StructType *MethodDefaultOverrideDescriptorStructTy; /// %swift.method_default_override_descriptor llvm::StructType *TypeMetadataRecordTy; llvm::PointerType *TypeMetadataRecordPtrTy; llvm::StructType *FieldDescriptorTy; diff --git a/test/IRGen/default_override.sil b/test/IRGen/default_override.sil new file mode 100644 index 0000000000000..ccf09349c6c6e --- /dev/null +++ b/test/IRGen/default_override.sil @@ -0,0 +1,62 @@ +// RUN: %target-swift-frontend \ +// RUN: %s \ +// RUN: -module-name Library \ +// RUN: -emit-ir \ +// RUN: -enable-experimental-feature CoroutineAccessors \ +// RUN: -enable-library-evolution \ +// RUN: | %IRGenFileCheck %s + +// REQUIRES: swift_feature_CoroutineAccessors + +sil_stage canonical + +import Builtin + +struct Int { + var _value: Builtin.Int64 +} + +open class B { + open var i: Int { read set } +} + +sil @B_i_read : $@yield_once @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_read2 : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_set : $@convention(method) (Int, @guaranteed B) -> () +sil @B_i_modify : $@yield_once @convention(method) (@guaranteed B) -> @yields @inout Int +sil @B_i_modify2 : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields @inout Int + +sil_vtable B { + #B.i!read: (B) -> () -> () : @B_i_read + #B.i!read2: (B) -> () -> () : @B_i_read2 + #B.i!setter: (B) -> (Int) -> () : @B_i_set + #B.i!modify: (B) -> () -> () : @B_i_modify + #B.i!modify2: (B) -> () -> () : @B_i_modify2 +} + +sil @B_i_read2_default_override : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields Int +sil @B_i_modify2_default_override : $@yield_once_2 @convention(method) (@guaranteed B) -> @yields @inout Int + +sil_default_override_table B { + #B.i!read2: #B.i!read: (B) -> () -> () : @B_i_read2_default_override + #B.i!modify2: #B.i!modify: (B) -> () -> () : @B_i_modify2_default_override +} + +// CHECK: %swift.method_default_override_descriptor = type { i32, i32, i32 } + +// CHECK-LABEL: @"$s7Library1BCMn" = {{.*}}constant <{ + // Other stuff. +// CHECK-SAME: i32 +// CHECK-SAME: %swift.method_default_override_descriptor +// CHECK-SAME: %swift.method_default_override_descriptor +// CHECK-SAME:}> +// CHECK-SAME:<{ + // Eventually... +// CHECK-SAME: i32 2 +// CHECK-SAME: %swift.method_default_override_descriptor { +// CHECK-SAME: B_i_read2_default_overrideTwc +// CHECK-SAME: } +// CHECK-SAME: %swift.method_default_override_descriptor { +// CHECK-SAME: B_i_modify2_default_overrideTwc +// CHECK-SAME: } +// CHECK-SAME:}> From ddb89af28385b378d24c7599b55fe26debb6283d Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 21 Mar 2025 14:48:28 -0700 Subject: [PATCH 20/21] [NFC] Runtime: Return early here. In preparation for adding more work to this branch. --- stdlib/public/runtime/Metadata.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 1c81fd8f27c8c..0cbd7a87820e9 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3701,15 +3701,18 @@ static void initClassVTable(ClassMetadata *self) { } } - if (description->hasOverrideTable()) { - auto *overrideTable = description->getOverrideTable(); - auto overrideDescriptors = description->getMethodOverrideDescriptors(); - for (auto &descriptor : overrideDescriptors) { - installOverrideInVTable( - descriptor.Class.get(), descriptor.Method.get(), - [&descriptor]() { return descriptor.getImpl(); }, overrideTable, - classWords); - } + if (!description->hasOverrideTable()) { + // The class didn't override anything, so we're done. + return; + } + + auto *overrideTable = description->getOverrideTable(); + auto overrideDescriptors = description->getMethodOverrideDescriptors(); + for (auto &descriptor : overrideDescriptors) { + installOverrideInVTable( + descriptor.Class.get(), descriptor.Method.get(), + [&descriptor]() { return descriptor.getImpl(); }, overrideTable, + classWords); } } From 5534eb40436487dd2038cb3f0ac035bb0ca9b7e2 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 21 Mar 2025 17:08:27 -0700 Subject: [PATCH 21/21] [DefaultOverides] Install in vtables at runtime. --- include/swift/ABI/Metadata.h | 90 ++++++- stdlib/public/runtime/Metadata.cpp | 65 ++++- ...ne_accessors_default_implementations.swift | 240 ++++++++++++++++++ 3 files changed, 388 insertions(+), 7 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 1e4619b1a2731..0789767055800 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -654,6 +654,54 @@ using TargetRelativeProtocolRequirementPointer = using RelativeProtocolRequirementPointer = TargetRelativeProtocolRequirementPointer; +/// An entry in the default override table, consisting of one of our methods +/// `replacement` together with (1) another of our methods `original` which +/// might have been overridden by a subclass and (2) an implementation of +/// `replacement` to be used by such a subclass if it does not provide its own +/// implementation. +template +struct TargetMethodDefaultOverrideDescriptor { + /// The method which replaced the original at call-sites. + TargetRelativeMethodDescriptorPointer Replacement; + + /// The method originally called at such call sites. + TargetRelativeMethodDescriptorPointer Original; + + union { + TargetCompactFunctionPointer Impl; + TargetRelativeDirectPointer AsyncImpl; + TargetRelativeDirectPointer CoroImpl; + }; + + bool isData() const { + auto *replacement = Replacement.get(); + assert(replacement && "no replacement"); + return replacement->Flags.isData(); + } + + void *getImpl() const { + auto *replacement = Replacement.get(); + assert(replacement && "no replacement"); + if (replacement->Flags.isAsync()) { + return AsyncImpl.get(); + } else if (replacement->Flags.isCalleeAllocatedCoroutine()) { + return CoroImpl.get(); + } else { + return Impl.get(); + } + } +}; + +/// Header for a table of default override descriptors. Such a table is a +/// variable-sized structure whose length is stored in this header which is +/// followed by that many TargetMethodDefaultOverrideDescriptors. +template +struct TargetMethodDefaultOverrideTableHeader { + /// The number of TargetMethodDefaultOverrideDescriptor records following this + /// header in the class's nominal type descriptor. + uint32_t NumEntries; +}; + /// An entry in the method override table, referencing a method from one of our /// ancestor classes, together with an implementation. template @@ -4205,9 +4253,11 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) TargetCanonicalSpecializedMetadataAccessorsListEntry, TargetCanonicalSpecializedMetadatasCachingOnceToken, InvertibleProtocolSet, - TargetSingletonMetadataPointer> { + TargetSingletonMetadataPointer, + TargetMethodDefaultOverrideTableHeader, + TargetMethodDefaultOverrideDescriptor> { private: - using TrailingGenericContextObjects = + using TrailingGenericContextObjects = swift::TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, TargetResilientSuperclass, @@ -4223,7 +4273,9 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) TargetCanonicalSpecializedMetadataAccessorsListEntry, TargetCanonicalSpecializedMetadatasCachingOnceToken, InvertibleProtocolSet, - TargetSingletonMetadataPointer>; + TargetSingletonMetadataPointer, + TargetMethodDefaultOverrideTableHeader, + TargetMethodDefaultOverrideDescriptor>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4254,6 +4306,10 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) using MetadataCachingOnceToken = TargetCanonicalSpecializedMetadatasCachingOnceToken; using SingletonMetadataPointer = TargetSingletonMetadataPointer; + using DefaultOverrideTableHeader = + TargetMethodDefaultOverrideTableHeader; + using DefaultOverrideDescriptor = + TargetMethodDefaultOverrideDescriptor; using StoredPointer = typename Runtime::StoredPointer; using StoredPointerDifference = typename Runtime::StoredPointerDifference; @@ -4399,6 +4455,16 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) return this->hasSingletonMetadataPointer() ? 1 : 0; } + size_t numTrailingObjects(OverloadToken) const { + return hasDefaultOverrideTable() ? 1 : 0; + } + + size_t numTrailingObjects(OverloadToken) const { + if (!hasDefaultOverrideTable()) + return 0; + return getDefaultOverrideTable()->NumEntries; + } + public: const TargetRelativeDirectPointer & getResilientSuperclass() const { @@ -4430,6 +4496,10 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) return FieldOffsetVectorOffset; } + bool hasDefaultOverrideTable() const { + return getTypeContextDescriptorFlags().class_hasDefaultOverrideTable(); + } + bool isActor() const { return this->getTypeContextDescriptorFlags().class_isActor(); } @@ -4476,6 +4546,20 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) numTrailingObjects(OverloadToken{})}; } + const DefaultOverrideTableHeader *getDefaultOverrideTable() const { + if (!hasDefaultOverrideTable()) + return nullptr; + return this->template getTrailingObjects(); + } + + llvm::ArrayRef getDefaultOverrideDescriptors() + const { + if (!hasDefaultOverrideTable()) + return {}; + return {this->template getTrailingObjects(), + numTrailingObjects(OverloadToken{})}; + } + /// Return the bounds of this class's metadata. TargetClassMetadataBounds getMetadataBounds() const { if (!hasResilientSuperclass()) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 0cbd7a87820e9..21df9c3346265 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3682,9 +3682,11 @@ static void installOverrideInVTable(ContextDescriptor const *baseContext, } /// Using the information in the class context descriptor, fill in in the -/// immediate vtable entries for the class and install overrides of any -/// superclass vtable entries. -static void initClassVTable(ClassMetadata *self) { +/// immediate vtable entries for the class install overrides of any +/// superclass vtable entries, and install any default overrides if appropriate. +static void initClassVTable(ClassMetadata *self, + llvm::SmallVectorImpl + &superclassesWithDefaultOverrides) { const auto *description = self->getDescription(); auto *classWords = reinterpret_cast(self); @@ -3706,14 +3708,46 @@ static void initClassVTable(ClassMetadata *self) { return; } + auto hasSuperclassWithDefaultOverride = + superclassesWithDefaultOverrides.size() > 0; + std::unordered_set seenDescriptors; + + // Install our overrides. auto *overrideTable = description->getOverrideTable(); auto overrideDescriptors = description->getMethodOverrideDescriptors(); for (auto &descriptor : overrideDescriptors) { + if (hasSuperclassWithDefaultOverride) + seenDescriptors.insert(descriptor.Method); + installOverrideInVTable( descriptor.Class.get(), descriptor.Method.get(), [&descriptor]() { return descriptor.getImpl(); }, overrideTable, classWords); } + + if (!hasSuperclassWithDefaultOverride) { + // No ancestor had default overrides to consider, so we're done. + return; + } + + // Install any necessary default overrides. + for (auto *description : superclassesWithDefaultOverrides) { + assert(description->hasDefaultOverrideTable()); + auto *header = description->getDefaultOverrideTable(); + assert(header->NumEntries > 0 && "default override table with 0 entries"); + auto entries = description->getDefaultOverrideDescriptors(); + for (auto &entry : entries) { + auto *original = entry.Original.get(); + if (!seenDescriptors.count(original)) + continue; + auto *replacement = entry.Replacement.get(); + if (seenDescriptors.count(replacement)) + continue; + installOverrideInVTable( + description, replacement, [&entry]() { return entry.getImpl(); }, + header, classWords); + } + } } static void initClassFieldOffsetVector(ClassMetadata *self, @@ -4059,6 +4093,7 @@ _swift_initClassMetadataImpl(ClassMetadata *self, auto superDependencyAndSuper = getSuperclassMetadata(self, allowDependency); if (superDependencyAndSuper.first) return superDependencyAndSuper.first; + auto super = superDependencyAndSuper.second; self->Superclass = super; @@ -4079,6 +4114,28 @@ _swift_initClassMetadataImpl(ClassMetadata *self, nullptr); #endif + // To populate the vtable, we must check our ancestors for default overloads. + // We may obstructed by dependencies, however, so do it now before doing any + // setup. + llvm::SmallVector + superclassesWithDefaultOverrides; + if (self->getDescription()->hasOverrideTable()) { + const ClassMetadata *super = superDependencyAndSuper.second; + while (super && !super->isPureObjC()) { + const auto *description = super->getDescription(); + if (description->hasDefaultOverrideTable()) { + // This superclass has default overrides. Record it for later + // traversal. + superclassesWithDefaultOverrides.push_back(description); + } + auto superDependencyAndSuper = + getSuperclassMetadata(super, allowDependency); + if (superDependencyAndSuper.first) + return superDependencyAndSuper.first; + super = superDependencyAndSuper.second; + } + } + // Copy field offsets, generic arguments and (if necessary) vtable entries // from our superclass. copySuperclassMetadataToSubclass(self, layoutFlags); @@ -4086,7 +4143,7 @@ _swift_initClassMetadataImpl(ClassMetadata *self, // Copy the class's immediate methods from the nominal type descriptor // to the class metadata. if (!hasStaticVTable(layoutFlags)) - initClassVTable(self); + initClassVTable(self, superclassesWithDefaultOverrides); initClassFieldOffsetVector(self, numFields, fieldTypes, fieldOffsets); diff --git a/test/Interpreter/coroutine_accessors_default_implementations.swift b/test/Interpreter/coroutine_accessors_default_implementations.swift index 0367016f25ef7..a75e06e64794f 100644 --- a/test/Interpreter/coroutine_accessors_default_implementations.swift +++ b/test/Interpreter/coroutine_accessors_default_implementations.swift @@ -76,6 +76,25 @@ public func getAnyP() -> any P { return I() } +public func readSomeB(_ b: B) { + print(#function, "b.i", b.i) + print(#function, "b[5]", b[5]) +} + +public func modifySomeB(_ b: inout B) { + print(#function, "begin") + increment(int: &b.i) + increment(int: &b[5]) + print(#function, "end") +} + +public func modifySomeB2(_ b: inout B2) { + print(#function, "begin") + increment(int: &b.i) + increment(int: &b[5]) + print(#function, "end") +} + //--- LibImpl.underscored.swift struct I : P { @@ -92,6 +111,54 @@ struct I : P { } } +open class B { + public init() {} + @_borrowed + open var i: Int { + _read { + fatalError() + } + _modify { + fatalError() + } + } + @_borrowed + open subscript(i : Int) -> Int { + _read { + fatalError() + } + _modify { + fatalError() + } + } +} + +open class B2 { + public init() {} + open var i: Int { + get { + fatalError() + } + set { + fatalError() + } + _modify { + fatalError() + } + } + open subscript(i : Int) -> Int { + get { + fatalError() + } + set { + fatalError() + } + _modify { + fatalError() + } + } +} + //--- LibImpl.nonunderscored.swift struct I : P { @@ -127,6 +194,52 @@ struct I : P { } } +open class B { + public init() {} + open var i: Int { + read { + fatalError() + } + modify { + fatalError() + } + } + open subscript(i : Int) -> Int { + read { + fatalError() + } + modify { + fatalError() + } + } +} + +open class B2 { + public init() {} + open var i: Int { + get { + fatalError() + } + set { + fatalError() + } + modify { + fatalError() + } + } + open subscript(i : Int) -> Int { + get { + fatalError() + } + set { + fatalError() + } + modify { + fatalError() + } + } +} + //--- Executable.swift import Library @@ -163,6 +276,82 @@ public struct S : P { } } +class D : B { + var _iImpl : Int + @_borrowed + override var i : Int { + _read { + print(#function, "before yield", _iImpl) + yield _iImpl + print(#function, "after yield", _iImpl) + } + _modify { + print(#function, "before yield", _iImpl) + yield &_iImpl + print(#function, "after yield", _iImpl) + } + } + var _sImpl: [Int] + @_borrowed + override subscript(i : Int) -> Int { + _read { + print(#function, "before yield", _sImpl[i]) + yield _sImpl[i] + print(#function, "after yield", _sImpl[i]) + } + _modify { + print(#function, "before yield", _sImpl[i]) + yield &_sImpl[i] + print(#function, "after yield", _sImpl[i]) + } + } + override init() { + self._iImpl = 42 + self._sImpl = [1, 1, 2, 3, 5, 8, 13, 21] + } +} + +class D2 : B2 { + var _iImpl : Int + override var i : Int { + get { + print(#function, "before get", _sImpl[i]) + return _sImpl[i] + } + set { + print(#function, "before set", _sImpl[i]) + _sImpl[i] = newValue + print(#function, "after set", _sImpl[i]) + } + _modify { + print(#function, "before yield", _iImpl) + yield &_iImpl + print(#function, "after yield", _iImpl) + } + } + var _sImpl: [Int] + override subscript(i : Int) -> Int { + get { + print(#function, "before get", _sImpl[i]) + return _sImpl[i] + } + set { + print(#function, "before set", _sImpl[i]) + _sImpl[i] = newValue + print(#function, "after set", _sImpl[i]) + } + _modify { + print(#function, "before yield", _sImpl[i]) + yield &_sImpl[i] + print(#function, "after yield", _sImpl[i]) + } + } + override init() { + self._iImpl = 42 + self._sImpl = [1, 1, 2, 3, 5, 8, 13, 21] + } +} + func readFromSomePLocal(_ p: T) { print(#function, "p.i", p.i) print(#function, "p[5]", p[5]) @@ -175,6 +364,26 @@ func modifySomePLocal(_ p: inout T) { print(#function, "end") } +func getSomeBLocal() -> B { + return D() +} + +public func getSomeB2Local() -> B2 { + return D2() +} + +func readSomeBLocal(_ b: B) { + print(#function, "p.i", b.i) + print(#function, "p[5]", b[5]) +} + +func modifySomeBLocal(_ b: inout B) { + print(#function, "begin") + increment(int: &b.i) + increment(int: &b[5]) + print(#function, "end") +} + @main struct M { static func main() { var s = S() @@ -251,6 +460,37 @@ func modifySomePLocal(_ p: inout T) { // CHECK-NEXT: subscript(_:) after yield 10 // CHECK-NEXT: readFromSomeP(_:) p[5] 10 readFromSomeP(sp) + var sb = getSomeBLocal() +// CHECK-NEXT: i before yield 42 +// CHECK-NEXT: i after yield 42 +// CHECK-NEXT: readSomeB(_:) b.i 42 +// CHECK-NEXT: subscript(_:) before yield 8 +// CHECK-NEXT: subscript(_:) after yield 8 +// CHECK-NEXT: readSomeB(_:) b[5] 8 + readSomeB(sb) +// CHECK-NEXT: modifySomeB(_:) begin +// CHECK-NEXT: i before yield 42 +// CHECK-NEXT: increment(int:) before increment 42 +// CHECK-NEXT: increment(int:) after increment 43 +// CHECK-NEXT: i after yield 43 +// CHECK-NEXT: subscript(_:) before yield 8 +// CHECK-NEXT: increment(int:) before increment 8 +// CHECK-NEXT: increment(int:) after increment 9 +// CHECK-NEXT: subscript(_:) after yield 9 +// CHECK-NEXT: modifySomeB(_:) end + modifySomeB(&sb) + var sb2 = getSomeB2Local() +// CHECK-NEXT: modifySomeB2(_:) begin +// CHECK-NEXT: i before yield 42 +// CHECK-NEXT: increment(int:) before increment 42 +// CHECK-NEXT: increment(int:) after increment 43 +// CHECK-NEXT: i after yield 43 +// CHECK-NEXT: subscript(_:) before yield 8 +// CHECK-NEXT: increment(int:) before increment 8 +// CHECK-NEXT: increment(int:) after increment 9 +// CHECK-NEXT: subscript(_:) after yield 9 +// CHECK-NEXT: modifySomeB2(_:) end + modifySomeB2(&sb2) } }