mirror of
https://github.com/rust-lang/rust.git
synced 2025-12-02 11:37:54 +00:00
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.
113 lines
4.2 KiB
Rust
113 lines
4.2 KiB
Rust
//@ compile-flags: -Z annotate-moves=100 -Copt-level=0 -g
|
|
// Test that custom size limits work correctly
|
|
#![crate_type = "lib"]
|
|
|
|
struct Struct99 {
|
|
data: [u8; 99], // just below custom 100-byte threshold
|
|
}
|
|
|
|
const _: () = { assert!(size_of::<Struct99>() == 99) };
|
|
|
|
impl Clone for Struct99 {
|
|
// CHECK-LABEL: <size_limit::Struct99 as core::clone::Clone>::clone
|
|
fn clone(&self) -> Self {
|
|
// Should NOT be annotated since 99 < 100
|
|
// CHECK: call void @llvm.memcpy{{.*}}, !dbg ![[#SZ99_COPY_LOC:]]
|
|
Struct99 { data: self.data }
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: size_limit::test_99_copy
|
|
pub fn test_99_copy() {
|
|
let sz99 = Struct99 { data: [42; 99] };
|
|
let _copy = sz99.clone();
|
|
}
|
|
|
|
// CHECK-LABEL: size_limit::test_99_move
|
|
pub fn test_99_move() {
|
|
let sz99 = Struct99 { data: [42; 99] };
|
|
// Should NOT be annotated
|
|
// CHECK-NOT: compiler_move
|
|
let _moved = sz99;
|
|
}
|
|
|
|
struct Struct100 {
|
|
data: [u8; 100], // 100 bytes - equal to custom 100-byte threshold
|
|
}
|
|
|
|
const _: () = { assert!(size_of::<Struct100>() == 100) };
|
|
|
|
impl Clone for Struct100 {
|
|
// CHECK-LABEL: <size_limit::Struct100 as core::clone::Clone>::clone
|
|
fn clone(&self) -> Self {
|
|
// CHECK: call void @llvm.memcpy{{.*}}, !dbg ![[#SZ100_COPY_LOC:]]
|
|
// CHECK: call void @llvm.memcpy{{.*}}, !dbg ![[#SZ100_RETURN_LOC:]]
|
|
Struct100 { data: self.data }
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: size_limit::test_100_copy
|
|
pub fn test_100_copy() {
|
|
let sz100 = Struct100 { data: [42; 100] };
|
|
let _copy = sz100.clone();
|
|
}
|
|
|
|
// CHECK-LABEL: size_limit::test_100_move
|
|
pub fn test_100_move() {
|
|
let sz100 = Struct100 { data: [42; 100] };
|
|
// CHECK: call void @llvm.memcpy{{.*}}, !dbg ![[#SZ100_MOVE_LOC:]]
|
|
let _moved = sz100;
|
|
}
|
|
|
|
struct Struct101 {
|
|
data: [u8; 101], // 101 bytes - above custom 100-byte threshold
|
|
}
|
|
|
|
const _: () = { assert!(size_of::<Struct101>() == 101) };
|
|
|
|
impl Clone for Struct101 {
|
|
// CHECK-LABEL: <size_limit::Struct101 as core::clone::Clone>::clone
|
|
fn clone(&self) -> Self {
|
|
// CHECK: call void @llvm.memcpy{{.*}}, !dbg ![[#SZ101_COPY_LOC:]]
|
|
// CHECK: call void @llvm.memcpy{{.*}}, !dbg ![[#SZ101_RETURN_LOC:]]
|
|
Struct101 { data: self.data }
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: size_limit::test_101_copy
|
|
pub fn test_101_copy() {
|
|
let sz101 = Struct101 { data: [42; 101] };
|
|
let _copy = sz101.clone();
|
|
}
|
|
|
|
// CHECK-LABEL: size_limit::test_101_move
|
|
pub fn test_101_move() {
|
|
let sz101 = Struct101 { data: [42; 101] };
|
|
// CHECK: call void @llvm.memcpy{{.*}}, !dbg ![[#SZ101_MOVE_LOC:]]
|
|
let _moved = sz101;
|
|
}
|
|
|
|
// The scope for no-annotated is clone function itself
|
|
// CHECK-DAG: ![[#SZ99_COPY_LOC]] = !DILocation({{.*}}scope: ![[#SZ99_COPY_SCOPE:]]
|
|
// CHECK-DAG: ![[#SZ99_COPY_SCOPE]] = {{(distinct )?}}!DISubprogram(name: "clone",
|
|
|
|
// Clone itself is copy, but return is move.
|
|
// CHECK-DAG: ![[#SZ100_COPY_LOC]] = !DILocation({{.*}}scope: ![[#SZ100_COPY_SCOPE:]]
|
|
// CHECK-DAG: ![[#SZ100_COPY_SCOPE]] = {{(distinct )?}}!DISubprogram(name: "compiler_copy<{{(array\$<|\[)u8[,;].*}},{{ *[0-9]+}}>"
|
|
// CHECK-DAG: ![[#SZ100_RETURN_LOC]] = !DILocation({{.*}}scope: ![[#SZ100_RETURN_SCOPE:]]
|
|
// CHECK-DAG: ![[#SZ100_RETURN_SCOPE]] = {{(distinct )?}}!DISubprogram(name: "compiler_move<{{(array\$<|\[)u8[,;].*}},{{ *[0-9]+}}>"
|
|
|
|
// Assignment is move
|
|
// CHECK-DAG: ![[#SZ100_MOVE_LOC]] = !DILocation({{.*}}scope: ![[#SZ100_MOVE_SCOPE:]]
|
|
// CHECK-DAG: ![[#SZ100_MOVE_SCOPE]] = {{(distinct )?}}!DISubprogram(name: "compiler_move<{{(array\$<|\[)u8[,;].*}},{{ *[0-9]+}}>"
|
|
|
|
// Clone itself is copy, but return is move.
|
|
// CHECK-DAG: ![[#SZ101_COPY_LOC]] = !DILocation({{.*}}scope: ![[#SZ101_COPY_SCOPE:]]
|
|
// CHECK-DAG: ![[#SZ101_COPY_SCOPE]] = {{(distinct )?}}!DISubprogram(name: "compiler_copy<{{(array\$<|\[)u8[,;].*}},{{ *[0-9]+}}>"
|
|
// CHECK-DAG: ![[#SZ101_RETURN_LOC]] = !DILocation({{.*}}scope: ![[#SZ101_RETURN_SCOPE:]]
|
|
// CHECK-DAG: ![[#SZ101_RETURN_SCOPE]] = {{(distinct )?}}!DISubprogram(name: "compiler_move<{{(array\$<|\[)u8[,;].*}},{{ *[0-9]+}}>"
|
|
|
|
// Assignment is move
|
|
// CHECK-DAG: ![[#SZ101_MOVE_LOC]] = !DILocation({{.*}}scope: ![[#SZ101_MOVE_SCOPE:]]
|
|
// CHECK-DAG: ![[#SZ101_MOVE_SCOPE]] = {{(distinct )?}}!DISubprogram(name: "compiler_move<{{(array\$<|\[)u8[,;].*}},{{ *[0-9]+}}>"
|