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

Allow loading LLVM plugins with both legacy and new pass manager #91125

Merged
merged 5 commits into from
Dec 30, 2021

Conversation

eskarn
Copy link
Contributor

@eskarn eskarn commented Nov 22, 2021

Opening a draft PR to get feedback and start discussion on this feature. There is already a codegen option passes which allow giving a list of LLVM pass names, however we currently can't use a LLVM pass plugin (as described here : https://llvm.org/docs/WritingAnLLVMPass.html), the only available passes are the LLVM built-in ones.

The proposed modification would be to add another codegen option pass-plugins, which can be set with a list of paths to shared library files. These libraries are loaded using the LLVM function PassPlugin::Load, which calls the expected symbol lvmGetPassPluginInfo, and register the pipeline parsing and optimization callbacks.

An example usage with a single plugin and 3 passes would look like this in the .cargo/config:

rustflags = [
    "-C", "pass-plugins=/tmp/libLLVMPassPlugin",
    "-C", "passes=pass1 pass2 pass3",
]

This would give the same functionality as the opt LLVM tool directly integrated in rust build system.

Additionally, we can also not specify the passes option, and use a plugin which inserts passes in the optimization pipeline, as one could do using clang.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @matthewjasper (or someone else) soon.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 22, 2021
@nagisa
Copy link
Member

nagisa commented Nov 22, 2021

How is this different from the flag introduced by #86267?

@eskarn
Copy link
Contributor Author

eskarn commented Nov 23, 2021

How is this different from the flag introduced by #86267?

The main difference is using the new LLVM pass manager registration, which is not possible with the current flag.

The flag llvm-plugins allows loading an arbitrary library, and then if the library happens to use the legacy pass manager registration, then its passes can be registered.

The proposed flag would allow loading only libraries which use the new LLVM pass manager format, which means those exposing the symbol lvmGetPassPluginInfo, which gets loaded by PassPlugin::Load.
There is also verification of the LLVM_PLUGIN_API_VERSION, which is declared during the new PM registration.

@nagisa
Copy link
Member

nagisa commented Nov 23, 2021

Could the already existing flag be made to work for both oldPM and newPM plugins?

@eskarn
Copy link
Contributor Author

eskarn commented Nov 23, 2021

PassPlugin::Load won't load a library without the expected symbol, so we'd need to handle the legacy plugins separately. Keeping only the existing llvm-plugins option, something like this could work:

  • if the option new_llvm_pass_manager is true, then we skip this code entirely :
    for plugin in &sess.opts.debugging_opts.llvm_plugins {
    let path = Path::new(plugin);
    let res = DynamicLibrary::open(path);
    match res {
    Ok(_) => debug!("LLVM plugin loaded succesfully {} ({})", path.display(), plugin),
    Err(e) => bug!("couldn't load plugin: {}", e),
    }
    mem::forget(res);
    }

    Then we just keep the proposed modifications in compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp (only using the existing llvm-plugins option instead).
  • if the option new_llvm_pass_manager is false, we run the code in llvm_util.rs and LLVMRustOptimizeWithNewPassManager won't be called (so we won't try to load the plugins as new PM ones)

The only issue would be when the option isn't specified and the arch is s390x, there is an exception here:

.unwrap_or_else(|| cgcx.target_arch != "s390x" && llvm_util::get_version() >= (13, 0, 0))

For this specific case, the user of the llvm-plugins would have to know the new PM plugins are not usable because of the exception.

So basically, to use the new PM plugins we'd have to actively set new_llvm_pass_manager to true (and not use the s390x arch).

Would this solution seem acceptable ?

@nagisa
Copy link
Member

nagisa commented Nov 23, 2021

Making it conditional on a flag seems reasonable (oldPM is going away shortly anyway), though I was kind of hoping that PassPlugin::Load would handle plugins for either PM in the first place.

@eskarn
Copy link
Contributor Author

eskarn commented Nov 24, 2021

With 0ce67ab389746cd4e135ed15b3c2eb7c32c0d064 and 0ce67ab389746cd4e135ed15b3c2eb7c32c0d064, now we only load new PM plugins from llvm-plugins if new-llvm-pass-manager is set to yes explicitly. Otherwise the behavior is the same as before, loading legacy plugins only.

These changes should address current @bjorn3 and @nagisa feedback.

Ok(_) => debug!("LLVM plugin loaded succesfully {} ({})", path.display(), plugin),
Err(e) => bug!("couldn't load plugin: {}", e),
let use_new_llvm_pm_plugin_register =
sess.opts.debugging_opts.new_llvm_pass_manager.unwrap_or(false);
Copy link
Member

Choose a reason for hiding this comment

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

If new_llvm_pass_manager returns None, the new pass manager is used when the llvm version is at least 13 and the target is not s390x. See

pub(crate) fn should_use_new_llvm_pass_manager(
cgcx: &CodegenContext<LlvmCodegenBackend>,
config: &ModuleConfig,
) -> bool {
// The new pass manager is enabled by default for LLVM >= 13.
// This matches Clang, which also enables it since Clang 13.
// FIXME: There are some perf issues with the new pass manager
// when targeting s390x, so it is temporarily disabled for that
// arch, see https://github.com/rust-lang/rust/issues/89609
config
.new_llvm_pass_manager
.unwrap_or_else(|| cgcx.target_arch != "s390x" && llvm_util::get_version() >= (13, 0, 0))
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes that's what i was mentioning at the end of #91125 (comment).

What i'm proposing is to condition the new PM plugin loading only on the explicit new_llvm_pass_manager=yes and not the actual usage of the new PM later. Since there are exceptions and a default value hardcoded, we can't trust the option to reflect reality.

If we do want to condition on the actual usage of the new PM then i see two potential issues:

  • current users of llvm-plugins when updating to a llvm13 based rust will trigger PassPlugin::Load on their legacy plugins since the new PM is enabled by default. They will need to explicitly set new_llvm_pass_manager=false
  • i'm not sure how to use should_use_new_llvm_pass_manager in llvm_util.rs, maybe just move it to llvm_util.rs to make it more widely available ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bjorn3 do you think the current code is acceptable regarding the explicit usage of new_llvm_pass_manager or i should
propose the alternative described in my last reply ?

Copy link
Member

Choose a reason for hiding this comment

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

Sorry for the late reply. Missed your previous comment.

current users of llvm-plugins when updating to a llvm13 based rust will trigger PassPlugin::Load on their legacy plugins since the new PM is enabled by default. They will need to explicitly set new_llvm_pass_manager=false

At least in that case you will get an error message rather than silently ignoring it, right?

i'm not sure how to use should_use_new_llvm_pass_manager in llvm_util.rs, maybe just move it to llvm_util.rs to make it more widely available ?

Makes sense I think.

do you think the current code is acceptable regarding the explicit usage of new_llvm_pass_manager or i should
propose the alternative described in my last reply ?

I think using should_use_new_llvm_pass_manager is better. I don't strongly object to the current code though.

Copy link
Contributor Author

@eskarn eskarn Dec 17, 2021

Choose a reason for hiding this comment

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

Sorry for the late reply. Missed your previous comment.

No problem, i should have pinged you earlier but i was busy on other tasks anyway.

current users of llvm-plugins when updating to a llvm13 based rust will trigger PassPlugin::Load on their legacy plugins since the new PM is enabled by default. They will need to explicitly set new_llvm_pass_manager=false

At least in that case you will get an error message rather than silently ignoring it, right?

Indeed, in that case cargo build fails with an error and i can see original LLVM error message directly in cargo non verbose logs like this: Plugin entry point not found in '/tmp/libLLVMPlugin.so'. Is this a legacy plugin?

i'm not sure how to use should_use_new_llvm_pass_manager in llvm_util.rs, maybe just move it to llvm_util.rs to make it more widely available ?

Makes sense I think.

Ok let's try it.

do you think the current code is acceptable regarding the explicit usage of new_llvm_pass_manager or i should
propose the alternative described in my last reply ?

I think using should_use_new_llvm_pass_manager is better. I don't strongly object to the current code though.

Ok thank you for your feedback, i'll try doing it this way and see how that goes.

@camelid camelid added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Nov 25, 2021
@bors
Copy link
Contributor

bors commented Dec 12, 2021

☔ The latest upstream changes (presumably #90716) made this pull request unmergeable. Please resolve the merge conflicts.

@eskarn eskarn changed the title Add a codegen option to allow loading LLVM pass plugins Allow loading LLVM plugins with both legacy and new pass manager Dec 17, 2021
@eskarn
Copy link
Contributor Author

eskarn commented Dec 17, 2021

Resolved conflicts with #90716, re-tested with upstream, and updated the PR title to reflect more what is now being proposed.

@nagisa
Copy link
Member

nagisa commented Dec 18, 2021

Please click the “ready for review” button once you think its good to go.

@eskarn eskarn marked this pull request as ready for review December 20, 2021 14:42
@nagisa
Copy link
Member

nagisa commented Dec 27, 2021

@bors r+

@bors
Copy link
Contributor

bors commented Dec 27, 2021

📌 Commit f431df0 has been approved by nagisa

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 27, 2021
@bors
Copy link
Contributor

bors commented Dec 30, 2021

⌛ Testing commit f431df0 with merge 1b3a5f2...

@bors
Copy link
Contributor

bors commented Dec 30, 2021

☀️ Test successful - checks-actions
Approved by: nagisa
Pushing 1b3a5f2 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Dec 30, 2021
@bors bors merged commit 1b3a5f2 into rust-lang:master Dec 30, 2021
@rustbot rustbot added this to the 1.59.0 milestone Dec 30, 2021
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (1b3a5f2): comparison url.

Summary: This benchmark run did not return any relevant changes.

If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf.

@rustbot label: -perf-regression

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants