Skip to content

[SIL][PackageCMO] Allow optimizing [serialized_for_pkg] functions #73902

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 4, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ private func transitivelyErase(load: LoadInst, _ context: SimplifyContext) {

private extension Value {
func canBeCopied(into function: Function, _ context: SimplifyContext) -> Bool {
if !function.isSerialized {
if !function.isAnySerialized {
return true
}

Expand All @@ -297,7 +297,7 @@ private extension Value {

while let value = worklist.pop() {
if let fri = value as? FunctionRefInst {
if !fri.referencedFunction.hasValidLinkageForFragileRef {
if !fri.referencedFunction.hasValidLinkageForFragileRef(function.serializedKind) {
return false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -655,9 +655,8 @@ extension FullApplySite {
return false
}
// Cannot inline a non-inlinable function it an inlinable function.
if parentFunction.isSerialized,
let calleeFunction = referencedFunction,
!calleeFunction.isSerialized {
if let calleeFunction = referencedFunction,
!calleeFunction.canBeInlinedIntoCaller(parentFunction.serializedKind) {
return false
}

Expand Down
32 changes: 31 additions & 1 deletion SwiftCompilerSources/Sources/SIL/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,37 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
}
public var isSerialized: Bool { bridged.isSerialized() }

public var hasValidLinkageForFragileRef: Bool { bridged.hasValidLinkageForFragileRef() }
public var isAnySerialized: Bool { bridged.isAnySerialized() }

public enum SerializedKind {
case notSerialized, serialized, serializedForPackage
}

public var serializedKind: SerializedKind {
switch bridged.getSerializedKind() {
case .IsNotSerialized: return .notSerialized
case .IsSerialized: return .serialized
case .IsSerializedForPackage: return .serializedForPackage
default: fatalError()
}
}

private func serializedKindBridged(_ arg: SerializedKind) -> BridgedFunction.SerializedKind {
switch arg {
case .notSerialized: return .IsNotSerialized
case .serialized: return .IsSerialized
case .serializedForPackage: return .IsSerializedForPackage
default: fatalError()
}
}

public func canBeInlinedIntoCaller(_ kind: SerializedKind) -> Bool {
bridged.canBeInlinedIntoCaller(serializedKindBridged(kind))
}

public func hasValidLinkageForFragileRef(_ kind: SerializedKind) -> Bool {
bridged.hasValidLinkageForFragileRef(serializedKindBridged(kind))
}

public enum ThunkKind {
case noThunk, thunk, reabstractionThunk, signatureOptimizedThunk
Expand Down
11 changes: 10 additions & 1 deletion include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,12 @@ struct BridgedFunction {
IsSignatureOptimizedThunk
};

enum class SerializedKind {
IsNotSerialized,
IsSerialized,
IsSerializedForPackage
};

enum class Linkage {
Public,
PublicNonABI,
Expand Down Expand Up @@ -621,7 +627,10 @@ struct BridgedFunction {
BRIDGED_INLINE PerformanceConstraints getPerformanceConstraints() const;
BRIDGED_INLINE InlineStrategy getInlineStrategy() const;
BRIDGED_INLINE bool isSerialized() const;
BRIDGED_INLINE bool hasValidLinkageForFragileRef() const;
BRIDGED_INLINE bool isAnySerialized() const;
BRIDGED_INLINE SerializedKind getSerializedKind() const;
BRIDGED_INLINE bool canBeInlinedIntoCaller(SerializedKind) const;
BRIDGED_INLINE bool hasValidLinkageForFragileRef(SerializedKind) const;
BRIDGED_INLINE ThunkKind isThunk() const;
BRIDGED_INLINE void setThunk(ThunkKind) const;
BRIDGED_INLINE bool needsStackProtection() const;
Expand Down
16 changes: 14 additions & 2 deletions include/swift/SIL/SILBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,20 @@ bool BridgedFunction::isSerialized() const {
return getFunction()->isSerialized();
}

bool BridgedFunction::hasValidLinkageForFragileRef() const {
return getFunction()->hasValidLinkageForFragileRef();
bool BridgedFunction::isAnySerialized() const {
return getFunction()->isAnySerialized();
}

BridgedFunction::SerializedKind BridgedFunction::getSerializedKind() const {
return (SerializedKind)getFunction()->getSerializedKind();
}

bool BridgedFunction::canBeInlinedIntoCaller(SerializedKind kind) const {
return getFunction()->canBeInlinedIntoCaller(swift::SerializedKind_t(kind));
}

bool BridgedFunction::hasValidLinkageForFragileRef(SerializedKind kind) const {
return getFunction()->hasValidLinkageForFragileRef(swift::SerializedKind_t(kind));
}

bool BridgedFunction::needsStackProtection() const {
Expand Down
24 changes: 6 additions & 18 deletions include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -874,10 +874,8 @@ class SILFunction
void setLinkage(SILLinkage linkage) { Linkage = unsigned(linkage); }

/// Checks if this (callee) function body can be inlined into the caller
/// by comparing their SerializedKind_t values.
/// by comparing their `SerializedKind_t` values.
///
/// If the \p assumeFragileCaller is true, the caller must be serialized,
/// in which case the callee needs to be serialized also to be inlined.
/// If both callee and caller are `not_serialized`, the callee can be inlined
/// into the caller during SIL inlining passes even if it (and the caller)
/// might contain private symbols. If this callee is `serialized_for_pkg`,
Expand All @@ -894,21 +892,13 @@ class SILFunction
/// ```
///
/// \p callerSerializedKind The caller's SerializedKind.
/// \p assumeFragileCaller True if the call site of this function already
/// knows that the caller is serialized.
bool canBeInlinedIntoCaller(
std::optional<SerializedKind_t> callerSerializedKind = std::nullopt,
bool assumeFragileCaller = true) const;
bool canBeInlinedIntoCaller(SerializedKind_t callerSerializedKind) const;

/// Returns true if this function can be referenced from a fragile function
/// body.
/// \p callerSerializedKind The caller's SerializedKind. Used to be passed to
/// \c canBeInlinedIntoCaller.
/// \p assumeFragileCaller Default to true since this function must be called
// if the caller is [serialized].
bool hasValidLinkageForFragileRef(
std::optional<SerializedKind_t> callerSerializedKind = std::nullopt,
bool assumeFragileCaller = true) const;
bool hasValidLinkageForFragileRef(SerializedKind_t callerSerializedKind) const;

/// Get's the effective linkage which is used to derive the llvm linkage.
/// Usually this is the same as getLinkage(), except in one case: if this
Expand Down Expand Up @@ -1165,11 +1155,9 @@ class SILFunction
bool isSerialized() const {
return SerializedKind_t(SerializedKind) == IsSerialized;
}
bool isSerializedForPackage() const {
return SerializedKind_t(SerializedKind) == IsSerializedForPackage;
}
bool isNotSerialized() const {
return SerializedKind_t(SerializedKind) == IsNotSerialized;
bool isAnySerialized() const {
return SerializedKind_t(SerializedKind) == IsSerialized ||
SerializedKind_t(SerializedKind) == IsSerializedForPackage;
}

/// Get this function's serialized attribute.
Expand Down
9 changes: 4 additions & 5 deletions include/swift/SIL/SILGlobalVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,10 @@ class SILGlobalVariable
/// Check if this global variable is [serialized]. This does not check
/// if it's [serialized_for_package].
bool isSerialized() const;
/// Check if this global variable is [serialized_for_package].
bool isSerializedForPackage() const;
/// Checks whether this global var is neither [serialized] nor
/// [serialized_for_package].
bool isNotSerialized() const;

/// Check if this global variable is [serialized] or [serialized_for_package].
bool isAnySerialized() const;

/// Get this global variable's serialized attribute.
SerializedKind_t getSerializedKind() const;
void setSerializedKind(SerializedKind_t isSerialized);
Expand Down
5 changes: 3 additions & 2 deletions include/swift/SIL/SILMoveOnlyDeinit.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ class SILMoveOnlyDeinit final : public SILAllocated<SILMoveOnlyDeinit> {
return funcImpl;
}

bool isNotSerialized() const {
return SerializedKind_t(serialized) == IsNotSerialized;
bool isAnySerialized() const {
return SerializedKind_t(serialized) == IsSerialized ||
SerializedKind_t(serialized) == IsSerializedForPackage;
}
SerializedKind_t getSerializedKind() const {
return SerializedKind_t(serialized);
Expand Down
5 changes: 4 additions & 1 deletion include/swift/SIL/SILProperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ class SILProperty : public llvm::ilist_node<SILProperty>,
AbstractStorageDecl *Decl,
std::optional<KeyPathPatternComponent> Component);

bool isNotSerialized() const { return SerializedKind_t(Serialized) == IsNotSerialized; }
bool isAnySerialized() const {
return SerializedKind_t(Serialized) == IsSerialized ||
SerializedKind_t(Serialized) == IsSerializedForPackage;
}
SerializedKind_t getSerializedKind() const {
return SerializedKind_t(Serialized);
}
Expand Down
9 changes: 4 additions & 5 deletions include/swift/SIL/SILVTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,10 @@ class SILVTable final : public SILAllocated<SILVTable>,
bool isSerialized() const {
return SerializedKind_t(SerializedKind) == IsSerialized;
}
bool isSerializedForPackage() const {
return SerializedKind_t(SerializedKind) == IsSerializedForPackage;
}
bool isNotSerialized() const {
return SerializedKind_t(SerializedKind) == IsNotSerialized;

bool isAnySerialized() const {
return SerializedKind_t(SerializedKind) == IsSerialized ||
SerializedKind_t(SerializedKind) == IsSerializedForPackage;
}

SerializedKind_t getSerializedKind() const {
Expand Down
10 changes: 5 additions & 5 deletions include/swift/SIL/SILWitnessTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,12 @@ class SILWitnessTable : public llvm::ilist_node<SILWitnessTable>,
bool isSerialized() const {
return SerializedKind_t(SerializedKind) == IsSerialized;
}
bool isSerializedForPackage() const {
return SerializedKind_t(SerializedKind) == IsSerializedForPackage;
}
bool isNotSerialized() const {
return SerializedKind_t(SerializedKind) == IsNotSerialized;

bool isAnySerialized() const {
return SerializedKind_t(SerializedKind) == IsSerialized ||
SerializedKind_t(SerializedKind) == IsSerializedForPackage;
}

SerializedKind_t getSerializedKind() const {
return SerializedKind_t(SerializedKind);
}
Expand Down
6 changes: 3 additions & 3 deletions lib/SIL/IR/Linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void SILLinkerVisitor::deserializeAndPushToWorklist(SILFunction *F) {
return;
}

assert(F->isNotSerialized() == Mod.isSerialized() &&
assert(!F->isAnySerialized() == Mod.isSerialized() &&
"the de-serializer did set the wrong serialized flag");

F->setBare(IsBare);
Expand Down Expand Up @@ -186,8 +186,8 @@ void SILLinkerVisitor::linkInVTable(ClassDecl *D) {
// for processing.
for (auto &entry : Vtbl->getEntries()) {
SILFunction *impl = entry.getImplementation();
if (!Vtbl->isSerialized() ||
impl->hasValidLinkageForFragileRef()) {
if (!Vtbl->isAnySerialized() ||
impl->hasValidLinkageForFragileRef(Vtbl->getSerializedKind())) {
// Deserialize and recursively walk any vtable entries that do not have
// bodies yet.
maybeAddFunctionToWorklist(impl,
Expand Down
1 change: 0 additions & 1 deletion lib/SIL/IR/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,6 @@ SerializedKind_t SILDeclRef::getSerializedKind() const {
}

// Anything else that is not public is not serializable.
// pcmo TODO: should check if package-cmo is enabled?
if (d->getEffectiveAccess() < AccessLevel::Public)
return IsNotSerialized;

Expand Down
28 changes: 9 additions & 19 deletions lib/SIL/IR/SILFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,8 +920,6 @@ bool SILFunction::hasName(const char *Name) const {
Checks if this (callee) function body can be inlined into the caller
by comparing their SerializedKind_t values.

If the \p assumeFragileCaller is true, the caller must be serialized,
in which case the callee needs to be serialized also to be inlined.
If both callee and caller are not_serialized, the callee can be inlined
into the caller during SIL inlining passes even if it (and the caller)
might contain private symbols. If this callee is serialized_for_pkg, it
Expand All @@ -934,22 +932,13 @@ Callee serialized_for_pkg | ok | ok | no
serialized | ok | ok | ok

*/
bool SILFunction::canBeInlinedIntoCaller(
std::optional<SerializedKind_t> callerSerializedKind,
bool assumeFragileCaller) const {
// If the \p assumeFragileCaller is true, the caller must
// be serialized, so return true only if the callee is also
// serialized.
if (assumeFragileCaller)
return isSerialized();

bool SILFunction::canBeInlinedIntoCaller(SerializedKind_t callerSerializedKind) const {
switch (getSerializedKind()) {
// If both callee and caller are not_serialized, the callee
// can be inlined into the caller during SIL inlining passes
// even if it (and the caller) might contain private symbols.
case IsNotSerialized:
return callerSerializedKind.has_value() &&
callerSerializedKind.value() == IsNotSerialized;
return callerSerializedKind == IsNotSerialized;

// If Package-CMO is enabled, we serialize package, public,
// and @usableFromInline decls as [serialized_for_package].
Expand All @@ -962,8 +951,7 @@ bool SILFunction::canBeInlinedIntoCaller(
// for this callee's body to be inlined into the caller.
// It can however be referenced by [serialized] caller.
case IsSerializedForPackage:
return callerSerializedKind.has_value() &&
callerSerializedKind.value() != IsSerialized;
return callerSerializedKind != IsSerialized;
case IsSerialized:
return true;
}
Expand All @@ -972,16 +960,18 @@ bool SILFunction::canBeInlinedIntoCaller(

/// Returns true if this function can be referenced from a fragile function
/// body.
bool SILFunction::hasValidLinkageForFragileRef(
std::optional<SerializedKind_t> callerSerializedKind,
bool assumeFragileCaller) const {
bool SILFunction::hasValidLinkageForFragileRef(SerializedKind_t callerSerializedKind) const {
// Fragile functions can reference 'static inline' functions imported
// from C.
if (hasForeignBody())
return true;

// The call site of this function must have checked that
// caller.isAnySerialized() is true, as indicated by the
// function name itself (contains 'ForFragileRef').
assert(callerSerializedKind != IsNotSerialized);
// If we can inline it, we can reference it.
if (canBeInlinedIntoCaller(callerSerializedKind, assumeFragileCaller))
if (canBeInlinedIntoCaller(callerSerializedKind))
return true;

// If the containing module has been serialized already, we no longer
Expand Down
9 changes: 4 additions & 5 deletions lib/SIL/IR/SILGlobalVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,10 @@ bool SILGlobalVariable::shouldBePreservedForDebugger() const {
bool SILGlobalVariable::isSerialized() const {
return SerializedKind_t(Serialized) == IsSerialized;
}
bool SILGlobalVariable::isSerializedForPackage() const {
return SerializedKind_t(Serialized) == IsSerializedForPackage;
}
bool SILGlobalVariable::isNotSerialized() const {
return SerializedKind_t(Serialized) == IsNotSerialized;

bool SILGlobalVariable::isAnySerialized() const {
return SerializedKind_t(Serialized) == IsSerialized ||
SerializedKind_t(Serialized) == IsSerializedForPackage;
}

/// Get this global variable's fragile attribute.
Expand Down
Loading