* 紀錄一般知識,盡量不針對特定編譯器或硬體。可以舉例。
* 課程:
* [[http://web.stanford.edu/class/cs143/|CS143: Compilers]]
* [[https://www.cs.cmu.edu/~15745/|15-745 Optimizing Compilers for Modern Architectures]]
如何能深刻了解編譯器?
* [[https://www.amazon.com/Language-Implementation-Patterns-Domain-Specific-Programming/dp/193435645X/ref=pd_sbs_14_t_1?_encoding=UTF8&psc=1&refRID=R8WCCTA0AZH0AY4JJD27|Language Implementation Patterns]]
* 前端,直譯器相關。
* [[wp>Compilers: Principles, Techniques, and Tools]]
* 優化相關。前端不推薦。
* [[http://www.amazon.com/Engineering-Compiler-Second-Keith-Cooper/dp/012088478X|Engineering a Compiler, Second Edition]] - 入門
* [[http://www.amazon.com/Advanced-Compiler-Design-Implementation-Muchnick/dp/1558603204|Advanced Compiler Design and Implementation]] - 進階
* 寫一個編譯器或是參與現有編譯器的開發。
有用連結:
* [[http://compilers.iecc.com/index.phtml|comp.compilers newsgroup]]
* [[http://coolshell.cn/articles/1547.html|使用Flex Bison 和LLVM编写自己的编译器]]
* [[http://www.codeopt.com/|Search: Compilers / Optimization]]
* [[http://llvm.org/devmtg/2012-04-12/Slides/Workshops/David_Chisnall.pdf|What LLVM can do for you]]
$ wget http://llvm.org/devmtg/2012-04-12/Slides/Workshops/memo.cc
$ wget http://llvm.org/devmtg/2012-04-12/Slides/Workshops/examples.tbz2
====== 編譯流程 ======
* [[wp>Compiler]]
* 源代碼 (Source Code) -> 語彙分析 (Lexical Analysis) -> 語法分析 (Syntax Analysis) -> 語意分析 (Semantic Analysis) -> 生成中間表示式 (IR Generation) -> 優化中間表示式 (IR Optimization) -> 生成目標代碼 (Code Generation) -> 優化目標代碼 (Optimization) -> 目標代碼 (Machine Code)
===== 前端 =====
* [[https://www.amazon.com/Language-Implementation-Patterns-Domain-Specific-Programming/dp/193435645X|Language Implementation Patterns]]
* [[Lex & Yacc]]
* [[http://avnpc.com/pages/devlang|《自制编程语言》相关资料]]
* [[https://github.com/azru0512/win_sjis|win_sjis]]
* 重點著重在如何建立 AST。前端會遍歷 AST 多次,每次遍歷求得的資訊會記在 AST 節點上, 供下一次遍歷使用。
* [[http://yinwang0.lofter.com/post/183ec2_47c086|怎样写一个解释器 - 王垠]]
* [[wp>Parsing]]
* The grammar cannot remember the presence of a construct over an arbitrarily long input; this is necessary for a language in which, for example, a name must be declared before it may be referenced. More powerful grammars that can express this constraint, however, cannot be parsed efficiently. Thus, it is a common strategy to create a relaxed parser for a context-free grammar which accepts a superset of the desired language constructs (that is, it accepts some invalid constructs); later, the unwanted constructs can be filtered out at the semantic analysis (contextual analysis) step.
* [[http://stackoverflow.com/questions/1078908/what-is-the-difference-between-keyword-and-reserved-word|What is the difference between “keyword” and “reserved word”?]]
* [[wp>Reserved word]]
* In a computer language, a reserved word (also known as a reserved identifier) is a word that cannot be used as an identifier, such as a name of a variable or function or a label – it is "reserved from use". This is a syntactic definition, and a reserved word may have no meaning. This is a syntactic definition, and a reserved word may have no meaning. A closely related and often conflated notion is a keyword which is a word with special meaning; this is a semantic definition.
* [[http://stackoverflow.com/questions/3846727/why-is-the-difference-between-an-expression-and-a-statement|Why is the difference between an expression and a statement]]
* [[wp>Expression (computer_science)|Expression]]
* [[wp>Statement (computer science)|Statement]]
* 表達式 (expression) 和述句 (statement) 主要差別在於前者產生値,而後者產生動作。後者可藉由前者產生的值,進行賦値或是流程控制。
* [[JTC1/SC22/WG14 - C]]
* 閱讀標準,理解用詞。
* [[SDCC]]
* [[JTC1/SC22/WG21 - C++]]
* [[wp>Data type]]
* [[wp>Primitive data type]]
* 基本型別。下述衍生型別基本上都是以基本型別建構而成。
* [[wp>Composite data type]]
* 複合型別。
* [[wp>Reference type]]
* 引用類型。Java 裡面除基本型別 ([[wp>Primitive data type]]) 之外,皆為引用類型。
* [[wp>Dangling else]]
* else 如何與 if 配對。
* [[http://blog.robertelder.org/building-a-c-compiler-type-system-the-formidable-declarator/|Building A C Compiler Type System - Part 1: The Formidable Declarator]]
* [[http://blog.robertelder.org/building-a-c-compiler-type-system-a-canonical-type-representation/|Building A C Compiler Type System - Part 2: A Canonical Type Representation]]
===== 產生中間語言 =====
請見 [[http://www.stanford.edu/class/cs143/lectures/130_TAC.pdf|Three-Address Code IR]]。
對於中間語言常用的格式,[[wp>Static single assignment form|Static single assignment form (SSA)]],只需要知道底下幾點:
* 每個變量只被賦值一次 (類似函數式編程中 immutable 的概念)。
* 一般程序轉換成 SSA form 時,會針對控制流插入 Phi Node。
* Phi Node 在 LLVM 中被實現為一條 LLVM IR ([[http://stackoverflow.com/questions/11485531/what-exactly-phi-instruction-does-and-how-to-use-it-in-llvm|What exactly PHI instruction does and how to use it in LLVM]])。
* SSA form 簡化後續優化所需實現。
延伸閱讀:
* [[http://ssabook.gforge.inria.fr/latest/book.pdf|SSA Book]]
===== 優化中間語言 =====
請見 [[http://www.stanford.edu/class/cs143/lectures/140_IR_Optimization.pdf|IR Optimization]]、[[http://www.stanford.edu/class/cs143/lectures/150_Global_Optimization.pdf|Global
Optimization]] 和 [[http://www.stanford.edu/class/cs143/lectures/160_Global_Optimization_2.pdf|Global Optimization, Part II]]。
===== 生成目標代碼 =====
Code Generation 包含以下步驟:
- [[wp>Instruction selection]]
- [[wp>Register allocation]]
- [[wp>Instruction scheduling]]
可以參考 LLVM 的流程 [[http://www.llvm.org/docs/CodeGenerator.html#high-level-design|The high-level design of the code generator]]。
* [[http://staff.science.uva.nl/~andy/compiler/opt+backend.pdf|Introduction to Compiler Design: optimization and backend issues]]
==== 指令選擇 ====
Instruction selection 將 IR 轉換成 machine code,或是低階的 IR (接近目標機器碼),但仍使用 pseudo register,留待之後的 register allocation 將目標機器的暫存器分配給 pseudo register。Instruction selection 一般是將 machine code 模板貼至 IR tree 上,稱為 tiling。一般來說,會將 IR tree 轉成 [[wp>Directed acyclic graph|DAG]] (directed acyclic graph)。Tree 和 DAG 不同之處在於,節點在 tree 中只有一個父節點,而在 DAG 中可以有多個父節點。DAG 這個特性用來將 common subexpression 暴露出來。若一個節點為 common subexpression,在 tree 中會出現多次,而在 DAG 只會出現一次。DAG 有助於後續的 instruction selection 和 register allocation。
見 [[http://www.amazon.com/gp/product/0321486811/ref=pd_lpo_k2_dp_sr_1?pf_rd_p=1278548962&pf_rd_s=lpo-top-stripe-1&pf_rd_t=201&pf_rd_i=0441017649&pf_rd_m=ATVPDKIKX0DER&pf_rd_r=02WFWGQTZ4SV9K8QQM5C|Compilers: Principles, Techniques, and Tools]] 第 8.9 節: Instruction Selection by Tree Rewriting。
* [[http://www.inf.uni-konstanz.de/dbis/teaching/ws0203/compiler-construction/download/comp-08.pdf|Instruction Selection]]
* [[http://www.lrde.epita.fr/dload/20040602-Seminar/vasseur0604_code-gen-gen_report.pdf|Code-generator generators: generating the instruction selector]]
* Optimal code selection in DAGs
* [[http://cs.tju.edu.cn/faculty/hujing/%E4%B8%80%E4%BA%9B%E8%99%8E%E4%B9%A6%E7%9A%84%E5%8F%82%E8%80%83/PDF&PPT/Advanced%20topics%20More%20Instruction%20Selection.pdf|More Instruction Selection]]
* Compilers: Principles, Techniques, and Tools - 8.9
LLVM 使用的是 SelectionDAG based instruction selector。利用 tblgen 生成 instruction selector。
* Scheduling expression DAGs for minimal register need
* Optimal Integrated Code Generation for Clustered VLIW Architectures
* [[wp>Sethi–Ullman algorithm]]
* [[http://comments.gmane.org/gmane.comp.compilers.llvm.devel/46730|Sethi-Ullman Scheduling]]
* lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp ([[http://www.llvm.org/docs/CodeGenerator.html#selectiondag_sched|SelectionDAG Scheduling and Formation Phase]])
==== 暫存器配置 ====
兩個述句 (statement) 之間的點被稱為 program point。一個變數被 define,代表該變數被寫入值; 一個變數被 use,代表該變數的值被讀出。針對變數 a 而言,
---------- program point
a = 1; // define
----------
b = a; // use
----------
一個變數被稱為 live,代表該變數其值在將來會被使用到,亦即在某個時間點被讀出。一個變數的 live range 是一個 program point 的集合,該變數在這些 program ponit 為 live。一個變數的 live interval 是一個最小區間,該區間包含該變數所有的 live range。live interval 相較於 live range 較不精確,但是其 register allocation 較為簡單。[[http://www.stanford.edu/class/cs143/lectures/170_Register_Allocation.pdf|Register Allocation]] 第 64 頁演示的 linear scan 算法就是針對各個變數的 live interval 配置暫存器。編譯器在做 register allocation 時,會視情況針對某變數做 live range splitting,其目的是希望在 live range splitting 之後,能減少各變數之間 live range 重疊的情況。如果兩個變數的 live range 有所重疊,則代表這兩個變數無法分配到同一個暫存器,這會增加暫存器使用量。
[[wp>Register allocation]] 包含兩個步驟: 首先,register allocation 釐清哪些變數將可存入暫存器; 接著,register assignment 將物理暫存器分配給那些變數。如果途中發現暫存器不敷使用,register allocation 演算法會選擇將某些暫存器存入內存,這個動作稱之為 spill。register allocation 演算法目標之一就是要降低 spill 的次數。一般來說,register allocation 演算法會選擇把擁有較長 live interval 的變數 spill 到內存。這是因為如果將暫存器分配給該變數,則該暫存器將有較長時間不能再被使用。所以,一般會將暫存器優先分配給 live interval 較短的變數。有時候,如果某暫存器其中的值可以在之後簡單的計算出來,算法可以選擇 spill 該暫存器,且之後不須從內存將其值讀出,只需要重新計算即可,這稱之為 [[wp>Rematerialization|rematerialization]]。
相較於 linear scan 是利用 live interval 此一較不精確的描述來分配暫存器,[[http://www.stanford.edu/class/cs143/lectures/170_Register_Allocation.pdf|Register Allocation]] 第 109 頁演示的 graph coloring 算法利用 live range 分配暫存器。每個變數代表一個節點。如果變數 A 和變數 B 的 live range 有所重疊,則節點 A 和 B 之間有連線。針對各個變數的 live range 所建立的圖稱為 register interference graph (RIG)。此算法目的是要替 RIG 上的節點著色 (代表暫存器),有連線的節點必須著以不同的顏色。
根據 register allocation 的範圍,可分為 local register allocation 和 global register allocation。前者針對一個 basic block 分配暫存器給各個變數,後者則針對數個 basic block。
LLVM 3.0 預計以 greedy register allocation 取代現行的 linear scan 算法。我想你現在具有足夠知識開始閱讀文章 [[http://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html|Greedy Register Allocation in LLVM 3.0]] 了。:-) LLVM 3.0 有兩個 register allocator,分別是 basic allocator 和 greedy allocator。兩者都是根據變數的 live range 分配暫存器。它們都有兩個重要的資料結構: priority queue 和 live interval unions。Priority queue 以變數的 live range 的 spill weight 由高到低排序,將暫存器優先配給 spill 代價較高的變數。Live interval unions 存放的是已經分配到暫存器的 live range,目的是用來檢查某暫存器分配給那些 live range。底下略為描述 basic allocator 和 greedy allocator 的運作方式。
- Basic allocator: 由 priority queue 中挑出 spill 代價最高的 live range,在此稱之為 live range A,試圖配置暫存器給它。如果成功,將配置結果更新到 live interval unions; 否則,則 spill live range A。這裡不需要檢查 live interval unions 選擇 spill 其它 live range,因為我們優先配置暫存器給 spill 代價較高的 live range。一般是透過插入 load/store 指令 spill 某個 live range,這會造出新的 live range (在 load 跟 store 之間)。這一個因 spill 造出的 live range 會塞入 priority queue 等待下一次分配,其 spill weight 設為無限大。等到下一次為該 spill weight 為無限大的 live range A 分配暫存器時,basic allocator 會選擇一暫存器,並檢視 live interval unions 看該暫存器分配給那些 live range,亦即與那些 live range 有 interfering。Basic allocator spill 那些倒楣的 live range,再將該暫存器配給該 live range A。
- Greedy allocator: 與 basic allocator 的差別在,當沒有適當的暫存器可供分配給 live range A,greedy allocator 會由 live interval unions 中挑出 spill weight 較小的 live range B 並 spill 它 (稱為 evict)。Live range B 會被塞回 priority queue 等待下一次分配。注意! 在 basic allocator 中,不會挑 live interval unions 中 spill weight 較小的 live range 做 spill。此外,geedy allocator 會在 spill live range 之前先試著做 live range splitting,將 live range 分成較小塊的 live range,再將它們塞回 priority queue。
* [[http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-September/043322.html|[LLVMdev] Greedy Register Allocation in LLVM 3.0]]
* [[http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-December/046073.html|[LLVMdev] Greedy Register Allocation in LLVM 3.0]]
* [[http://llvm.org/devmtg/2011-11/Olesen_RegisterAllocation.pdf|Register Allocation in LLVM 3.0]]
* [[http://compilers.cs.ucla.edu/fernando/publications/drafts/survey.pdf|A Survey on Register Allocation]]
* [[http://www.cs.cmu.edu/~dkoes/research/dkoes_thesis.pdf|Towards a More Principled Compiler: Register Allocation and Instruction Selection Revisited]]
* [[wp>Strahler number]]
* [[wp>Ershove Number]]
==== 指令排程 ====
請見 [[http://www.stanford.edu/class/cs143/lectures/190_Code_Optimization.pdf|Code Optimization]]。
Instruction scheduling 和 register allocation 的順序如何調整及其優缺點請見 [[wp>Instruction_scheduling#The_phase_order_of_Instruction_Scheduling|The phase order of Instruction Scheduling]]。
* instruction scheduling 目的在於提高 ILP。
* instruction scheduling 在 register allocation 之前的優點在於 ILP 能最大化。因為在 register allocation 之前,使用的是虛擬暫存器 (數量無限),多條指令可以並行; 但這也是缺點,ILP 最大化之後,會對 register allocation 造成壓力,多條指令執行可能會使用超過物理暫存器數量的暫存器,register allocation 必須做 spill。
* instruction scheduling 在 register allocation 之後的優缺點和上面剛好相反。
* LLVM 在 register allocation 前後都會做 instruction scheduling。
====== Optimization ======
* [[http://www.valleytalk.org/wp-content/uploads/2011/11/%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90-%E5%8E%9F%E7%90%86%E9%83%A8%E5%88%86%E5%85%A8.pdf|程序分析-原理部分(全)]]
* [[wp>Dominator|Dominator/Post-dominator]]
* [[wp>Dependence_analysis#Control_dependencies|Control Dependence]]
* [[http://www.freescale.com/infocenter/Codewarrior/index.jsp?topic=/com.freescale.doc.mcu.coldfire_compiler/220_opt.Live_Range_Splitting.html|Intermediate Optimizations ]]
* [[http://lists.cs.uiuc.edu/pipermail/llvmdev/2014-October/078222.html|[LLVMdev] Switch instruction lowering]]
* [[wp>Multiway branch]]
* [[http://llvm.org/pubs/2007-05-31-Switch-Lowering.pdf|Improving Switch Lowering for The LLVM Compiler System]]
* A Superoptimizer Analysis of Multiway Branch Code Generation
====== Link Time Optimization======
* [[wp>Link-time optimization]]
====== Exceptio Handling ======
目前所見的例外處理其實作上基本遵循 [[http://www.codesourcery.com/cxx-abi/abi-eh.html|Itanium C++ ABI: Exception Handling]]。它實現所謂的 zero-cost exception handling,也就是例外處理不會造成程式正常執行流程效能上的影響。[(http://stackoverflow.com/questions/307610/how-do-exceptions-work-behind-the-scenes-in-c#307716)]
personality function/routine 是語言執行時期函示庫中的函式,用來作為 system unwind library 和 language-specific exception handling semantics 的介面。這是因為不同的程式語言對於例外處理有不同的行為,personality function 接受例外的內文,包含其型別和值。
* [[http://luse.blogspot.tw/2009/05/c.html|淺談C++例外處理 (前篇)]]
* [[http://luse.blogspot.tw/2009/10/c.html|淺談C++例外處理 (中篇)]]
* [[http://luse.blogspot.tw/2010/01/c.html|淺談C++例外處理 (後篇)]]
===== LLVM =====
LLVM 3.0 針對例外處理主要提供以下 LLVM IR/Intrinsics 支援。前端在處理程式語言中的例外處理時,可以將其對映到 LLVM 中用來支援例外處理的指令,再交由之後的優化器和後端處理。底下將會以 C++ 中例外處理的 try/catch/throw 當作對映。
- invoke: 針對在 try block 中的函式呼叫,改用 invoke 而非 call。如果在一個會丟出例外的函式中呼叫另一個函式,同樣也是用 invoke。
- landingpad: 例外發生之後,會跳轉到一個特殊的基本塊。在這個基本塊中,landingpad 用來準備之後例外處理需要的資料。
- resume
- llvm.eh.typeid.for
以 [[http://llvm.org/devmtg/2011-09-16/EuroLLVM2011-ExceptionHandling.pdf|The new LLVM exception handling scheme]] 說明 LLVM 如何實現例外處理。
try {
...
MayThrowSomething();
AnotherFunctionCall();
...
} catch (int i) {
DoSomethingWithInt(i);
} catch (class A a) {
DoSomethingWithA(a);
}
其 try block 對映的 LLVM IR 為,
invoke void @_Z17MayThrowSomethingv() to label %cont unwind label %lpad
我們可以看到在 try block 中的函式呼叫改用 invoke,其分支目標有二: label %cont 和 unwind label %lpad,分別為正常執行流程和例外處理流程。
lpad:
%info = landingpad { i8*, i32 } personality @__gxx_personality_v0 catch @_ZTIi catch @_ZTl1A
%except = extractvalue { i8*, i32 } %info, 0
%selector = extractvalue { i8*, i32 } %info, 1
%typeid = call i32 @llvm.eh.typeid.for(@_ZTIi)
%match = icmp eq i32 %selector, %typeid
br i1 %match, label %run_catch, label %try_next
landingpad 是用來準備例外資訊給後面的例外處理程序使用。landing pad (lpad) 中文意思是停機坪,這代表當例外發生時,執行流程會跳轉到這裡,再轉發到其它地方。%info 是該例外的資訊,{ i8*, i32 } 是其型別。其中 i8* 是 exception pointer,i32 是 selector。personality @__gxx_personality_v0 代表目前處理的是 C\+\+ 的例外,它知道如何將該例外與 catch clause 作 C\+\+ 型別比較。catch @_ZTIi 和 catch @_ZTl1A 則是列出所有的 catch clause,@_ZTIi 和 @_ZTl1A 是語言特定的全域變數,這裡用來代表 catch clause 的 C\+\+ 型別資訊。
* [[http://www.llvm.org/docs/ExceptionHandling.html|Exception Handling in LLVM]]
* [[http://www.llvm.org/docs/LangRef.html|LLVM Language Reference Manual]]
* [[http://blog.llvm.org/2011/11/llvm-30-exception-handling-redesign.html|LLVM 3.0 Exception Handling Redesign]]
* [[http://www.cplusplus.com/doc/tutorial/exceptions/]]
* [[http://stackoverflow.com/questions/329059/what-is-gxx-personality-v0-for]]
* [[http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-July/041748.html]]
* [[Binutils]]
* [[Exception handling]]
====== 術語 ======
* [[wp>Clobbering]][(http://www.cs.nctu.edu.tw/~chenwj/log/IM/zakk-clobber-2011-06-28.txt)]
* clobbered register 代表該條指令執行時,其內容會被修改的暫存器。
* Register scavenging [(http://lists.llvm.org/pipermail/llvm-dev/2017-April/111696.html)]
* 在 register allocation 之後的 pass,如果需要新的 register,會做 register scavenging 看附近有沒有暫時沒被用到的 register。如果都沒有,就只能做 spill。
* Stack slot
* [[http://stackoverflow.com/questions/3232614/how-does-an-optimizing-c-compiler-reuse-stack-slots-of-a-function|how does an optimizing c++ compiler reuse stack slots of a function?]]
* [[wp>Rematerialization]]
* 將所需的值重新計算,避免對內存的存取。可以看做是 spill 的反向動作。
* [[Sibling call]]
* [[wp>Partial evaluation]]
* Futamura projections[(http://people.cs.nctu.edu.tw/~chenwj/log/IM/chapuni64-2012-11-30.txt)]
* [[wp>Fixed-point arithmetic]]
* [[http://www-inst.eecs.berkeley.edu/~cs61c/sp06/handout/fixedpt.html|Introduction: Real numbers in Real world]]
* [[https://www.dsprelated.com/showarticle/139.php|A Fixed-Point Introduction by Example]]
* (二進制) 小數點前後的位數固定。一般用 Qm.f 表示,其中 m 表示整數位,f 表示小數位,另外隱藏一位有號位 (signed bit)。
* 固定點運算可以先將小數點右移至底,將其當作一般整數運算,再將小數點左移。例如: 兩個 Q7.24 相乘 (Q7.24 加上有號位,總共 32 位)。
- widen the i32s to i56s
- multiply
- shift right 24
- truncate to i32
* [[wp>Saturation arithmetic]]
* 飽和運算。運算結果有最大最小值,高於最大值以最大值計,低於最小值以最小值計,前述行為被形容為飽和。
* 有理數/分數 (rational number/fractional number)
* 實數 (real number)
* [[https://stackoverflow.com/questions/32002157/how-to-understand-volatile-and-non-volatile-registers|How to understand volatile and non-volatile registers?]]
* [[https://msdn.microsoft.com/en-us/library/6t169e9c.aspx|Caller/Callee Saved Registers]]
* caller-saved register 是調用方在調用函式之前需要保存的暫存器,因為這些暫存器在調用函式之後,其內容會被揮發 (破壞) 掉,故又稱 volatile register。
* callee-saved register 是被調用方在使用之前和之後需要保存和回復的暫存器。因為這些暫存器在調用函式之後,其內容會不變,故又稱 non-volatile register。
===== 硬件平台相關 =====
* [[wp>VLIW]]
* 非常依賴編譯器的軟流水優化。
* Hardware Loop
* [[http://www.eetimes.com/document.asp?doc_id=1275609|Architecture-oriented C optimization, part 1: DSP features]]
* 一般 loop 在尾部透過 compare and branch 決定是否要繼續執行 loop。branch 指令會打斷 pipeline。
* 增加硬件紀錄 loop counter 和 loop start/end。
* [[http://llvm.org/devmtg/2011-11/Simpson_PortingLLVMToADSP.pdf|Porting LLVM to a Next Generation DSP]]
* 一般 DSP 非常依賴 ILP,透過 SMT,VLIW 和 SIMD 達成。
* [[wp>Delay Slot]]
* Branch delay slot
* [[http://www.pagetable.com/?p=313|Having Fun with Branch Delay Slots]]
* 在 branch 指令之後的 pipeline stage 填入不論 branch taken 或是 not taken 都會執行的指令。屬於編譯器軟流水優化。
* [[wp>Literal pool]][(http://www.cs.nctu.edu.tw/~chenwj/log/IM/zakk-literal-pool-2011-10-05.txt)]
* ARM 相關。
* [[http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0473c/Bgbccbdi.html|Literal pools]]
* [[http://benno.id.au/blog/2009/01/02/literal-pools|The trouble with literal pools]]
===== 代碼分析相關 =====
* [[wp>Control flow graph]]
* 以下的程序分析都發生在 control flow graph。
* [[wp>Dominator (graph theory)|Dominator]]
* 分析在 control flow graph 中,節點之間的關係。如果所有從 entry node 出發到 node n 的路徑,都需要經過 node d,則稱 d dominate n。每個 node 都 dominate 自己。{{https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Dominator_control_flow_graph.svg/400px-Dominator_control_flow_graph.svg.png?210x240}}
* 1 dominate 3。所有從 1 出發到 3 的路徑,都需要經過 1。
* 1/2 strictly dominate 3。1/2 dominate 3 且 1/2 不等於 3。
* immediate dominator of 3 is 2。2 strictly dominate 3,且 2 沒有 strictly dominate 其它 strictly dominate 3 的 node; 相反的,1 strictly dominate 3,但是 1 strictly dominate 2,而 2 又 strictly dominate 3。
* 單純從字面上理解 immediate dominator 應該也可以。
* dominance frontier of n
* frontier 是前沿。dominance frontier of n,是一個集合,集合中的節點,其 immediate predecessor 被 n 所 dominate,但節點本身不被 n 所 dominate。
* 用來決定在 CFG 哪些地方設置 [[wp>Static single assignment form|SSA]] 所需的 Phi node。
* 一個節點 n,假設引用到變量 v。如果 n 屬於兩個 dominance frontier 的交集,且這兩個 dominance frontier 各自都定義變量 v,則在節點 n 之前插入 Phi node。
* dominator tree
* tree 中每一個節點 n 的子節點 m,immediate dominator of m is n。
* [[wp>Data-flow analysis]]
* 分析程序在各個不同的點,屬性的變化。屬性的變化,可以從 CFG 由前往後,或是後往前推導。
* dead code elimination 所需的 live variable analysis 屬於 backward analysis。
* [[wp>Live variable analysis]]
===== 代碼優化相關 =====
附上 LLVM 相關代碼。
* [[https://en.wikipedia.org/wiki/Alias_analysis#Type-based_alias_analysis|Type-Based Alias Analysis (TBAA)]]
* [[http://www.drdobbs.com/cpp/type-based-alias-analysis/184404273|Type-Based Alias Analysis]]
* 假設不同型別的指針所指向的內存空間不重疊,並據此做優化。
* [[wp>Tail call]]
* 可以用迭代實現的遞迴。請見 [[https://xuanji.appspot.com/isicp/1-2-procedures.html|Procedures and the Processes They Generate]]。
* [[wp>Dead code elimination|Dead code elimination (DCE)]]
* 消除未被使用的表達式。
* [[http://llvm.org/docs/doxygen/html/DCE_8cpp_source.html|DCE.cpp]]
* [[wp>Dead store|Dead store elimination (DSE)]]
* 如果一個變量被賦值之後,其值未被使用,則為 dead store。
* [[http://llvm.org/doxygen/DeadStoreElimination_8cpp_source.html|DeadStoreElimination.cpp]]
* [[wp>Global value numbering|Global value numbering (GVN)]]
* 為變量和表達式編號。兩個變量或表達式具有相同編號,代表其中之一可以被消除。和 CSE 是互補的優化。
* [[http://blog.llvm.org/2009/12/introduction-to-load-elimination-in-gvn.html|Introduction to load elimination in the GVN pass]]
* [[http://llvm.org/doxygen/GVN_8cpp_source.html|GVN.cpp]]
* [[wp>Common subexpression elimination|Common subexpression elimination (CSE)]]
* 將表達式中重複的部分提出,避免重複計算。
* [[wp>Loop-invariant code motion|Loop-invariant code motion (LICM)]]
* 將迴圈運算中,不會改變的變數提升至迴圈外,避免重複計算。
* [[http://llvm.org/docs/doxygen/html/LICM_8cpp_source.html|LICM.cpp]]
* Scalar Replacement of Aggregates (SRoA)
* [[https://blog.regehr.org/archives/1603|How LLVM Optimizes a Function]]
* 如果 aggregate [(http://stackoverflow.com/questions/35722514/what-is-the-difference-between-scalar-types-and-aggregate-types-in-c)] (如: struct) 中的 field 沒有 alias,則將該 field 當成 scalar 處理,以利後續優化。底下是經過 LLVM ''opt'' 優化後的 IR。
// sroa.c
typedef struct {
int x;
int y;
} A;
int func() {
A a = { .x = 1, .y = 2 };
// int a.x.sroa = a.x;
// int a.y.sroa = a.y;
return a.x + a.y;
}
; clang -O0 -emit-llvm -S sroa.c -o sroa_O0.ll
define i32 @func() #0 {
entry:
%a = alloca %struct.A, align 4
%0 = bitcast %struct.A* %a to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* bitcast (%struct.A* @func.a to i8*), i64 8, i32 4, i1 false)
%x = getelementptr inbounds %struct.A, %struct.A* %a, i32 0, i32 0
%1 = load i32, i32* %x, align 4
%y = getelementptr inbounds %struct.A, %struct.A* %a, i32 0, i32 1
%2 = load i32, i32* %y, align 4
%add = add nsw i32 %1, %2
ret i32 %add
}
; opt -sroa sroa_O0.ll -S -o sroa.ll
define i32 @func() #0 {
entry:
%a.sroa.0.0.copyload = load i32, i32* getelementptr inbounds (%struct.A, %struct.A* @func.a, i64 0, i32 0), align 4
%a.sroa.2.0.copyload = load i32, i32* getelementptr inbounds (%struct.A, %struct.A* @func.a, i64 0, i32 1), align 4
%add = add nsw i32 %a.sroa.0.0.copyload, %a.sroa.2.0.copyload
ret i32 %add
}
* [[https://gcc.gnu.org/wiki/summit2010?action=AttachFile&do=get&target=jambor.pdf|The new intraprocedural Scalar Replacement of Aggregates]]
* [[http://llvm.org/doxygen/SROA_8cpp_source.html|SROA.cpp]]
* [[http://llvm.org/devmtg/2009-10/ScalarEvolutionAndLoopOptimization.pdf|ScalarEvolution and Loop Optimization]]
* SCEV (Scalar Evolution)
* [[https://gcc.gnu.org/onlinedocs/gccint/Scalar-evolutions.html|15.5 Scalar evolutions]]
* 將隨著每個迭代有序變化的變量 (induction variable) 以 SCEV 表示。
* [[http://www.cs.cmu.edu/afs/cs/academic/class/15745-f03/public/lectures/L7_handouts.pdf|Loop Invariant Computation and Code Motion]]
* [[https://gcc.gnu.org/onlinedocs/gccint/Loop-representation.html|15.1 Loop representation]]
* back edge (d -> a),a dominate d。又叫 latch。
* natural loop of a back edge。包含 back edge 頭尾兩個節點,構成迴圈的最小集合。
* LLVM 會先做 [[http://llvm.org/doxygen/LoopSimplify_8cpp_source.html|LoopSimplify.cpp]] 將 natural loop 正規化,方便後續處理。
* 插入 pre-header。確保外部進入 loop 只有一個入口點,即 pre-header。
* 插入 exit-block。確保離開 loop 只有一個出口點,即 exit-block。
* 確保只有一條 back edge。
* 以 LICM 為例,會把 loop invariant 從 loop body 提升至 pre-header。
* Vectorization
* [[http://llvm.org/docs/Vectorizers.html|Auto-Vectorization in LLVM]]
* [[http://en.cppreference.com/w/c/language/restrict|restrict type qualifier]]
* [[https://llvm.org/devmtg/2017-02-04/Restrict-Qualified-Pointers-in-LLVM.pdf|Restrict-qualified pointers in LLVM]]
===== 代碼保護相關 =====
* [[stack-smashing protector]]
* 堆棧溢出保護
====== 知名編譯器 ======
* [[http://nuwen.net/mingw.html|MinGW]]
* [[http://www.edg.com/index.php|EDG]]
* [[http://www.comeaucomputing.com/|Comeau C/C++]]
* [[wp>Microsoft Visual Studio]]
* [[http://people.cs.nctu.edu.tw/~chenwj/slide/How%20we%20bring%20up%20%20a%20new%20target%20-%20Copy%20for%20%e9%99%b3%e9%9f%8b%e4%bb%bb.ppt|How We Bing Up a New Target]]
* Od compilation 即是 -O0。
* CRT: C runtime 函式庫。
* [[https://www.iar.com/|IAR]]