Skip to content

Support inlinability with [serialized_for_package] #73754

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 1 commit into from
May 24, 2024
Merged

Conversation

elsh
Copy link
Contributor

@elsh elsh commented May 20, 2024

SIL function can be serialized with different kinds: [serialized] or
[serialized_for_package] if Package CMO is enabled. The latter kind
allows a function to be serialized even if it contains loadable types,
if Package CMO is enabled.

The tri-state serialization kind requires validating inlinability
depending on the serialization kinds of callee vs caller; e.g. if the
callee is [serialized_for_package], the caller must be not [serialized].

Package CMO sets [serialized_for_package] to SIL function, global var,
v-table, and witness-table, if inlinable.

Renamed IsSerialized_t as SerializedKind_t and added IsSerializedForPackage.
Updated various call sites with getSerializedKind().
SILFunction::canBeSerializedIntoCaller handles inlinability checks b/t caller and callee.

Resolves rdar://128406520

@elsh elsh requested a review from kavon as a code owner May 20, 2024 18:00
@elsh elsh requested review from slavapestov and aschwaighofer May 20, 2024 18:03
@elsh
Copy link
Contributor Author

elsh commented May 20, 2024

@swift-ci smoke test

@elsh elsh force-pushed the elsh/serialized_kind branch from dd594f8 to 4152f34 Compare May 20, 2024 18:42
@elsh elsh requested a review from nkcsgexi May 20, 2024 18:57
@nkcsgexi
Copy link
Contributor

Thank you! This makes more sense than maintaining two booleans.

@elsh elsh force-pushed the elsh/serialized_kind branch 2 times, most recently from 34a67f4 to cf1a94c Compare May 21, 2024 00:41
@elsh
Copy link
Contributor Author

elsh commented May 21, 2024

@swift-ci smoke test

@elsh elsh force-pushed the elsh/serialized_kind branch from cf1a94c to 70c6446 Compare May 21, 2024 09:41
@elsh
Copy link
Contributor Author

elsh commented May 21, 2024

@swift-ci smoke test

@elsh elsh force-pushed the elsh/serialized_kind branch 2 times, most recently from f2ae567 to d991810 Compare May 22, 2024 00:14
@elsh
Copy link
Contributor Author

elsh commented May 22, 2024

@swift-ci test

@elsh elsh force-pushed the elsh/serialized_kind branch from d991810 to ecafc6a Compare May 22, 2024 07:16
@elsh
Copy link
Contributor Author

elsh commented May 22, 2024

@swift-ci test

1 similar comment
@elsh
Copy link
Contributor Author

elsh commented May 22, 2024

@swift-ci test

@elsh elsh force-pushed the elsh/serialized_kind branch from 27cf40e to cc3ca1c Compare May 22, 2024 11:17
@elsh
Copy link
Contributor Author

elsh commented May 22, 2024

@swift-ci test

1 similar comment
@elsh
Copy link
Contributor Author

elsh commented May 22, 2024

@swift-ci test

@elsh elsh force-pushed the elsh/serialized_kind branch from d09bf98 to 0e90551 Compare May 22, 2024 18:02
@elsh
Copy link
Contributor Author

elsh commented May 22, 2024

@swift-ci test

@elsh elsh force-pushed the elsh/serialized_kind branch from 0e90551 to 9f5d5ee Compare May 23, 2024 01:46
@elsh
Copy link
Contributor Author

elsh commented May 23, 2024

@swift-ci test

@elsh
Copy link
Contributor Author

elsh commented May 23, 2024

@swift-ci test


/// Returns true if this function can be referenced from a fragile function
/// body.
bool hasValidLinkageForFragileRef() const;
bool hasValidLinkageForFragileRef(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove the optional here and always pass the referencing entity's SerializationKind_t.

It can come from the enclosing/referencing abstraction: function, v-table, witness table, property, movedeinit.

*/
bool SILFunction::canBeInlinedIntoCaller(
std::optional<SerializedKind_t> callerSerializedKind,
bool assumeFragileCaller) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the need of the boolean assumeFragileCaller.

If the caller is fragile (i.e serialized) this should be communicated via passing SerializedKind_t::Serialized

That argument should not be optional.

@aschwaighofer
Copy link
Contributor

I think the APIs should be:

bool hasValidLinkageForFragileRef(SerializationKind_t fromContext);
bool canBeInlinedIntoCaller(SerializationKind_t callerSerialization);

hasValidLinkageForFragileRef probably should become:

bool SILFunction::hasValidLinkageForFragileRef(SerializationKind_t fromContext) const {                        
  ....                                                         
                                                                                
  // If we can inline it, we can reference it.
  assert(fromContext != SerializationKind_t::NotSerialized);                                  
  if (canBeInlinedIntoCaller(fromContext))                                        
    return true;  

And we pass down the context from the enclosing entity (I think you are already doing that in some places of your patch). Some examples follow.

SILVerifier.cpp (here the context/ referencing entity is the SILWitnessTable):

void SILWitnessTable::verify(const SILModule &M) const {                        
  if (!verificationEnabled(M))                                                  
    return;                                                                     
                                                                                
  if (isDeclaration())                                                          
    assert(getEntries().empty() &&                                              
           "A witness table declaration should not have any entries.");         
                                                                                
  for (const Entry &E : getEntries())                                           
    if (E.getKind() == SILWitnessTable::WitnessKind::Method) {                  
      SILFunction *F = E.getMethodWitness().Witness;                            
      if (F) {                                                                  
        // If a SILWitnessTable is going to be serialized, it must only         
        // reference public or serializable functions.                          
 +       if (!isNotSerialized()) {                                                   
 +         assert(F->hasValidLinkageForFragileRef(this->getSerializationKind()) &&                           
                 "Fragile witness tables should not reference "                 
                 "less visible functions.");

Devirtualizer.cpp (here the context/referencing entity is the caller function):

+    if (!applySite.getFunction()->isNotSerialized()) {                                
  // function_ref inside fragile function cannot reference a private or       
  // hidden symbol.                                                           
+    if (!f->hasValidLinkageForFragileRef(applySite.getFunction->getSerializationKind()))                                     
    return false;                                                             
} 

[serialized_for_package] if Package CMO is enabled. The latter kind
allows a function to be serialized even if it contains loadable types,
if Package CMO is enabled. Renamed IsSerialized_t as SerializedKind_t.

The tri-state serialization kind requires validating inlinability
depending on the serialization kinds of callee vs caller; e.g. if the
callee is [serialized_for_package], the caller must be _not_ [serialized].
Renamed `hasValidLinkageForFragileInline` as `canBeInlinedIntoCaller`
that takes in its caller's SerializedKind as an argument. Another argument
`assumeFragileCaller` is also added to ensure that the calle sites of
this function know the caller is serialized unless it's called for SIL
inlining optimization passes.

The [serialized_for_package] attribute is allowed for SIL function, global var,
v-table, and witness-table.

Resolves rdar://128406520
@elsh elsh force-pushed the elsh/serialized_kind branch from 36d7fb0 to 5ccc4cd Compare May 23, 2024 22:54
@elsh
Copy link
Contributor Author

elsh commented May 23, 2024

bool SILFunction::hasValidLinkageForFragileRef(SerializationKind_t fromContext) const {
....

// If we can inline it, we can reference it.
assert(fromContext != SerializationKind_t::NotSerialized);
if (canBeInlinedIntoCaller(fromContext))
return true;

Making the arg non-optional requires passing in caller info to hasValidLinkageForFragileRef which is called in several spots and will require more changes. As this PR is already too large, I'd like to address the feedback in a separate PR along with changes to SIL optimization passes that call the function.

@elsh
Copy link
Contributor Author

elsh commented May 23, 2024

@swift-ci test

@@ -384,7 +384,11 @@ struct SILDeclRef {
/// True if the function should be treated as transparent.
bool isTransparent() const;
/// True if the function should have its body serialized.
IsSerialized_t isSerialized() const;
bool isSerialized() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a follow-up PR we should also introduce a predicate: bool isAnySerializedKind().

The places where we now have to write double negation !isNotSerialized() are really hard to read. At least for my senile brains.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already on it in an upcoming PR; I agree it's illegible for me too :)

Copy link
Contributor

@aschwaighofer aschwaighofer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some changes here are a regression in correctness running in package CMO mode until we follow up with a patch that properly passes the serialization kind around in all places where we now pass assumeFragileCaller.

That is fine with me in the interest of smaller changes, with the understanding that we will have to follow-up with those changes.

@@ -554,9 +553,6 @@ ResilienceExpansion SILFunction::getResilienceExpansion() const {
// attribute is preserved if the current module is in
// the same package, thus should be in the same resilience
// domain.
if (isSerializedForPackage() == IsSerializedForPackage)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait I don't understand why these lines were removed.

The resilience expansion inside of a [serialized_for_package] function is Maximal -- we allow loadable types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, in the line below, maximal is returned if [serialized_for_package].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right isSerialized is only [serialized] not [serialized_for_package]

@elsh elsh merged commit 9392a69 into main May 24, 2024
5 checks passed
@elsh elsh deleted the elsh/serialized_kind branch May 24, 2024 17:13
@elsh
Copy link
Contributor Author

elsh commented May 24, 2024

Some changes here are a regression in correctness running in package CMO mode until we follow up with a patch that properly passes the serialization kind around in all places where we now pass assumeFragileCaller.

That is fine with me in the interest of smaller changes, with the understanding that we will have to follow-up with those changes.

The assumeFragileCaller arg was actually passed to keep the existing behavior as much as possible except when necessary to pass a caller's serializedKind to keep the previous state of Package CMO. In the upcoming PR, the arg will be removed and inlining and other relevant passes will take advantage of the new API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants