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

Possible solution to allow MIR inlining with -Zinstrument-coverage #83061

Closed
richkadel opened this issue Mar 12, 2021 · 6 comments · Fixed by #83080
Closed

Possible solution to allow MIR inlining with -Zinstrument-coverage #83061

richkadel opened this issue Mar 12, 2021 · 6 comments · Fixed by #83080
Labels
A-code-coverage Area: Source-based code coverage (-Cinstrument-coverage) A-mir-opt-inlining Area: MIR inlining T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@richkadel
Copy link
Contributor

Today, the MIR Inline pass is skipped if -Zinstrument-coverage is enabled.

I have an idea for a workaround that will allow MIR inlining.

I believe the problem is, when generating the LLVM coverage counters, and related coverage info used to generate the coverage map, each Coverage MIR statement assumes the coverage data should be associated with the current function being codegenned.

See

let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id());

The self.instance is the function instance being generated. But if MIR inlining is performed, the original coverage counters for the inlined function will conflict with coverage counters of the current instance. For example, in both functions, the first counter is 1. But there will be two occurrences of that counter, for different functions in the original source. (That's essentially the problem.)

To fix this, I believe we can just add the original function's def_id to the MIR Coverage statement data.

Everywhere self.instance is used in the codegen_coverage() function should (probably) be replaced with an instance derived from the original def_id. This will apply those counters to the correct FunctionCoverage, and there will be no conflicts.

I may not be able to work on this very soon, and disabling MIR inlining is an OK workaround for now, but I wanted to capture the idea for me or someone else to try in the future.

@richkadel
Copy link
Contributor Author

cc: @jyn514 @wesleywiser @tmandry

@richkadel
Copy link
Contributor Author

One other note I forgot. Since an inlined function can be generated more than once, we need to make sure the coverage data isn't re-added. I suspect this is already checked, but there should be a test for this, once it's implemented.

@jyn514
Copy link
Member

jyn514 commented Mar 12, 2021

I'm not sure why you cc-ed me, I'm not very familiar with either MIR inlining or instrument-coverage (or codegen in general for that matter).

@richkadel
Copy link
Contributor Author

No important reason Josh. Sorry. I know you typically add the coverage label, so that was the only reason

@jyn514 jyn514 added A-code-coverage Area: Source-based code coverage (-Cinstrument-coverage) A-mir-opt-inlining Area: MIR inlining T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 12, 2021
@richkadel
Copy link
Contributor Author

Addendum:

I glossed over some details. One thing I just remembered, which is actually quite helpful, is we will need an actual Instance, not just a MIR def_id, for generics. The Inline pass will get that Instance (in order to inline a function). That means that the def_id for the Instance won't be available until the Inline pass. So there is no need to add a def_id to the Coverage statement when first constructed, but when inlining, the Coverage statement will need to be updated or replaced, adding the inlined function's Instance def_id.

Then in codegen_coverage() will use self.instance if the Coveage statement doesn't have it's own inline_instance.

This allows different parameterized versions of an inlined function to get different coverage results, just like non-inlined generics do now.

@tmiasko
Copy link
Contributor

tmiasko commented Mar 13, 2021

It is possible to derive an inlined instance from a scope information associated with a statement, and use that when creating a counter. Coverage itself could remain as it is now.

The general approach seems to work just fine, although I did only very limited amount of testing (I also relaxed checks for duplicate ids, allowing them as long as code regions & expressions are the same).

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Mar 17, 2021
Make source-based code coverage compatible with MIR inlining

When codegenning code coverage use the instance that coverage data was
originally generated for, to ensure basic level of compatibility with
MIR inlining.

Fixes rust-lang#83061
@bors bors closed this as completed in b688b69 Mar 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-code-coverage Area: Source-based code coverage (-Cinstrument-coverage) A-mir-opt-inlining Area: MIR inlining T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants