Open reinerp opened 4 years ago
(Oh, on the last comment: I see why there's two loads: one of them was a 1-byte load, not a 2-byte load....)
Clone
is special-cased for cases where Copy
is also implemented. The other case depends entirely on optimizer as derive(Clone)
has to generate a fairly generic sequence of operations that would work for arbitrary enums.
I wonder if it would be worthwhile to try to detect and optimize this case. If a type 'could have been' Copy
(it and all of its transitive fields are either #[derive(Copy)]
or #[derive(Clone)]
with no user-provided Clone
impls), then the Clone
implementation should be able to be the same as a Copy
implementation.
For maximum effectiveness, this would probably have to be done during codegen (e.g. instead of codegenning the actual Clone
implementation, we 'synthesize' a function in librustc_codegen_ssa
), which would be quite hacky. However, it might result in some performance wins (less work for LLVM to do, and more efficient code generation).
It's still not perfect (why are there two 2-byte loads instead of a single 4-byte load?) but it's much better.
Because that could result in a fault. Note that Bar
is 1-aligned, so there is no guarantee that byte 4 is dereferencable. Adding align(2)
would be sufficient to guarantee that, in which case it will compile to a simple load.
Btw, it looks like nowadays the compiler gets it right - at least in the release mode, the assembly ends up being same for Copy
and non-Copy
enums:
playground::clone_foo:
movzbl (%rdi), %eax
movzbl 1(%rdi), %edx
retq
playground::clone_bar:
movzwl (%rdi), %eax
retq
For this Rust code, we get some pretty bad assembly:
(Playground)
Assembly:
To see that it's possibly to do better, we can simply add a
Copy
instance to the types. Then the code even forClone
gets much better:(Playground)
Assembly:
It's still not perfect (why are there two 2-byte loads instead of a single 4-byte load?) but it's much better.