I’ve found that if I implement methods for a private type, then the methods stay in the compiler output even if the compiler automatically inlines all of the calls to those methods. The only way I’ve found to make it remove the methods from the compiler output is to annotate the method implementation with #[inline(always)]. That’s not too much of a hassle, but I’m curious why it’s necessary. For an example of this in action, consider the following bit of code:
// Note: In this example I’m implementing a method from an external trait,
// but the same behavior happens for locally defined traits
use std::ops::Mul;
pub fn foo(x: u64, y: u64) -> u64 {
let a = Matrix {
value: [[x, x], [y, y]]
};
let b = Matrix {
value: [[x, y], [x, y]]
};
let c = a * b;
c.value[0][0]
}
struct Matrix {
value: [[u64; 2]; 2]
}
impl Mul for Matrix {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let a = self.value;
let b = rhs.value;
let mut c = [[0; 2]; 2];
c[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0];
c[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1];
c[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0];
c[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1];
Self {
value: c
}
}
}
We have a private type Matrix, an implementation of Mul for it, and a public function that takes two integers, makes two matrices using those integers, multiplies them, and returns the top left entry in the resulting matrix.
When I put this in the Compiler Explorer and compile with -C opt-level=[anything but 0], the compiler inlines the call to Mul and then optimizes away the matrix multiplication entirely, and foo ends up looking like this:
example::foo:
imul rdi, rdi
lea rax, [rdi + rdi]
ret
This is exactly what I would expect. The weird part is that in addition to the assembly for foo, the compiler output includes the instructions for mul. This seems unnecessary. The compiler inlined every call to mul, so the program never actually uses the assembly instruction call to jump to where mul is defined. So leaving the instructions for mul in the compiled binary seems like a pure waste of space. Indeed, if I annotate mul with #[inline(always)], then the compiler outputs only the instructions for foo. Why is it that I would need to annotate with #[inline(always)] in order to eliminate the instructions for the method from the compiler output?