Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Dynamic function replacement #20333

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ types where the metadata itself has unknown layout.)
global ::= global 'To' // swift-as-ObjC thunk
global ::= global 'TD' // dynamic dispatch thunk
global ::= global 'Td' // direct method reference thunk
global ::= global 'TI' // implementation of a dynamic_replaceable function
global ::= global 'TX' // function pointer of a dynamic_replaceable 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)
Expand Down
70 changes: 70 additions & 0 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -4230,6 +4230,76 @@ TargetTypeContextDescriptor<Runtime>::getSingletonMetadataInitialization() const
}
}

/// An entry in the chain of dynamic replacement functions.
struct DynamicReplacementChainEntry {
void *implementationFunction;
DynamicReplacementChainEntry *next;
};

/// A record describing the root of dynamic replacements for a function.
struct DynamicReplacementKey {
RelativeDirectPointer<DynamicReplacementChainEntry, false> root;
uint32_t flags;
};

/// A record describing a dynamic function replacement.
class DynamicReplacementDescriptor {
RelativeIndirectablePointer<DynamicReplacementKey, false> replacedFunctionKey;
RelativeDirectPointer<void, false> replacementFunction;
RelativeDirectPointer<DynamicReplacementChainEntry, false> chainEntry;
uint32_t flags;

public:
/// Enable this replacement by changing the function's replacement chain's
/// root entry.
/// This replacement must be done while holding a global lock that guards this
/// function's chain. Currently this is done by holding the
/// \p DynamicReplacementLock.
void enableReplacement() const;

/// Disable this replacement by changing the function's replacement chain's
/// root entry.
/// This replacement must be done while holding a global lock that guards this
/// function's chain. Currently this is done by holding the
/// \p DynamicReplacementLock.
void disableReplacement() const;

uint32_t getFlags() const { return flags; }
};

/// A collection of dynamic replacement records.
class DynamicReplacementScope
: private swift::ABI::TrailingObjects<DynamicReplacementScope,
DynamicReplacementDescriptor> {

uint32_t flags;
uint32_t numReplacements;

using TrailingObjects =
swift::ABI::TrailingObjects<DynamicReplacementScope,
DynamicReplacementDescriptor>;
friend TrailingObjects;

ArrayRef<DynamicReplacementDescriptor> getReplacementDescriptors() const {
return {this->template getTrailingObjects<DynamicReplacementDescriptor>(),
numReplacements};
}

public:
void enable() const {
for (auto &descriptor : getReplacementDescriptors()) {
descriptor.enableReplacement();
}
}

void disable() const {
for (auto &descriptor : getReplacementDescriptors()) {
descriptor.disableReplacement();
}
}
uint32_t getFlags() { return flags; }
};

} // end namespace swift

#pragma clang diagnostic pop
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ SIMPLE_DECL_ATTR(_nonoverride, NonOverride,
OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | OnAssociatedType |
UserInaccessible | NotSerialized,
79)
DECL_ATTR(_dynamicReplacement, DynamicReplacement,
OnAbstractFunction | OnVar | OnSubscript | UserInaccessible,
80)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
Expand Down
78 changes: 78 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class ASTPrinter;
class ASTContext;
struct PrintOptions;
class Decl;
class AbstractFunctionDecl;
class FuncDecl;
class ClassDecl;
class GenericFunctionType;
class LazyConformanceLoader;
Expand Down Expand Up @@ -205,6 +207,12 @@ class DeclAttribute : public AttributeBase {
Swift3Inferred : 1
);

SWIFT_INLINE_BITFIELD(DynamicReplacementAttr, DeclAttribute, 1,
/// Whether this attribute has location information that trails the main
/// record, which contains the locations of the parentheses and any names.
HasTrailingLocationInfo : 1
);

SWIFT_INLINE_BITFIELD(AbstractAccessControlAttr, DeclAttribute, 3,
AccessLevel : 3
);
Expand Down Expand Up @@ -893,6 +901,76 @@ class ObjCAttr final : public DeclAttribute,
}
};

/// The @_dynamicReplacement(for:) attribute.
class DynamicReplacementAttr final
: public DeclAttribute,
private llvm::TrailingObjects<DynamicReplacementAttr, SourceLoc> {
friend TrailingObjects;

DeclName ReplacedFunctionName;
AbstractFunctionDecl *ReplacedFunction;

/// Create an @_dynamicReplacement(for:) attribute written in the source.
DynamicReplacementAttr(SourceLoc atLoc, SourceRange baseRange,
DeclName replacedFunctionName, SourceRange parenRange);

explicit DynamicReplacementAttr(DeclName name)
: DeclAttribute(DAK_DynamicReplacement, SourceLoc(), SourceRange(),
/*Implicit=*/false),
ReplacedFunctionName(name), ReplacedFunction(nullptr) {
Bits.DynamicReplacementAttr.HasTrailingLocationInfo = false;
}

/// Retrieve the trailing location information.
MutableArrayRef<SourceLoc> getTrailingLocations() {
assert(Bits.DynamicReplacementAttr.HasTrailingLocationInfo);
unsigned length = 2;
return {getTrailingObjects<SourceLoc>(), length};
}

/// Retrieve the trailing location information.
ArrayRef<SourceLoc> getTrailingLocations() const {
assert(Bits.DynamicReplacementAttr.HasTrailingLocationInfo);
unsigned length = 2; // lParens, rParens
return {getTrailingObjects<SourceLoc>(), length};
}

public:
static DynamicReplacementAttr *
create(ASTContext &Context, SourceLoc AtLoc, SourceLoc DynReplLoc,
SourceLoc LParenLoc, DeclName replacedFunction, SourceLoc RParenLoc);

static DynamicReplacementAttr *create(ASTContext &ctx,
DeclName replacedFunction);

static DynamicReplacementAttr *create(ASTContext &ctx,
DeclName replacedFunction,
AbstractFunctionDecl *replacedFuncDecl);

DeclName getReplacedFunctionName() const {
return ReplacedFunctionName;
}

AbstractFunctionDecl *getReplacedFunction() const {
return ReplacedFunction;
}

void setReplacedFunction(AbstractFunctionDecl *f) {
assert(ReplacedFunction == nullptr);
ReplacedFunction = f;
}

/// Retrieve the location of the opening parentheses, if there is one.
SourceLoc getLParenLoc() const;

/// Retrieve the location of the closing parentheses, if there is one.
SourceLoc getRParenLoc() const;

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_DynamicReplacement;
}
};

/// Represents any sort of access control modifier.
class AbstractAccessControlAttr : public DeclAttribute {
protected:
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2590,6 +2590,10 @@ class ValueDecl : public Decl {
/// Is this declaration marked with 'dynamic'?
bool isDynamic() const;

bool isObjCDynamic() const {
return isObjC() && isDynamic();
}

/// Set whether this type is 'dynamic' or not.
void setIsDynamic(bool value);

Expand Down
11 changes: 11 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,8 @@ ERROR(expected_sil_rbrace,none,
"expected '}' at the end of a sil body", ())
ERROR(expected_sil_function_type, none,
"sil function expected to have SIL function type", ())
ERROR(sil_dynamically_replaced_func_not_found,none,
"dynamically replaced function not found %0", (Identifier))

// SIL Stage
ERROR(expected_sil_stage_name, none,
Expand Down Expand Up @@ -1406,6 +1408,15 @@ ERROR(attr_nskeyedarchiverencodenongenericsubclassesonly_removed, none,
"@NSKeyedArchiverEncodeNonGenericSubclassesOnly is no longer necessary",
())

ERROR(attr_dynamic_replacement_expected_rparen,none,
"expected ')' after function name for @_dynamicReplacement", ())
ERROR(attr_dynamic_replacement_expected_function,none,
"expected a function name in @_dynamicReplacement(for:)", ())
ERROR(attr_dynamic_replacement_expected_for,none,
"expected 'for' in '_dynamicReplacement' attribute", ())
ERROR(attr_dynamic_replacement_expected_colon,none,
"expected ':' after @_dynamicReplacement(for", ())

// opened
ERROR(opened_attribute_expected_lparen,none,
"expected '(' after 'opened' attribute", ())
Expand Down
27 changes: 27 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3878,10 +3878,37 @@ ERROR(dynamic_with_final,none,
ERROR(dynamic_with_nonobjc,none,
"a declaration cannot be both '@nonobjc' and 'dynamic'",
())
ERROR(dynamic_with_transparent,none,
"a declaration cannot be both '@_tranparent' and 'dynamic'",
())
ERROR(dynamic_requires_objc,none,
"'dynamic' %0 %1 must also be '@objc'",
(DescriptiveDeclKind, DeclName))

//------------------------------------------------------------------------------
// MARK: @_dynamicReplacement(for:)
//------------------------------------------------------------------------------

ERROR(dynamic_replacement_accessor_type_mismatch, none,
"replaced accessor %0's type does not match", (DeclName))
ERROR(dynamic_replacement_accessor_not_dynamic, none,
"replaced accessor for %0 is not marked dynamic", (DeclName))
ERROR(dynamic_replacement_accessor_not_explicit, none,
"replaced accessor %select{get|set|_read|_modify|willSet|didSet|unsafeAddress|addressWithOwner|addressWithNativeOwner|unsafeMutableAddress|mutableAddressWithOwner|}0 for %1 is not explicitly defined",
(unsigned, DeclName))
ERROR(dynamic_replacement_function_not_dynamic, none,
"replaced function %0 is not marked dynamic", (DeclName))
ERROR(dynamic_replacement_function_not_found, none,
"replaced function %0 could not be found", (DeclName))
ERROR(dynamic_replacement_accessor_not_found, none,
"replaced accessor for %0 could not be found", (DeclName))
ERROR(dynamic_replacement_function_of_type_not_found, none,
"replaced function %0 of type %1 could not be found", (DeclName, Type))
NOTE(dynamic_replacement_found_function_of_type, none,
"found function %0 of type %1", (DeclName, Type))
ERROR(dynamic_replacement_not_in_extension, none,
"dynamicReplacement(for:) of %0 is not defined in an extension or at the file level", (DeclName))

//------------------------------------------------------------------------------
// MARK: @available
//------------------------------------------------------------------------------
Expand Down
6 changes: 5 additions & 1 deletion include/swift/AST/StorageImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,12 @@ class AccessStrategy {

Kind getKind() const { return TheKind; }

bool hasAccessor() const {
return TheKind == DirectToAccessor || TheKind == DispatchToAccessor;
}

AccessorKind getAccessor() const {
assert(TheKind == DirectToAccessor || TheKind == DispatchToAccessor);
assert(hasAccessor());
return FirstAccessor;
}

Expand Down
4 changes: 4 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ namespace swift {
/// was not compiled with -enable-testing.
bool EnableTestableAttrRequiresTestableModule = true;

/// If true, the 'dynamic' attribute is added to all applicable
/// declarations.
bool EnableImplicitDynamic = false;

///
/// Flags for developers
///
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ NODE(Directness)
NODE(DynamicAttribute)
NODE(DirectMethodReferenceAttribute)
NODE(DynamicSelf)
NODE(DynamicallyReplaceableFunctionImpl)
NODE(DynamicallyReplaceableFunctionKey)
NODE(DynamicallyReplaceableFunctionVar)
CONTEXT_NODE(Enum)
NODE(EnumCase)
NODE(ErrorType)
Expand Down
Loading