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

[EDS] Emit resilient conditional runtime records in Swift #73060

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

NuriAmari
Copy link
Collaborator

@NuriAmari NuriAmari commented Apr 16, 2024

Conditional runtime records is an existing frontend flag that prompts the Swift frontend to emit metadata that is designed to communicate to dead stripping optimization passes when certain runtime records are eligible for stripping, based on the liveness of other symbols.

This metadata takes the following form in LLVM IR, communicating the target global may be stripped if either dep_1, or dep_2 is dead:

!1 = !{ptr @target, i32 1, !{ptr @dep_1, ptr @dep_2}}

The issue with this metadata is that it is rather brittle. Globals referenced in this metadata can be deleted, replacing references to the globals in metadata with nullptr values. A nullptr value does not necessarily indicate a dead dependency.

How the liveness of a dependency is to be interpretted, depends on the linkage of the dependency, and if it is declared or defined within the module.

With just a nullptr value, one cannot identify which case we are in, and thus must conservatively treat such values as live dependencies.

One solution to this problem is to audit the optimization pipeline for passes that invalidate the metadata in this way, and prevent the invalidation. This doesn't scale however, and can break at any time, as metadata isn't guaranteed to be maintained by LLVM passes.

Instead, we emit real LLVM globals, similar to llvm.used. They are stored in llvm.compiler.used, and so will be preserved until the linker. At this point they will be automatically dropped by any linker using dead stripping.

Until that point, they will hold their dependencies live, automatically protected from invalidation, as they are data structures like any other. Since we are emitting data, these structures can also be interpretted by the linker, a feature the metadata did not support.

This patch replaces emitting the old metadata, with the new format. I have been referring to each such record as a "conditionally live record", and emitting them into a special section for easy identification.

@@ -640,10 +640,15 @@ emitGlobalList(IRGenModule &IGM, ArrayRef<llvm::WeakTrackingVH> handles,
IGM.addUsedGlobal(var);
}

// Keep classes and categories around for potential ObjC interop.
if (IGM.IRGen.Opts.SafeConditionalRuntimeRecords &&
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

At some point this was found to be unsafe, though I wasn't around when the issue was discovered. I've thought about removing this restriction a few times, but it seems unsafe to me. IIUC objc_classes and objc_categories sections are read by the runtime, and so usages would not be detectable statically.

@@ -1,47 +0,0 @@
// Tests that with -conditional-runtime-records, IRGen marks class, struct,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This test is replaced by the ConditionIRGen test

@NuriAmari
Copy link
Collaborator Author

@swift-ci Please test apple/llvm-project#8588

@NuriAmari
Copy link
Collaborator Author

apple/llvm-project#8588

@swift-ci Please test

Conditional runtime records is an existing frontend flag that prompts
the Swift frontend to emit metadata that is designed to communicate
to dead stripping optimization passes when certain runtime
records are eligible for stripping, based on the liveness of other
symbols.

This metadata takes the following form in LLVM IR, communicating
the target global may be stripped if either dep_1, or dep_2 is dead:

```
!1 = !{ptr @target, i32 1, !{ptr @dep_1, ptr @dep_2}}
```

The issue with this metadata is that it is rather brittle. Globals
referenced in this metadata can be deleted, replacing references
to the globals in metadata with nullptr values. A nullptr value
does not necessarily indicate a dead dependency.

How the liveness of a dependency is to be interpretted, depends
on the linkage of the dependency, and if it is declared or
defined within the module.

With just a nullptr value, one cannot identify which case
we are in, and thus must conservatively treat such values as live
depdendencies.

One solution to this problem is to audit the optimization pipeline
for passes that invalidate the metadata in this way, and prevent
the invalidation. This doesn't scale however, and can break at any time,
as metadata isn't guaranteed to be maintained by LLVM passes.

Instead, we emit real LLVM globals, similar to llvm.used. They are
stored in `llvm.compiler.used`, and so will be preserved until
the linker. At this point they will be automatically dropped by
any linker using dead stripping.

Until that point, they will hold their dependencies live, automatically
protected from invalidation, as they are data structures like any other.
Since we are emitting data, these structures can also be interpretted
by the linker, a feature the metadata did not support.

This patch replaces emitting the old metadata, with the new format. I
have been referring to each such record as a "conditionally live
record", and emitting them into a special section for easy
identification.

This patch also adds a new front-end flags,
-safe-conditional-runtime-records, that emits fewer conditionally
live records, avoiding a case with Objective-C class records that
may be unsafe with Objective-C interop. The existing flag
still emits the same records, just in the new format.
@NuriAmari NuriAmari force-pushed the resilient-conditional-runtime-records branch from d66e91b to 07ce9c2 Compare April 17, 2024 21:27
@NuriAmari
Copy link
Collaborator Author

apple/llvm-project#8588

@swift-ci Please test

@NuriAmari
Copy link
Collaborator Author

@swift-ci Please test source compatibility

@NuriAmari
Copy link
Collaborator Author

@NuriAmari NuriAmari marked this pull request as ready for review April 17, 2024 21:41
@NuriAmari
Copy link
Collaborator Author

apple/llvm-project#8588

@swift-ci Please test

@NuriAmari
Copy link
Collaborator Author

Compat suite failures look unrelated.

Copy link
Member

@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.

This seems like a reasonable approach to me.

@NuriAmari
Copy link
Collaborator Author

Thanks for the review @aschwaighofer , do you mind taking a look at the LLVM side: apple/llvm-project#8588 ?

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.

None yet

2 participants