<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【筆記】匯編入門

      鑒于在華子呆著沒事干,開始學習匯編。

      簡單循環

      int test(int a, int b) {
          for (int i = 0; i < b; ++i) {
              a <<= 1;
          }
          return a;
      }
      

      O0

      	.file	"a.cpp"
      	.text
      	.globl	_Z4testii             ; ii 表示接受兩個整型參數
      	.type	_Z4testii, @function
      _Z4testii:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	pushq	%rbp
      	.cfi_def_cfa_offset 16
      	.cfi_offset 6, -16
      	movq	%rsp, %rbp
      	.cfi_def_cfa_register 6
      	movl	%edi, -20(%rbp)  ; 將 a 保存到棧位置 -20(%rbp)
      	movl	%esi, -24(%rbp)  ; 將 b 保存到棧位置 -24(%rbp)
      	movl	$0, -4(%rbp)     ; 初始化 i = 0
      	jmp	.L2                  
      .L3:
      	sall	-20(%rbp)        ; a <<= 1
      	addl	$1, -4(%rbp)     ; i++
      .L2:
      	movl	-4(%rbp), %eax   ; 將循環計數器加載到 EAX
      	cmpl	-24(%rbp), %eax  ; 比較 i 和第二個參數 b
      	jl	.L3                  ; 如果 i < b,跳轉到循環體
      	movl	-20(%rbp), %eax  ; 將 a 加載到 EAX(返回值寄存器)
      	popq	%rbp
      	.cfi_def_cfa 7, 8
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z4testii, .-_Z4testii
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      這段代碼沒有進行優化(可能是使用 -O0 編譯選項),因此:

      • 內存訪問頻繁: 所有操作都通過內存進行,而不是直接使用寄存器
      • 棧幀完整: 創建了完整的棧幀,包括基指針的保存和恢復
      • 循環效率低: 每次循環都需要從內存加載和存儲值

      O1

      	.file	"a.cpp"
      	.text
      	.globl	_Z4testii
      	.type	_Z4testii, @function
      _Z4testii:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	movl	%edi, %eax  ; 將 a 移動到 EAX(結果寄存器)
      	testl	%esi, %esi  ; 測試 b 的值. testl指令,是將兩個操作數做與來設置零標志位和負數標識,常用的方法是testl %eax,%eax來檢查%eax是正數負數還是0
      	jle	.L2             ; 如果 b <= 0,跳轉到.L2(直接返回)
      	movl	$0, %edx    ; 將循環計數器 EDX 初始化為0
      .L3:
      	addl	%eax, %eax  ; a = a + a
      	addl	$1, %edx    ; i++
      	cmpl	%edx, %esi  ; 比較 i 和 b
      	jne	.L3             ; 如果不相等就繼續循環
      .L2:
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z4testii, .-_Z4testii
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      寄存器分配優化:

      • 使用EAX同時存儲參數和結果,減少數據移動
      • 使用EDX作為循環計數器,避免內存訪問

      循環優化:

      • 使用do-while循環結構,減少一次條件判斷
      • 使用計數器與參數直接比較,而不是與0比較

      算術優化:

      • 使用addl %eax, %eax實現乘以2,而不是使用移位指令
      • 這可能是因為在某些處理器上,加法比移位更快

      棧幀消除:

      • 沒有創建棧幀(沒有pushq %rbp和movq %rsp, %rbp)
      • 所有操作都在寄存器中完成,極大提高效率

      O2

      	.file	"a.cpp"
      	.text
      	.p2align 4
      	.globl	_Z4testii
      	.type	_Z4testii, @function
      _Z4testii:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	movl	%edi, %eax
      	testl	%esi, %esi  ; 測試第二個參數(b)
      	jle	.L2             ; 如果b <= 0,跳轉到.L2(直接返回)
      	xorl	%edx, %edx  ; i = 0
      	testb	$1, %sil    ; 測試
      	je	.L3
      	addl	%eax, %eax  ; 如果是奇數,就會執行現在這一段
      	movl	$1, %edx    ; i = 1
      	cmpl	$1, %esi    ; 
      	je	.L11            ; 如果 b == 1,直接 return,否則會進入下面的 L3:
      	.p2align 4,,10
      	.p2align 3
      .L3:
      	addl	$2, %edx    ; 每次 i += 2
      	sall	$2, %eax    ; a <<= 2
      	cmpl	%edx, %esi  ; 比較 i 和 b
      	jne	.L3             ; 如果不相等就繼續循環
      .L2:
      	ret
      .L11:
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z4testii, .-_Z4testii
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      
      • 每次迭代處理2位的移位(乘以4),而不是1位,減少循環迭代次數。

      O3

      	.file	"a.cpp"
      	.text
      	.p2align 4
      	.globl	_Z4testii
      	.type	_Z4testii, @function
      _Z4testii:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	movl	%edi, %eax
      	testl	%esi, %esi
      	jle	.L2
      	xorl	%edx, %edx
      	testb	$1, %sil
      	je	.L3
      	addl	%eax, %eax
      	movl	$1, %edx
      	cmpl	$1, %esi
      	je	.L11
      	.p2align 4,,10
      	.p2align 3
      .L3:
      	addl	$2, %edx
      	sall	$2, %eax
      	cmpl	%edx, %esi
      	jne	.L3
      .L2:
      	ret
      .L11:
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z4testii, .-_Z4testii
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      
      • 和 O2 的效果是一樣的。

      Fibonacci(遞歸)

      int fib(int x) {
          if (x == 0) {
              return 0;
          }
          if (x == 1) {
              return 1;
          }
          return fib(x - 1) + fib(x - 2);
      }
      

      利用這個代碼的匯編,我們講清楚如何棧幀操作。

      O0

      	.file	"a.cpp"
      	.text
      	.globl	_Z3fibi
      	.type	_Z3fibi, @function
      _Z3fibi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	pushq	%rbp              ; 將調用者函數的基址指針 (%rbp) 的值壓入棧中保存。RBP 是 callee-saved,因為接下來修改了(從調用者的棧幀底部變成當前棧幀的底部),所以必須保存。
      	.cfi_def_cfa_offset 16
      	.cfi_offset 6, -16
      	movq	%rsp, %rbp        ; 從現在開始,%rbp 成了一個穩定的“錨點”,指向當前棧幀的底部。函數內的局部變量和參數都將通過相對于 %rbp 的偏移來訪問(例如 -20(%rbp))
      	.cfi_def_cfa_register 6
      	pushq	%rbx              ; 根據調用約定,%rbx 是被調用者保存的寄存器(Callee-saved)。這意味著如果函數要使用它,必須在函數開頭保存其原始值,并在函數返回前恢復它。這里是因為后面的遞歸調用需要用到
      	subq	$24, %rsp         ; 在棧上分配24字節空間
      	.cfi_offset 3, -24
      	movl	%edi, -20(%rbp)   ; 將 n 存入棧幀(位置:%rbp-20)
      	cmpl	$0, -20(%rbp)
      	jne	.L2                   ; 如果 n != 0,跳轉到.L2
      	movl	$0, %eax          ; 否則返回0
      	jmp	.L3
      .L2:
      	cmpl	$1, -20(%rbp)
      	jne	.L4                   ; 如果 n != 1,跳轉到.L4(遞歸部分)
      	movl	$1, %eax          ; 否則返回 1
      	jmp	.L3
      .L4:
      	movl	-20(%rbp), %eax   
      	subl	$1, %eax          ; 計算 n-1,保存至 EAX
      	movl	%eax, %edi        ; 將 n-1 作為參數
      	call	_Z3fibi           ; f(n-1)
      	movl	%eax, %ebx        ; 把 f(n-1) 的結果存到 EBX 上
      	movl	-20(%rbp), %eax  
      	subl	$2, %eax          ; 計算 n-2
      	movl	%eax, %edi        ; 把 n-2 作為參數
      	call	_Z3fibi           ; f(n-2)
      	addl	%ebx, %eax        ; 把 f(n-1) + f(n-2) 作為返回值
      .L3:
      	movq	-8(%rbp), %rbx    ; 恢復保存的 %rbx
      	leave                     ; 恢復棧幀(等價于 movq %rbp, %rsp + popq %rbp)
      	.cfi_def_cfa 7, 8
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fibi, .-_Z3fibi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      調用約定的規則:調用者保存 vs. 被調用者保存

      x86-64架構下的寄存器被分為兩大陣營,這是理解一切的關鍵:

      • 調用者保存寄存器(Caller-saved / Volatile Registers):

        • 包括: RAX, RCX, RDX, RDI, RSI, R8, R9, R10, R11 等。
        • 規則: 如果一個函數(調用者)希望在這些寄存器中的值在子函數調用后仍然有效,它必須自己在調用call指令之前手動把它們保存到棧上。子函數(被調用者)可以隨意修改這些寄存器而無需恢復。
        • 設計目的: 用于傳遞臨時性的參數和結果。
      • 被調用者保存寄存器(Callee-saved / Non-volatile Registers):

        • 包括: RBX, RBP, R12, R13, R14, R15。
        • 規則: 如果一個函數(被調用者)想要使用這些寄存器,它必須在函數的開頭(Prologue)保存它們的原始值,并在函數返回前(Epilogue)準確地恢復它們。對于調用者來說,它可以放心地認為這些寄存器的值在子函數調用后不會改變。
        • 設計目的: 保存需要跨越函數調用的長期變量。

      總結

      寄存器類型 是否可用 后果
      調用者保存 (如 RCX, RDX) 不可用 值會在后續 call 中被破壞,導致錯誤。必須手動保存,增加指令,降低效率。
      被調用者保存 (如 RBX, R12) 可用且推薦 值在后續 call 中保持不變。只需在函數開頭和結尾保存/恢復一次,安全且高效。

      結論:
      編譯器不是必須使用 RBX,但它必須使用一個被調用者保存寄存器RBX 是其中一個典型且傳統的選擇。它之所以出現在你的代碼中,是因為它是滿足“將一個值安全地跨越函數調用保存下來”這一需求的最直接、最正確、最高效的方式。

      O1

      	.file	"a.cpp"
      	.text
      	.globl	_Z3fibi
      	.type	_Z3fibi, @function
      _Z3fibi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	pushq	%rbp
      	.cfi_def_cfa_offset 16
      	.cfi_offset 6, -16
      	pushq	%rbx
      	.cfi_def_cfa_offset 24
      	.cfi_offset 3, -24
      	subq	$8, %rsp           ; 只分配了 8 字節
      	.cfi_def_cfa_offset 32
      	movl	%edi, %ebx         ; 把參數 n 保存到 EBX
      	testl	%edi, %edi          
      	je	.L2                    ; 如果是 0 就到 .L2
      	cmpl	$1, %edi
      	je	.L2                    ; 如果等于1,跳轉到.L2
      	leal	-1(%rdi), %edi     ; 用 leal 來計算 n - 1
      	call	_Z3fibi            ; f(n-1)
      	movl	%eax, %ebp         ; 暫存結果
      	leal	-2(%rbx), %edi    
      	call	_Z3fibi            ; f(n-2)
      	leal	0(%rbp,%rax), %ebx ; f(n-1)+f(n-2) 存到 EBX
      .L2:
      	movl	%ebx, %eax         ; 返回值寫入
      	addq	$8, %rsp           ; 恢復棧指針(分配了8字節,現在收回)
      	.cfi_def_cfa_offset 24
      	popq	%rbx
      	.cfi_def_cfa_offset 16
      	popq	%rbp
      	.cfi_def_cfa_offset 8
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fibi, .-_Z3fibi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      優化帶來的改進

      • 減少棧使用:只分配 8 字節??臻g,而不是 24 字節
      • 寄存器利用:更好地利用寄存器保存中間值,減少內存訪問
      • 指令選擇:使用更高效的指令做算術運算(如 leal 和 testl)
      • 代碼共享:所有返回路徑共享同一段代碼
      • 減少內存訪問:直接在寄存器間傳遞值,而不是通過棧

      O2

      	.file	"a.cpp"
      	.text
      	.p2align 4
      	.globl	_Z3fibi
      	.type	_Z3fibi, @function
      _Z3fibi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	pushq	%r15
      	.cfi_def_cfa_offset 16
      	.cfi_offset 15, -16
      	pushq	%r14
      	.cfi_def_cfa_offset 24
      	.cfi_offset 14, -24
      	pushq	%r13
      	.cfi_def_cfa_offset 32
      	.cfi_offset 13, -32
      	pushq	%r12
      	.cfi_def_cfa_offset 40
      	.cfi_offset 12, -40
      	movl	%edi, %r12d
      	pushq	%rbp
      	.cfi_def_cfa_offset 48
      	.cfi_offset 6, -48
      	pushq	%rbx
      	.cfi_def_cfa_offset 56
      	.cfi_offset 3, -56
      	subq	$88, %rsp
      	.cfi_def_cfa_offset 144
      	testl	%edi, %edi
      	je	.L2
      	cmpl	$1, %edi
      	je	.L2
      	leal	-1(%rdi), %r15d
      	xorl	%r12d, %r12d
      .L27:
      	cmpl	$1, %r15d
      	je	.L52
      	leal	-1(%r15), %r13d
      	xorl	%r14d, %r14d
      	movl	%r12d, 28(%rsp)
      	movl	%r13d, 32(%rsp)
      	movl	%r13d, %ebp
      	movl	%r14d, %r12d
      .L26:
      	cmpl	$1, %ebp
      	je	.L51
      	movl	%r15d, 36(%rsp)
      	leal	-1(%rbp), %ecx
      	xorl	%r14d, %r14d
      	movl	%ebp, 40(%rsp)
      	movl	%ecx, %ebp
      .L25:
      	cmpl	$1, %ebp
      	je	.L50
      	movl	%r14d, 48(%rsp)
      	leal	-1(%rbp), %edi
      	xorl	%r15d, %r15d
      	movl	%ebp, %r13d
      	movl	%ecx, 52(%rsp)
      	movl	%r12d, 44(%rsp)
      	movl	%edi, %r12d
      .L24:
      	cmpl	$1, %r12d
      	je	.L49
      	leal	-1(%r12), %r11d
      	xorl	%r14d, %r14d
      	movl	%r15d, 56(%rsp)
      	movl	%r14d, 16(%rsp)
      	movl	%r11d, %ebp
      	movl	%edi, 60(%rsp)
      	movl	%r11d, 64(%rsp)
      	movl	%r12d, 68(%rsp)
      .L23:
      	cmpl	$1, %ebp
      	je	.L48
      	leal	-1(%rbp), %r12d
      	movl	%ebp, 76(%rsp)
      	xorl	%r15d, %r15d
      	movl	%r12d, 72(%rsp)
      	movl	%r12d, %r14d
      .L22:
      	cmpl	$1, %r14d
      	je	.L47
      	leal	-1(%r14), %ebp
      	movl	%r13d, 4(%rsp)
      	xorl	%ecx, %ecx
      	movl	%ebp, 8(%rsp)
      	movl	%ebp, %ebx
      	movl	%r14d, 12(%rsp)
      .L21:
      	cmpl	$1, %ebx
      	je	.L46
      	leal	-1(%rbx), %ebp
      	xorl	%r13d, %r13d
      	movl	%ebp, %r14d
      	movl	%ebp, %edx
      	movl	%ecx, %ebp
      	movl	%ebx, %ecx
      	movl	%r14d, %ebx
      .L20:
      	movl	%ebx, %r14d
      	cmpl	$1, %ebx
      	je	.L45
      	movl	%ebx, 20(%rsp)
      	xorl	%r12d, %r12d
      	movl	%edx, %ebx
      .L16:
      	leal	-1(%r14), %edi
      	movl	%ecx, 24(%rsp)
      	call	_Z3fibi
      	movl	24(%rsp), %ecx
      	addl	%eax, %r12d
      	subl	$2, %r14d
      	je	.L54
      	cmpl	$1, %r14d
      	jne	.L16
      	movl	%ebx, %edx
      	movl	20(%rsp), %ebx
      	addl	$1, %r12d
      .L18:
      	addl	%r12d, %r13d
      	subl	$2, %ebx
      	jne	.L20
      .L45:
      	movl	%ecx, %ebx
      	leal	1(%r13), %esi
      	movl	%ebp, %ecx
      	addl	%esi, %ecx
      	subl	$2, %ebx
      	cmpl	$1, %edx
      	jne	.L21
      .L46:
      	movl	12(%rsp), %r14d
      	movl	%ecx, %ebx
      	movl	8(%rsp), %ebp
      	addl	$1, %ebx
      	movl	4(%rsp), %r13d
      	addl	%ebx, %r15d
      	subl	$2, %r14d
      	cmpl	$1, %ebp
      	jne	.L22
      .L47:
      	movl	76(%rsp), %ebp
      	movl	72(%rsp), %r12d
      	addl	$1, %r15d
      	addl	%r15d, 16(%rsp)
      	subl	$2, %ebp
      	cmpl	$1, %r12d
      	jne	.L23
      .L48:
      	movl	16(%rsp), %r14d
      	movl	56(%rsp), %r15d
      	movl	68(%rsp), %r12d
      	movl	64(%rsp), %r11d
      	addl	$1, %r14d
      	movl	60(%rsp), %edi
      	addl	%r14d, %r15d
      	subl	$2, %r12d
      	cmpl	$1, %r11d
      	jne	.L24
      .L49:
      	movl	48(%rsp), %r14d
      	movl	%r13d, %ebp
      	addl	$1, %r15d
      	movl	52(%rsp), %ecx
      	movl	44(%rsp), %r12d
      	subl	$2, %ebp
      	addl	%r15d, %r14d
      	cmpl	$1, %edi
      	jne	.L25
      .L50:
      	movl	40(%rsp), %ebp
      	addl	$1, %r14d
      	movl	36(%rsp), %r15d
      	addl	%r14d, %r12d
      	subl	$2, %ebp
      	cmpl	$1, %ecx
      	jne	.L26
      .L51:
      	movl	%r12d, %r14d
      	movl	32(%rsp), %r13d
      	movl	28(%rsp), %r12d
      	subl	$2, %r15d
      	addl	$1, %r14d
      	addl	%r14d, %r12d
      	cmpl	$1, %r13d
      	jne	.L27
      .L52:
      	addl	$1, %r12d
      .L2:
      	addq	$88, %rsp
      	.cfi_remember_state
      	.cfi_def_cfa_offset 56
      	movl	%r12d, %eax
      	popq	%rbx
      	.cfi_def_cfa_offset 48
      	popq	%rbp
      	.cfi_def_cfa_offset 40
      	popq	%r12
      	.cfi_def_cfa_offset 32
      	popq	%r13
      	.cfi_def_cfa_offset 24
      	popq	%r14
      	.cfi_def_cfa_offset 16
      	popq	%r15
      	.cfi_def_cfa_offset 8
      	ret
      	.p2align 4,,10
      	.p2align 3
      .L54:
      	.cfi_restore_state
      	movl	%ebx, %edx
      	movl	20(%rsp), %ebx
      	jmp	.L18
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fibi, .-_Z3fibi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      性能權衡
      這種優化帶來了性能提升,但代價是代碼大小增加:

      優點:

      • 減少函數調用開銷
      • 更好的指令級并行性
      • 更高效的內存訪問模式

      缺點:

      • 代碼大小顯著增加
      • 可讀性降低
      • 可能增加指令緩存壓力

      O3

      和 O2 一樣,略。

      尾遞歸優化

      int foo(int x) {
          if (x <= 0) {
              return 0;
          }
          return foo(x - 1) + 1;
      }
      

      O1

      我們這次先看 O1 的結果,因為他比較符合我們對源代碼(C++)的認知。

      	.file	"a.cpp"
      	.text
      	.globl	_Z3fooi
      	.type	_Z3fooi, @function
      _Z3fooi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	movl	$0, %eax        ; 預先將返回值設為0
      	testl	%edi, %edi      ; 測試參數n是否為0或負數
      	jle	.L5                 ; 如果n <= 0,直接返回0
      	subq	$8, %rsp        ; 分配8字節??臻g(用于對齊)
      	.cfi_def_cfa_offset 16
      	subl	$1, %edi        ; 參數減1
      	call	_Z3fooi         ; foo(x-1)
      	addl	$1, %eax        ; 結果加1
      	addq	$8, %rsp        ; 恢復棧指針
      	.cfi_def_cfa_offset 8
      	ret
      .L5:
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fooi, .-_Z3fooi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      
      

      O0

      	.file	"a.cpp"
      	.text
      	.globl	_Z3fooi
      	.type	_Z3fooi, @function
      _Z3fooi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	pushq	%rbp
      	.cfi_def_cfa_offset 16
      	.cfi_offset 6, -16
      	movq	%rsp, %rbp
      	.cfi_def_cfa_register 6
      	subq	$16, %rsp
      	movl	%edi, -4(%rbp)
      	cmpl	$0, -4(%rbp)
      	jg	.L2
      	movl	$0, %eax
      	jmp	.L3
      .L2:
      	movl	-4(%rbp), %eax
      	subl	$1, %eax
      	movl	%eax, %edi
      	call	_Z3fooi
      	addl	$1, %eax
      .L3:
      	leave
      	.cfi_def_cfa 7, 8
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fooi, .-_Z3fooi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      
      

      O2

      	.file	"a.cpp"
      	.text
      	.p2align 4
      	.globl	_Z3fooi
      	.type	_Z3fooi, @function
      _Z3fooi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	xorl	%eax, %eax
      	testl	%edi, %edi
      	cmovns	%edi, %eax  ; 如果 n 是非負數(non-negative),將 n 移動到 eax
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fooi, .-_Z3fooi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      編譯器完全識別并消除了遞歸調用

      尾遞歸優化其二

      int foo(int x) {
          if (x <= 0) {
              return 0;
          }
          return foo(x - 1) + (x & (x - 1));
      }
      

      O0

      	.file	"a.cpp"
      	.text
      	.globl	_Z3fooi
      	.type	_Z3fooi, @function
      _Z3fooi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	pushq	%rbp
      	.cfi_def_cfa_offset 16
      	.cfi_offset 6, -16
      	movq	%rsp, %rbp
      	.cfi_def_cfa_register 6
      	subq	$16, %rsp        ; 開 16 個字節的??臻g
      	movl	%edi, -4(%rbp)   ; 保存 x
      	cmpl	$0, -4(%rbp)     
      	jg	.L2                  ; 大于零進遞歸
      	movl	$0, %eax
      	jmp	.L3                  ; 否則返回 0
      .L2:
      	movl	-4(%rbp), %eax   ; 
      	subl	$1, %eax         ; 得到 x-1
      	movl	%eax, %edi       ; foo(x-1)
      	call	_Z3fooi
      	movl	-4(%rbp), %edx   ;
      	subl	$1, %edx         ; 得到 x-1
      	andl	-4(%rbp), %edx   ; 得到 x & (x-1)
      	addl	%edx, %eax       ; foo(x-1) + (x & (x-1))
      .L3:
      	leave
      	.cfi_def_cfa 7, 8
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fooi, .-_Z3fooi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      O1

      	.file	"a.cpp"
      	.text
      	.globl	_Z3fooi
      	.type	_Z3fooi, @function
      _Z3fooi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	movl	$0, %eax
      	testl	%edi, %edi
      	jle	.L5
      	pushq	%rbp
      	.cfi_def_cfa_offset 16
      	.cfi_offset 6, -16
      	pushq	%rbx               ; 保存 RBX 寄存器
      	.cfi_def_cfa_offset 24
      	.cfi_offset 3, -24
      	subq	$8, %rsp           ; 開 8 個字節的??臻g
      	.cfi_def_cfa_offset 32
      	movl	%edi, %ebp         ; 保存 x
      	leal	-1(%rdi), %ebx     
      	movl	%ebx, %edi         ; 計算 x-1 為參數
      	call	_Z3fooi            ; foo(x-1)
      	andl	%ebp, %ebx         ; x & (x-1)
      	addl	%ebx, %eax         ; EAX = foo(x-1) + (x & (x-1))
      	addq	$8, %rsp           ; 恢復??臻g
      	.cfi_def_cfa_offset 24
      	popq	%rbx
      	.cfi_def_cfa_offset 16
      	popq	%rbp
      	.cfi_def_cfa_offset 8
      	ret
      .L5:
      	.cfi_restore 3
      	.cfi_restore 6
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fooi, .-_Z3fooi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      O2

      	.file	"a.cpp"
      	.text
      	.p2align 4
      	.globl	_Z3fooi
      	.type	_Z3fooi, @function
      _Z3fooi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	xorl	%edx, %edx  ; EDX = 0 作為累加器
      	testl	%edi, %edi 
      	jle	.L1             ; 如果 n <= 0 就跳轉到結束區域
      	.p2align 4,,10
      	.p2align 3
      .L2:
      	movl	%edi, %eax  ; EAX = n
      	subl	$1, %edi    ; EDI = n - 1
      	andl	%edi, %eax  ; EAX = n & (n - 1)
      	addl	%eax, %edx  ; 累加
      	testl	%edi, %edi
      	jne	.L2             ; 如果 n != 0 就繼續循環
      .L1:
      	movl	%edx, %eax
      	ret
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fooi, .-_Z3fooi
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      

      O2 優化將遞歸完全轉換為迭代,消除所有函數調用開銷。

      O3

      這個 O3 優化版本展示了編譯器如何通過向量化(SIMD) 和循環展開來進一步優化代碼。我們不講解具體的實現,僅僅介紹大體的框架。

      • 向量化主循環:使用 SSE 指令并行處理 4 個元素
      • 標量尾端處理:處理不能被 4 整除的剩余元素
      • 循環展開:進一步優化標量部分
      	.file	"a.cpp"
      	.text
      	.p2align 4
      	.globl	_Z3fooi
      	.type	_Z3fooi, @function
      _Z3fooi:
      .LFB0:
      	.cfi_startproc
      	endbr64
      	testl	%edi, %edi
      	jle	.L19
      	cmpl	$4, %edi
      	jle	.L8
      	movd	%edi, %xmm6
      	movl	%edi, %edx
      	movdqa	.LC1(%rip), %xmm5
      	xorl	%eax, %eax
      	pshufd	$0, %xmm6, %xmm2
      	shrl	$2, %edx
      	pxor	%xmm0, %xmm0
      	pcmpeqd	%xmm4, %xmm4
      	paddd	.LC0(%rip), %xmm2
      	.p2align 4,,10
      	.p2align 3
      .L5:
      	movdqa	%xmm2, %xmm3
      	addl	$1, %eax
      	paddd	%xmm5, %xmm2
      	movdqa	%xmm3, %xmm1
      	paddd	%xmm4, %xmm1
      	pand	%xmm3, %xmm1
      	paddd	%xmm1, %xmm0
      	cmpl	%eax, %edx
      	jne	.L5
      	movdqa	%xmm0, %xmm1
      	movl	%edi, %edx
      	psrldq	$8, %xmm1
      	andl	$-4, %edx
      	paddd	%xmm1, %xmm0
      	movdqa	%xmm0, %xmm1
      	psrldq	$4, %xmm1
      	paddd	%xmm1, %xmm0
      	movd	%xmm0, %eax
      	testb	$3, %dil
      	je	.L1
      	subl	%edx, %edi
      .L3:
      	leal	-1(%rdi), %edx
      	movl	%edx, %ecx
      	andl	%edi, %ecx
      	addl	%ecx, %eax
      	testl	%edx, %edx
      	je	.L1
      	leal	-2(%rdi), %ecx
      	andl	%ecx, %edx
      	addl	%edx, %eax
      	testl	%ecx, %ecx
      	je	.L1
      	leal	-3(%rdi), %edx
      	andl	%edx, %ecx
      	addl	%ecx, %eax
      	testl	%edx, %edx
      	je	.L1
      	subl	$4, %edi
      	andl	%edx, %edi
      	addl	%edi, %eax
      .L1:
      	ret
      	.p2align 4,,10
      	.p2align 3
      .L19:
      	xorl	%eax, %eax
      	ret
      .L8:
      	xorl	%eax, %eax
      	jmp	.L3
      	.cfi_endproc
      .LFE0:
      	.size	_Z3fooi, .-_Z3fooi
      	.section	.rodata.cst16,"aM",@progbits,16
      	.align 16
      .LC0:
      	.long	0
      	.long	-1
      	.long	-2
      	.long	-3
      	.align 16
      .LC1:
      	.long	-4
      	.long	-4
      	.long	-4
      	.long	-4
      	.ident	"GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
      	.section	.note.GNU-stack,"",@progbits
      	.section	.note.gnu.property,"a"
      	.align 8
      	.long	1f - 0f
      	.long	4f - 1f
      	.long	5
      0:
      	.string	"GNU"
      1:
      	.align 8
      	.long	0xc0000002
      	.long	3f - 2f
      2:
      	.long	0x3
      3:
      	.align 8
      4:
      
      posted @ 2025-09-17 17:00  Imakf  閱讀(14)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 欧洲极品少妇| 欧美黑人性暴力猛交在线视频 | 香蕉乱码成人久久天堂爱| 日本毛茸茸的丰满熟妇| 激情综合网激情五月激情| 亚洲日韩性欧美中文字幕| 自拍偷区亚洲综合第二区| 人人妻人人做人人爽| 久久精品一偷一偷国产| 国产精品日韩深夜福利久久| 中国亚州女人69内射少妇| 国产av亚洲精品ai换脸电影| 精品人妻av中文字幕乱| 忍着娇喘人妻被中出中文字幕 | 免费人妻无码不卡中文18禁| 秋霞人妻无码中文字幕| 国产一区在线播放av| 在线aⅴ亚洲中文字幕| 最新亚洲人成网站在线观看| 欧洲lv尺码大精品久久久| 亚洲a∨国产av综合av| 精品偷拍被偷拍在线观看| A级毛片免费完整视频| 国产成本人片无码免费| 激情综合网一区二区三区| 欧美人与禽2o2o性论交| 巨爆乳中文字幕爆乳区| 日韩狼人精品在线观看| 人妻激情偷乱视频一区二区三区| 欧美日本在线一区二区三区| 国产成人欧美一区二区三区| 亚洲一区二区日韩综合久久| 国产精品视频午夜福利| 2021亚洲国产精品无码 | 国产二区三区不卡免费| 亚洲夂夂婷婷色拍WW47| 云南省| 免费观看性行为视频的网站| 久久天天躁夜夜躁狠狠 ds005.com| 台湾佬自拍偷区亚洲综合| 亚洲欧洲日产国码无码网站|