Skip to content

Conversation

jsgf
Copy link
Contributor

@jsgf jsgf commented Oct 17, 2025

Note: this is an alternative implementation of #147206; rather than being a MIR transform, it adds the annotations closer to codegen. It's functionally the same but the implementation is lower impact and it could be more correct.


This implements a new unstable compiler flag -Zannotate-moves that makes move and copy operations visible in profilers by creating synthetic debug information. This is achieved with zero runtime cost by manipulating debug info scopes to make moves/copies appear as calls to compiler_move<T, SIZE> and compiler_copy<T, SIZE> marker functions in profiling tools.

This allows developers to identify expensive move/copy operations in their code using standard profiling tools, without requiring specialized tooling or runtime instrumentation.

The implementation works at codegen time. When processing MIR operands (Operand::Move and Operand::Copy), the codegen creates an OperandRef with an optional move_annotation field containing an Instance of the appropriate profiling marker function. When storing the operand, store_with_annotation() wraps the store operation in a synthetic debug scope that makes it appear inlined from the marker.

Two marker functions (compiler_move and compiler_copy) are defined in library/core/src/profiling.rs. These are never actually called - they exist solely as debug info anchors.

Operations are only annotated if the type:

  • Meets the size threshold (default: 65 bytes, configurable via -Zannotate-moves=SIZE)
  • Has a non-scalar backend representation (scalars use registers, not memcpy)

This has a very small size impact on object file size. With the default limit it's well under 0.1%, and even with a very small limit of 8 bytes it's still ~1.5%. This could be enabled by default.

@rustbot
Copy link
Collaborator

rustbot commented Oct 17, 2025

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

Some changes occurred in compiler/rustc_codegen_gcc

cc @antoyo, @GuillaumeGomez

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Oct 17, 2025
@rustbot
Copy link
Collaborator

rustbot commented Oct 17, 2025

r? @madsmtm

rustbot has assigned @madsmtm.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@jsgf jsgf changed the title Add -Zannotate-moves for profiler visibility of move/copy operations Add -Zannotate-moves for profiler visibility of move/copy operations (codegen) Oct 17, 2025
This implements a new unstable compiler flag `-Zannotate-moves` that makes
move and copy operations visible in profilers by creating synthetic debug
information. This is achieved with zero runtime cost by manipulating debug
info scopes to make moves/copies appear as calls to `compiler_move<T, SIZE>`
and `compiler_copy<T, SIZE>` marker functions in profiling tools.

This allows developers to identify expensive move/copy operations in their
code using standard profiling tools, without requiring specialized tooling
or runtime instrumentation.

The implementation works at codegen time. When processing MIR operands
(`Operand::Move` and `Operand::Copy`), the codegen creates an `OperandRef`
with an optional `move_annotation` field containing an `Instance` of the
appropriate profiling marker function. When storing the operand,
`store_with_annotation()` wraps the store operation in a synthetic debug
scope that makes it appear inlined from the marker.

Two marker functions (`compiler_move` and `compiler_copy`) are defined
in `library/core/src/profiling.rs`. These are never actually called -
they exist solely as debug info anchors.

Operations are only annotated if the type:
   - Meets the size threshold (default: 65 bytes, configurable via
     `-Zannotate-moves=SIZE`)
   - Has a non-scalar backend representation (scalars use registers,
     not memcpy)

This has a very small size impact on object file size. With the default
limit it's well under 0.1%, and even with a very small limit of 8 bytes
it's still ~1.5%. This could be enabled by default.
@jsgf
Copy link
Contributor Author

jsgf commented Oct 17, 2025

MCP filed rust-lang/compiler-team#928

@saethlin
Copy link
Member

Does this support cranelift? The advantage to doing such things in MIR is that it is much easier to support cranelift.

@jsgf
Copy link
Contributor Author

jsgf commented Oct 18, 2025

Yeah, that's a good point which I mentioned in the MCP. I had assumed that cranelift and gcc used the same framework as llvm so supporting them would just be a matter of implementing with_move_annotation for them. But I guess that's not the case?

I didn't check whether the MIR transform approach did actually work for cranelift or gcc (does gcc support debuginfo in general?). But if it does, then I wonder if there's a middle ground between MIR and codegen to get the best of both?

@saethlin
Copy link
Member

Ah, I'll respond on the MCP Zulip thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants