Untitled

https://godbolt.org/

https://www.eventhelix.com/rust/

Enum

一种被特殊对待的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

&、Box、Rc、Arc

Untitled

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