mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-04 18:40:57 +03:00
5f29f11a4d
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.
34 lines
1.4 KiB
Rust
34 lines
1.4 KiB
Rust
//! Profiling markers for compiler instrumentation.
|
|
|
|
/// Profiling marker for move operations.
|
|
///
|
|
/// This function is never called at runtime. When `-Z annotate-moves` is enabled,
|
|
/// the compiler creates synthetic debug info that makes move operations appear as
|
|
/// calls to this function in profilers.
|
|
///
|
|
/// The `SIZE` parameter encodes the size of the type being copied. It's the same as
|
|
/// `size_of::<T>()`, and is only present for convenience.
|
|
#[unstable(feature = "profiling_marker_api", issue = "148197")]
|
|
#[lang = "compiler_move"]
|
|
pub fn compiler_move<T, const SIZE: usize>(_src: *const T, _dst: *mut T) {
|
|
unreachable!(
|
|
"compiler_move marks where the compiler-generated a memcpy for moves. It is never actually called."
|
|
)
|
|
}
|
|
|
|
/// Profiling marker for copy operations.
|
|
///
|
|
/// This function is never called at runtime. When `-Z annotate-moves` is enabled,
|
|
/// the compiler creates synthetic debug info that makes copy operations appear as
|
|
/// calls to this function in profilers.
|
|
///
|
|
/// The `SIZE` parameter encodes the size of the type being copied. It's the same as
|
|
/// `size_of::<T>()`, and is only present for convenience.
|
|
#[unstable(feature = "profiling_marker_api", issue = "148197")]
|
|
#[lang = "compiler_copy"]
|
|
pub fn compiler_copy<T, const SIZE: usize>(_src: *const T, _dst: *mut T) {
|
|
unreachable!(
|
|
"compiler_copy marks where the compiler-generated a memcpy for Copies. It is never actually called."
|
|
)
|
|
}
|