1 of 46

From source to binary

How Compiler Works

ソースからバイナリへ

從原始碼到二進制

Von Quelle Zu Binären

De source au binaire

Desde fuente a binario

Binarium ut a fonte

From source to binary

Luse Cheng

Sep 13, 2012 / 新竹碼農

Jim Huang (黃敬群)

jserv.tw@gmail.com

2 of 46

春秋: 微言大義

  • 隱公元年: 夏,五月,鄭伯於鄢

  • 短短幾個字,影含著諸多含意
    • 段不弟,故不言弟
    • 如二君,故曰克
    • 稱鄭伯,譏失教也

  • 為什麼要了解從原始碼到二進制
    • 電腦科學所有的問題都可以用另外一層的抽象化來解決 (All problems in computer science can be solved by another level of indirection)
    • 了解背後的運作原理, 破解微言大義

3 of 46

微言大義: �現代Linux環境, 程式啟動流程

Shell

eglibc

kernel

execve

./hello

SYS_execve

驗證執行檔

啟動loader

ld-linux.so

libc_start_main

hello

main

exit

SYS_exit

棄置Process

動態連結器

配置Process

動態連結

靜態連結

4 of 46

從原始碼到二進制: �常見的誤解

Source Code

Compiler (gcc, llvm)

HDL Source

Design Compiler (DC)

RTL Compiler (RC)

Assembly

Netlist

Selecting�Instruction

Technology�Mapping

軟體

硬體

  • 從原始碼到二進制,其實不只需要 Compiler
  • 真相: 需要很多 Tool 同心協力一起完成 (Toolchain)
  • 真正的Compiler 其實只做下面的事…

A compiler is a computer program (or set of programs) that transforms source code written in a programming language (the source language) into another computer language (the target language, often having a binary form known as object code)

source : http://en.wikipedia.org/wiki/Compiler

5 of 46

編譯 Hello world 程式的流程

Executable: hello 執行檔

Linker (LD) 連結器

Object File: hello.o 目的檔

Assembler (AS) 組譯器

Assembly File: hello.s 組合語言

Compiler (CC1) 編譯器

C program: hello.c 原始碼

編譯

組譯

連結

三大步驟

6 of 46

圖解簡易完整編譯流程

int main() {

printf (“Hello World”);

}

int printf (const char* fmt, …);

main:

LDR R1, [R2 + 12]

BAL printf

#include <stdio.h>

int main() {

printf (“Hello World”);

}

Preprocess

Compile

Assemble

Main:

EC 00 00 12

F0 ?? ?? ??

Linking

0x2000 <Main>:

EC 00 00 12

F0 00 10 00

0x1000 <printf>:

EC 00 00 12

stdio.h

libc.a

7 of 46

常見的誤解: GCC 是個 C 編譯器�答案: GCC 是一個 Compiler Driver

gcc

cc1

as

cpp

collect2

Preprocessed source (.i or .ii)

Asm (.s)

Reloadable (.o)

Binary

(Executable)

library

crt*

link script

Source (.c and .h)

gcc

binutils

glibc

ld

GCC 這個執行檔, 可以當Compiler, 也能當 Preprocessor, 也能當 Assembler, 更能當 Linker

8 of 46

如果有人想當你的程式碼�那你怎樣才能做她的編譯器 (Compiler) ?

  • 什麼是編譯器?
    • Source Language ->

Target Language

  • Recognizer: 讀懂她
  • Translator: 轉譯她
  • Optimization: 調教她

High Level Language

Optimized Low Level Language

Compiler

9 of 46

編譯器小歷史:

  • First compiler (1952)
    • 第一個編譯器
    • A-0, Grace Murray Hopper
  • First complete compiler (1957)
    • 第一個完整的編譯器
    • Fortran, John Backus (18-man years)
  • First multi-arch compiler (1960)
    • 第一個多平台編譯器
    • Cobol, Grace Murray Hopper el al.
  • The first self-hosting compiler (1962)
    • 第一個能自己編譯自己的編譯器
    • LISP, Tim Hart and Mike Levin

Image source:�http://www-history.mcs.st-and.ac.uk/PictDisplay/Hopper.html http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Grace_Hopper.jpg/300px-Grace_Hopper.jpg

Grace Hopper, inventor of

A-0, COBOL, and the

term “compiler.”

10 of 46

先有雞還是先有蛋

  • 先有C語言還是先有C編譯器

  • 一個有趣的故事:
    • Ken Thompson : Reflections on Trusting Trust �(Compiling a compiler)

  • Cross-compiling and Boot-strapping
    • 用X語言 (like Fortran or ASM) 寫一個C-的編譯器
    • 用C-寫一個C的編譯器
    • 用 C 寫一個C的編譯器

11 of 46

編譯器技術=屠龍之技

  • 莊子: 列禦寇第三十二
    • 朱泙漫學屠龍於支離益,單千金之家,三年技成而無所用其巧。

  • 用白話說就是有個人花了很多錢和時間學了屠龍技能, 後來發現沒有龍可以殺

  • 不過隨著硬體的發展,軟體的複雜度提升,這個世界上的龍也越來越多了

12 of 46

GCC 的架構

  • 課本終究還是課本

  • GCC 的特性與挑戰:
    • GCC 支援多種語言 (前端)
    • GCC 支援多種處理器 (後端)
    • Syntax Directed Translator
      • 程式的語法樹結構將無法和處理器架構脫鉤

  • 仔細想想: 有些最佳化和語言跟平台都沒有關係
    • 消除沒有用到的程式碼 (Dead code elimination)

  • 所以 GCC 引入中間層最佳化 (編譯器相關最佳化)

int main()

{

return 0;

IamDeadCode();

}

13 of 46

資料流程分析 (Data-flow analysis)

  • 為什麼需要資料流程分析

  • Q: 兩個指令怎樣才能交換位置 (Code Motion)
  • A: 如果兩個指令沒有相依性的話

  • 資料流程分析 = 偵測指令相依性
  • Reaching definition for a given instruction is another instruction, the target variable of which may reach the given instruction without an intervening assignment
    • Compute use-def chains and def-use chains

14 of 46

Code Motion & Pointer Aliasing

  • C 語言中阻擋 Compiler 最佳化的一大原因
    • Pointer Aliasing: 兩個不同的指標指到同一個記憶體位置

while (y < z) {

int x = a + b;

y += x;

}

void foo(int *a, int *b, int *y, int z) {

while (*y < z) {

int x = *a + *b;

*y += x;

}

}

int t1 = a + b;

while (y < z) {

y += t1;

}

Q: How about this?

  • A: Compiler 不能把*a + *b 移到迴圈外
    • 因為不確定使用者會不會這樣使用: foo(&num, &num, &num, 5566);
    • 這樣 a, b, y 互為別名, *y 的值改變會使 *a , *b 改變, 故不能提出到迴圈外

15 of 46

Strict Aliasing Rule & Restrict Pointer Aliasing

  • 如果 Pointer Aliasing 無限上綱
    • 那碰到 Pointer, Compiler 所有的最佳化都動不了
  • To or not to do:
    • Cross the Rubicon (O)
  • Strict Aliasing Rule
    • 摩登編譯器會假設不同 type 之間的 pointer 是沒有 aliasing 的
    • 可是世界上存在相當多原始人程式碼, 所以就造成了很多問題
    • Note: 使用 -fno-strict-aliasing 來關閉 Strict Aliasing Rule
  • C99: Restrict Pointer Aliasing
    • 使用 restrict 關鍵字 (--std=c99) 來告訴 Compiler 這個 Pointer 不會有 Pointer Aliasing

16 of 46

Static single assignment, SSA Form

  • 什麼是 SSA Form
    • 靜態單賦值形式 (O)
    • 静的単一代入 (O)

    • 用白話講, 在這個表示法當中�每個被 Assign 的變數只會出現一次
    • 作法 1. 每個被Assign的變數給一個�Version number 2. 使用 Φ functions
  • Example: (From Wiki)
    • INPUT: y = 1, y =2, x = y
    • SSA: y1 = 1, y2 = 2, x1 = y2

Diego Novillo, GCC Internals - IRs

17 of 46

使用 SSA 可以加強/加快以下的最佳化

  • Constant propagation
  • Sparse conditional constant propagation
  • Dead code elimination
  • Global value numbering
  • Partial redundancy elimination
  • Strength reduction
  • Register allocation

18 of 46

SSA @ Constant propagation (常數傳遞)

  • 用了SSA後每次 Constant propagation 都考100分
    • EX: GCC-3.x GCC-4.x Example

main:

mov r0, #0

bx lr

int main()

{

int a = 11,b = 13, c = 5566;

int i, result;

for (i = 0 ; i < 10000 ; i++)

result = (a*b + i) / (a*c);

return result;

}

main:

mov r4, #0

.L5:

mov r1, #61184

add r0, r4, #143

add r1, r1, #42

add r4, r4, #1

bl __divsi3

cmp r4, r5

ble .L5

GCC-4.x (SSA)

GCC-3.x (No-SSA)

19 of 46

GCC中端: Gimple & Tree SSA Optimizer

  • Gimple 是從 Generic Gimplify 而來的
    • 被限制每個運算只能有兩個運算元 (3-Address IR)
      • t1 = A op B ( op is operator like +-*/ … etc )
    • 被限制語法只能有某些控制流程
    • Gimple 可以被化簡成 SSA Form
    • 可以使用 -fdump-tree-<type>-<option> 來觀看其結構

  • Tree SSA Optimizer
    • 100+ Passes
    • Loop, Scalar optimization, alias analysis …etc
    • Inter-procedural analysis, Inter-procedural optimization

20 of 46

GCC後端: Register Transfer Language (RTL)

  • LISP-Style 的表示法
  • RTL Uses Virtual Register (無限多個 Register)
  • Register Allocation (Virtual Register -> Hard Register)
  • Instruction scheduling (Pipeline scheduling)
  • GCC Built-in Operation and Arch-defined Operation
  • Peephole optimization

(set (reg:SI 60 [b])

(plus:SI (reg:SI 61 [a])

(const_int -56 [0xffffffc8])))

b = a - 56

21 of 46

GCC後端: RTL Patten Match Engine

(define_insn "*addsi3"

[(set (match_operand:GPR 0 "register_operand" "=d,d")

(plus:GPR (match_operand:GPR 1 "register_operand" "d,d")

(match_operand:GPR 2 "arith_operand" "d,Q")))]

"!TARGET_MIPS16"

"@

addu\t%0,%1,%2

addiu\t%0,%1,%2"

[(set_attr "type" "arith")

(set_attr "mode" "si")])

MIPS.md

d代表是Register

Q代表是整數

指令的屬性(用於管線排程)

指令限制(非MIPS16才可使用)

addiu $2, $3, -56

b = a - 56

(set (reg:SI 60 [b])

(plus:SI (reg:SI 61 [a])

(const_int -56 [0xffffffc8])))

22 of 46

GCC後端 : 暫存器分配

  • 暫存器分配其實是個著色問題 (NP-Complete)
    • 給一個無向圖,相鄰的兩個vertex 不能著同一種顏色
    • 每個 vertex 代表一個變數, 每個 edge 代表的是兩個變數的生存時間有重疊
  • GCC 的暫存器分配,其實比著色問題更複雜
    • 在有些平台,某些指令其實只能搭配某組的暫存器
    • 其實有些變數是常數,或是 memory form,或是他是參數或回傳值,所以只能分配到某些特定暫存器
  • Old GCC RA: Reload Pass
    • GCC 把 pseudo-registers 根據指令的限制對應到硬體暫存器的過程
    • 其實是個複雜到極點的程式, 所以到最後就沒人看得懂了
  • 所以後來就重寫了一個
    • IRA (Integrated Register Allocator) : 整合 Coalescing, Live range splitting 以及挑選較好的 Hard Register 的做法

23 of 46

GCC後端 :管線排程

  • 以經典課本的五級Pipeline 為例子

IF

ID

EX

ME

WB

lw $8, a

lw $9, b

mul $10,$8,$8

add $11,$9,$10

Clock 0

Clock 1

Clock 2

Clock 3

Clock 4

Clock 5

Clock 6

Clock 7

Clock 8

lw $12,c

add $13,$12,$0

lw $8, a

lw $9, b

lw $12,c

mul $10,$8,$8

add $13,$12,$0

IF

ID

EX

ME

WB

24 of 46

GCC後端: Peephole optimization

  • 以管窺天最佳化法
  • 掃過整個IR, 看附近2 ~ n 個IR有沒有最佳化的機會
    • 感覺起來就像是用奧步
    • 可是如果奧步有用,那就得要好好使用
    • 在某些時候, 這個最佳化很好用, 尤其要生數據的時候 XD

;; subs rd, rn, #1

;; bcs dest ((unsigned)rn >= 1)

;; This is a common looping idiom (while (n--))

;; sub rd, rn, #1

;; cmn rd, #1 (equivalent to cmp rd, #-1)

;; bne dest

25 of 46

分很多個.c file or not 分很多個.c file

  • 為什麼要分很多檔案?
    • 因為要讓事情變得簡單
  • 為什麼我們在編譯程式的時候可以下 make -j24?
    • 因為編譯器在Compile 每個 .c 的時候視為獨立個體, 彼此之間沒有相依性
    • 用Compiler的術語稱之為 Compilation Unit
  • Static Global Variable vs Non-Static Global Variable
    • Static: 只有這個 Compilation Unit 可以看到這個變數
    • Non-Static: 所有Compilation Unit 可以看到這個變數
      • Q: 怎麼使用別的別的 Compilation Unit 內的變數
      • A: 使用 extern 關鍵字. EX: extern int i;

26 of 46

深入淺出 Compilation Unit

  • Compilation Unit 產生的限制
    • 關於沒有人使用的 Static Global Variable
      • Compiler 可以砍掉它來節省空間
    • 關於沒有人使用的 Non-Static Global Variable
      • Compiler 不能砍掉它
      • 因為不能確定別的 Compilation Unit 會不會用到它
  • Note: 如果確定沒有別的檔案會使用到這個變數或函式, 請宣告成 static

Compilation Unit

Internal Data

Visible Data

Internal Function

Visible Function

Internal Function

External Data Reference

External Func Reference

27 of 46

Compilation Unit�盡可能使用區域變數

main:

mov r3, #0

stmfd sp!, {r4, lr}

movw r4, #:lower16:.LANCHOR0

movt r4, #:upper16:.LANCHOR0

mov r1, r3

str r3, [r4, #0]

.L2:

ldr r0, .L4

bl printf

ldr r1, [r4, #0]

add r1, r1, #1

str r1, [r4, #0]

cmp r1, #9

ble .L2

ldmfd sp!, {r4, pc}

.L4:

.word .LC0

static int i;

int main()

{

for (i = 0 ; i < 10; i++)

printf ("Hello %d\n", i);

}

int main()

{

int i;

for (i = 0 ; i < 10; i++)

printf ("Hello %d\n", i);

}

main:

stmfd sp!, {r4, lr}

mov r4, #0

.L2:

mov r1, r4

ldr r0, .L4

add r4, r4, #1

bl printf

cmp r4, #10

bne .L2

ldmfd sp!, {r4, pc}

.L4:

.word .LC0

11

28 of 46

GCC 內建函式 (Built-in Function)

  • GCC 為了進行最佳化, 會辨認標準函式(因為語意有標準規範), 如果符合條件, 就會以處理器最適合的方式來計算
    • Strcpy => x86 字串處理指令
    • Printf => Puts, Putc
    • Memcpy => Block Transfer Instruction

  • 這對開發嵌入式系統的人常造成困擾
    • 因為開發嵌入式系統需要客製化的printf (O)
      • Note: 使用 -fno-builtin-XXX or -fno-builtin 來關閉這個功能

29 of 46

IPO (Inter-Procedural Optimization)

  • Procedure / Function: 結構化程式的主軸, 增加可用性, 減少維護成本
    • 副程式呼叫的執行成本
      • 準備參數: 把變數移到到特定 Register 或 push 到 Stack
      • 呼叫副程式, 儲存和回復 Register Context
    • 現代化程式: 有許多很小的副程式
      • int IsOdd(int num) {� return (num & 0x1);�}
  • 最簡單的 IPO: Function inlining
    • 簡單來說: 把整個副程式copy 一份到 caller 的程式碼去

30 of 46

IPO (Inter-Procedural Optimization)

  • 如何 Inline 一個 外部函式?
    • 基本上 Compiler 無法做到
      • 因為 Compiler 在編譯程式的時候不會去看別的 Compilation Unit 的內容, 不知道內容自然就沒辦法 inline 一個看不見的函式
  • 解法: Link Time Optimization (LTO)
    • 或叫: Whole-Program Optimization (WPO)
    • 關鍵 Link Time Code Generation

Source 1

Source 2

Source 3

Compiler

Compiler

Compiler

IR 1

IR 2

IR 3

Linker

Full IR

Compiler

Optimized Program

31 of 46

21世紀的另外一種選擇: LLVM

  • LLVM (以前稱為 Low Level Virtual Machine)
    • 用 “現代“ C++ 寫成的 Compiler infrastructure
    • 設計來進行全時最佳化 (compile-time, link-time, run-time, and idle-time optimizations)
  • LLVM 起源
    • 2000年UIUC Vikram Adve 與 Chris Lattner 的研究發展而成
  • 後來受到蘋果重用而大發異彩
    • 蘋果概念股 (?)
  • FreeBSD 10 將使用 Clang/LLVM 為預設的編譯器

32 of 46

LLVM: 全時最佳化編譯器平台

  • 知道原始碼的結構 (O)
  • 不知道變數的位址 (X)

空閒階段

執行階段

載入/安裝階段

連結階段

編譯階段

正所謂是

橫看成嶺側成峰

遠近高低各不同

  • 不知道原始碼的結構 (X)
  • 知道變數的位址 (O)
  • 不知道原始碼的結構 (X)
  • 不知道變數的位址 (X)
  • 知道處理器有甚麼能力 (O)

GNU Toolchain 的守備範圍

33 of 46

LLVM核心觀念: �LLVM is a Compiler IR

  • 既然 Compiler framework 就是要支援多種 Front-end 和 多種 Back-end
    • 使用 RISC-Like 平台無關的指令 (LLVM Instruction)和資料 (Metadata) 來表達原始碼
  • LLVM IR 的三變化 (等價形式)
    • In-Memory Form: JIT
    • Assembly language representation: ASM format (.ll)
    • Bitcode representation: Object code format (.bc)
  • 特點
    • Self-contained Representation 且有良好規範的 Compiler IR
    • 可以將 Compiler 到一半的結果序列化

34 of 46

�LLVM 的架構 : 從原始碼到二進制

C Front-End

C++ Front-End

LLVM bitcode

… Front-End

Stream-out

Optimized

LLVM bitcode

LLVM x86

Back-End

LLVM ARM

Back-End

X86 ASM

X86 Object

ARM ASM

ARM Object

Execution (JIT)

Execution (JIT)

In LLVM

Not in LLVM

LLVM Optimizer

BitCode �Passes

Selection DAG

Passes

Machine

Passes

LLVM C/C++

Back-End

C/C++ Source

Use LLVM Target

35 of 46

LLVM Toolchain (llvm-gcc)

  • 既然 LLVM 只是一個 Compiler-IR, 就代表 LLVM 在剛開始的時候是沒有辦法走完整個編譯流程的
  • LLVM-GCC (gcc-4.2)
    • 利用 GCC 已經存在而且眾多大眾使用的 Front-end
    • 後來 FSF出面拒絕這種方式 (GCC 4.3, GPLv3)
    • 後來 Apple 因此全面發展自己的 Front-end (Clang)

C Front-End

C++ Front-End

Generic

C Source

C++ Source

Gimple

Gimplify

LLVM Bitcode

NOTE: LLVM早期的也沒有自己的 Assembler (後來有 MC) 和 Linker 來建立完整編譯流程

36 of 46

LLVM Toolchain (Clang)

  • Clang : C, C++, Objective-C 的編譯器前端
    • Apple 為了取代 GCC 重新打造的 Frontend
    • 重寫一個前端代價是非常昂貴的
      • 其實你是 慣C? 還是 慣GCC?
      • GNU/Linux 世界使用了 GCC 行之有年的非標準語法
    • Clang 設計成模組化 Frontend : Clang C API
      • 可以 Export AST (Ex: RenderScript 用來產生 Java 的 Binding)
      • 可以用來實作自動完成, Refactor Tool … etc
      • 可以用來實作靜態程式碼分析工具 (Linting tool … etc)
    • Clang 改善了錯誤訊息 (Expressive diagnostics )
    • 快速編譯, 低記憶使用量, 容易學習和Hack的 Frontend

37 of 46

範例: Clang Expressive Diagnostics

xx.cc: In function 'void test(foo*)':

xx.cc:9:24: error: no match for 'operator+' in '(((a*)P) + ((sizetype)(*(long int*)(P->foo::<anonymous>.a::_vptr.a + -32u))))->a::bar() + * P'

xx.cc:9:24: error: return-statement with a value, in function returning 'void' [-fpermissive]

struct a {

virtual int bar();

};

struct foo : public virtual a {

};

void test(foo *P) {

return P->bar() + *P;

}

xx.cc:9:21: error: invalid operands to binary expression ('int' and 'foo')

return P->bar() + *P;

~~~~~~~~ ^ ~~

g++-4.7

clang-3.1

38 of 46

Clang/LLVM Toolchain

  • Clang Compiler Driver
    • 盡可能和 GCC 相容, 遇到不認識的Option不會停下來
    • 不需要 Cross Compiler, 本身就有 Cross Compiler 的能力 (?)
      • Cross Compiler != Cross Compiler Toolchian

CLANG

Language

Optimizer

C Source

C++ Source

LLVM Bitcode

AST Stream-out

Binding Script .. etc

來源: http://www.aosabook.org/en/llvm.html

39 of 46

LLVM 千秋萬世, 一統江湖

  • Q:挖賽, Clang + LLVM 那麼神, 其他人不是沒戲唱了嗎
    • A: 在這地球上還沒有人能統一天下, LLVM 當然也不行
  • Clang 之所以不用產生 Cross Compiler 的迷思
    • x86/64 clang 產生出來的LLVM Bitcode, 用 i686 的 llvm backend 來產生 x86 的機器碼 (-m32)
      • 可以產生出來, 可是不會動 XD
  • C/C++ 不是 Portable Language, 產生出來的 Bitcode 不 Portable 不是也是很正常的事嗎
    • LLVM Bitcode 本身就是 Portable (X)
    • 使用 host triple 來改變產生出來的 LLVM Bitcode (O)

滅諸侯,成帝業,為天下一統

40 of 46

LLVM TableGen (Target Description)

  • 編譯器Backend 都有一些很無聊的地方, 用另外一種語言來表示比直接寫 C/C++ 簡單好 maintain 的
    • GCC RTL Pattern : (MD, Machine Description file)
    • LLVM TableGen : (TD, Target Description file)

def ADDPS : PI<0x58,

(outs VR256:$dst),

(ins VR256:$src1, VR256:$src2),

"addps $src2, $src1, $dst",

[(set VR256:$dst, (fadd VR256:$src1, VR256:$src2))]>;

Instruction Encoding (For MC Assembler)

Input/Output

Assembly (For ASMPrinter)

Instruction Selection Pattern

(For Selection DAG)

41 of 46

完美Compiler進化論: LLVM的多變化

Source 1

Source 2

Source 3

Frontend

Frontend

Frontend

LLVM IR 1

LLVM IR 2

LLVM IR 3

LLVM Link

LLVM Optimizer

LLVM ARM Backend

IPOed executable/

Share object

Compile Time

Link Time

Source 1

Source 2

Source 3

Frontend

Frontend

Frontend

LLVM IR 1

LLVM IR 2

LLVM IR 3

LLVM Link

LLVM Optimizer

LLVM ARM Backend

Processor Specific

executable/

Share object

Compile Time

Link Time

Load /Installation Time

LLVM IR

Idle Time

LLVM Optimizer

Profile Data

LLVM ARM Backend

PGOed/ Runtime Opted

executable/ Share object

Load Time

42 of 46

LLVM 的發展

  • 最美好的時代, 最壞的時代
    • LLVM Bitcode 用來當傳遞格式還有很多問題
  • CASE STUDY: OpenCL SPIR
    • The OpenCL Khronos group proposes to extend LLVM to be the standard OpenCL IR (called SPIR)
    • Khronos SPIR For OpenCL Brings Binary Compatibility

43 of 46

相信你的編譯器

  • Q: x86 中把一個暫存器清成0
    • mov $0,%eax
    • and $0,%eax
    • sub %eax,%eax
    • xor %eax,%eax

  • 哪道指令最好?

44 of 46

相信你的編譯器: 解答

  • x86 的指令是可變長度的:
    • b8 00 00 00 00 mov $0,%eax
    • 83 e0 00 and $0,%eax
    • 29 c0 sub %eax,%eax
    • 31 c0 xor %eax,%eax

  • So, sub or xor? Turns out, both produce a false dependency on %eax. But CPUs know to ignore it for xor.

45 of 46

References

46 of 46

Any Question ?