https://www.eventhelix.com/rust/
一种被特殊对待的union类型,包含一个识别器,用来识别当前类型,占用8个字节。整个Enum的大小,取决于所有成员中占用最大的一个。例如下面的这个Number就占用24个字节(8 + 16),最大的成员是Complex占用16个字节,额外还有一个识别器占用8个字节。这个识别器的大小也会根据成员大小进行调整,主要是满足最小的对齐要求。
pub enum Number {
Integer(i64),
Float(f64),
Complex { real: f64, imaginary: f64 },
}
pub fn double(num: Number) -> Number {
match num {
Number::Integer(n) => Number::Integer(n + n),
Number::Float(n) => Number::Float(n + n),
Number::Complex { real, imaginary } => Number::Complex {
real: real + real,
imaginary: imaginary + imaginary,
},
}
}
上面的代码对应到汇编如下,获取Enum的前8个字节,根据其内容来决定走哪个match arm,如果是0就走LBB0_5是Number::Integer,如果不是1就走LBB0_3是Number::Complex,否则就是Number::Float。
// rsi 是enum的地址
// rdi 是要返回的枚举地址,这种return一个value,提前在外部把地址准备好后,然后传入进来
// 类似于C++中的RVO优化
example::double:
mov rax, rdi
// 读enum开时初的8个字节,这是识别器
mov rcx, qword ptr [rsi]
// 如果是0,就是Number::Integer,走Integer的逻辑
test rcx, rcx
je .LBB0_5
// 如果是1,就走Number:Float的逻辑
cmp ecx, 1
// 不是1就走Number::Complex逻辑
jne .LBB0_3
movsd xmm0, qword ptr [rsi + 8]
addsd xmm0, xmm0
movsd qword ptr [rax + 8], xmm0
mov qword ptr [rax], rcx
ret
.LBB0_5:
mov rdx, qword ptr [rsi + 8]
add rdx, rdx
mov qword ptr [rax + 8], rdx
mov qword ptr [rax], rcx
ret
.LBB0_3:
movupd xmm0, xmmword ptr [rsi + 8]
addpd xmm0, xmm0
movupd xmmword ptr [rax + 8], xmm0
mov qword ptr [rax], rcx
ret
pub fn magnitude_self_copy(self) -> f64 {
(self.real.powf(2.0) + self.imaginary.powf(2.0)).sqrt()
}
; The compiler has optimized the code to pass the real and
; imaginary parts in the xmm0 and xmm1 registers.
// 直接通过寄存器把self传递到函数中,这个应该是编译器的优化
example::Complex::magnitude_self_copy:
mulsd xmm0, xmm0 ; Square the real part
mulsd xmm1, xmm1 ; Square the imaginary part
addsd xmm1, xmm0 ; Add the two squared numbers and store the result in xmm1
xorps xmm0, xmm0 ; Clear xmm0. This will zero out the upper bits of the reg.
sqrtsd xmm0, xmm1 ; Perform the square root on the squared sum and store in xmm0
ret ; Return to the called with the result in xmm0
pub fn magnitude_self_reference(&self) -> f64 {
(self.real.powf(2.0) + self.imaginary.powf(2.0)).sqrt()
}
; The caller will pass the pointer to the Complex struct in the rdi register.
// 把&self作为函数的第一个参数,通过rdi传递
example::Complex::magnitude_self_reference:
// 向量指令,一次性载入16个字节
movupd xmm0, xmmword ptr [rdi] ; Vectorized load of the real and imaginary parts
mulpd xmm0, xmm0 ; Square the real and imaginary parts (vectorized)
movapd xmm1, xmm0 ; Vectorized copy of the real and imaginary parts to xmm1
unpckhpd xmm1, xmm0 ; Unpack the real and imaginary parts into xmm1 and xmm0
addsd xmm1, xmm0 ; Add the real and imaginary parts
xorps xmm0, xmm0 ; Clear the complete xmm0 to 0
sqrtsd xmm0, xmm1 ; square root of xmm1 -> xmm0
ret ; return xmm0
pub fn magnitude_self_box(self: Box<Self>) -> f64 {
(self.real.powf(2.0) + self.imaginary.powf(2.0)).sqrt()
}
; The caller stores the Complex object on the heap.
; The heap address is passed in the rdi register.
// Box的地址也是通过rdi传递,处理完成后需要主动调用Box的析构
example::Complex::magnitude_self_box:
push rax ; Preserve the current value of rax on the stack.
movupd xmm0, xmmword ptr [rdi] ; Vectorized load of the real and imaginary parts
mulpd xmm0, xmm0 ; Square the real and imaginary parts (vectorized)
movapd xmm1, xmm0 ; Vectorized copy of the real and imaginary parts to xmm1
unpckhpd xmm1, xmm0 ; Unpack the real and imaginary parts into xmm1 and xmm0
addsd xmm1, xmm0 ; Add the squared real and imaginary parts
xorps xmm0, xmm0 ; Clear the complete xmm0 to 0
sqrtsd xmm0, xmm1 ; square root of xmm1 -> xmm0
;♻️This method owns the Box. Now that the function is about to return so
; the Box is going out of scope and is about to be dropped.
; Dropping here means that the heap memory allocated for the Complex object
; can now be freed. Note that the rdi register already points to the memory that
; needs to be freed.
// 调用Box的析构函数
movsd qword ptr [rsp], xmm0 ; Save xmm0 on the stack
mov esi, 16 ; Size of the memory to be freed (Complex is 16 bytes)
mov edx, 8 ; The data is 8-byte aligned.
; The parameters to the de-allocation function are:
; rdi : Address of memory to be freed
; esi : Size of memory to be freed.
; edx: Alignment of the memory to be freed.
call qword ptr [rip + __rust_dealloc@GOTPCREL] ; The parameters to the de-allocation function are:
movsd xmm0, qword ptr [rsp] ; Restore the xmm0 from the stack. This is the return value.
pop rax ; Restore the value of the rax register
ret ; Return the result in xmm0