├── LLVM ├── 20081205_HowToUseJIT&MakeJITSupport_chenwj.pdf ├── GoogleMock for Dummies.ppt ├── GoogleTestPrimer.ppt ├── How To Use JIT.ppt ├── LLVM-RDF-01.md ├── LLVM-exception-handling-01.txt ├── LLVM-exception-handling-02.txt ├── LLVM-introduction-01.txt ├── LLVM-introduction-02.txt ├── LLVM-introduction-03.txt ├── LLVM-register-allocation-00.txt ├── LLVM-register-allocation-01.txt ├── LLVM-register-allocation-02.txt ├── LLVM-register-allocation-03.txt └── LLVM-release-testing.txt ├── MISC ├── ExceptionHandling-00.md └── Why-We-Created-Julia.txt ├── QEMU ├── KVM-allocate-vmcs.txt ├── KVM-flow.txt ├── KVM-handle-KVM_RUN.txt ├── KVM-handle-guest-io-instruction.txt ├── KVM-introduction-01.txt ├── KVM-introduction-02.txt ├── KVM-introduction-03.txt ├── KVM-introduction-04.txt ├── KVM-introduction-05.txt ├── KVM-mmu.txt ├── KVM-module-entry.txt ├── QEMU - block chaining.ppt ├── QEMU - cpu-interrupt.ppt ├── QEMU-access-guest-memory-01.txt ├── QEMU-access-guest-memory-02.txt ├── QEMU-access-guest-memory-flow.txt ├── QEMU-block-chaining-01.txt ├── QEMU-block-chaining-02.txt ├── QEMU-block-chaining-03.txt ├── QEMU-block-chaining-limit-01.txt ├── QEMU-block-chaining-limit-02.txt ├── QEMU-block-chaining-limit-chart.txt ├── QEMU-block-unchaining-01.txt ├── QEMU-handle-self-modifying-code-01.txt ├── QEMU-handle-self-modifying-code-02.txt ├── QEMU-io-emulation.txt ├── QEMU-memory.txt ├── QEMU-precise-exception-01.txt ├── QEMU-precise-exception-02.txt ├── QEMU-precise-exception-03.txt ├── QEMU-precise-exception-04.txt ├── QEMU-precise-exception-05.txt ├── QEMU-softmmu-01.txt ├── QEMU-softmmu-02.txt ├── QEMU-system-mode-01.txt ├── QEMU-tcg-01.txt ├── QEMU-tcg-02.txt ├── QEMU.txt ├── QEMU_Mascot.png ├── QEMU_Mascot.svg ├── QEMU_Mascot_embody_text.svg ├── QEMU_Mascot_embody_text_rotate.svg ├── QEMU_Mascot_no_text.svg ├── QEMU_Mascot_text_right.svg ├── QEMU_Mascot_v2.svg ├── QEMU_Mascot_v3.svg ├── Tutorial.ppt ├── qemu-part1.htm ├── qemu-part2.htm ├── qemu-part3.htm └── zruan0-2012-07-16.txt ├── README.md └── Talk ├── hellogcc-2012 ├── Makefile ├── hellogcc-2012-chenwj.pdf └── hellogcc-2012-chenwj.tex └── mclinker-2013 ├── CaseStudy.pptx ├── MCLinker-IR.pptx ├── Makefile ├── agenda.pptx ├── code ├── builder.cc ├── builder.cc.tex ├── builder_fib.ll ├── builder_fib.ll.tex ├── cmdline.sh ├── fib.c ├── fib.c.tex ├── fib.c.tex.aux ├── fib.ll ├── fib.ll.tex ├── fib.s ├── fib.simply.ll ├── fib.simply.ll.tex ├── hello_0.c ├── hello_1.cc ├── hello_1.cc.tex ├── hello_1.cpp.tex ├── hello_1.ll ├── hello_1.ll.tex ├── hello_2.cc ├── hello_2.cc.tex ├── hello_2.ll ├── hello_2.ll.tex ├── hello_3.cc ├── hello_3.cc.tex ├── hello_3.ll ├── hello_3.ll.tex ├── hello_4.cc ├── hello_4.cc.tex ├── hello_4.ll ├── hello_4.ll.tex ├── hello_5.cc ├── hello_5.cc.tex ├── hello_5.ll ├── hello_5.ll.tex ├── hello_6.cc ├── hello_6.cc.tex ├── hello_6.ll ├── hello_6.ll.tex ├── mi.txt ├── out.bc ├── out.ll ├── phi.c ├── relax-after.o ├── relax-after.objdump ├── relax-after.objdump.tex ├── relax-after.s ├── relax-after.s.tex ├── relax-before.objdump ├── relax-before.objdump.tex ├── relax.c ├── relax.c.tex ├── relax.o ├── relax.objdump.tex ├── relax.s ├── relax.tmp.s ├── ssa.c ├── ssa.c.tex ├── ssa.ll ├── ssa.ll.tex ├── tblgen.td ├── tblgen.td.tex └── tmp.objdump ├── dot ├── view-legalize-dags-fib.png └── view-legalize-dags.fib.dot ├── fig ├── README.txt ├── dag_to_dag.png ├── dag_to_mi.png ├── ir_builder.png ├── ir_to_dag.png ├── legalize.png ├── llvm_ir.png ├── lowering_flow.png ├── mc_layer_overview.png ├── mc_layer_structure.png ├── mclinker-2013-figure-chenwj.pptx ├── mcstreamer_and_mcinst.png ├── mi_to_mc.png ├── section_and_fragment_v1.png └── section_and_fragment_v2.png ├── introduction.pptx ├── keynote-gradient.sty ├── mclinker-2013-chenwj.pdf ├── mclinker-2013-chenwj.tex ├── mclinker-2013-chenwj.tex.orig ├── pygments ├── bash.aux ├── bash.tex ├── c.aux ├── c.tex ├── cpp.aux ├── cpp.tex ├── llvm.aux ├── llvm.tex └── tblgen.tex └── talk.txt /LLVM/20081205_HowToUseJIT&MakeJITSupport_chenwj.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/LLVM/20081205_HowToUseJIT&MakeJITSupport_chenwj.pdf -------------------------------------------------------------------------------- /LLVM/GoogleMock for Dummies.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/LLVM/GoogleMock for Dummies.ppt -------------------------------------------------------------------------------- /LLVM/GoogleTestPrimer.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/LLVM/GoogleTestPrimer.ppt -------------------------------------------------------------------------------- /LLVM/How To Use JIT.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/LLVM/How To Use JIT.ppt -------------------------------------------------------------------------------- /LLVM/LLVM-RDF-01.md: -------------------------------------------------------------------------------- 1 |

2 | Copyright (c) 2018 陳韋任 (Chen Wei-Ren)

3 |

4 | 5 | RDF 全稱為 Register Dataflow Framework。是 Hexagon 後端針對暫存器分配之後所設計的一套數據流分析框架。更精確一點的說,此框架是運行在暫存器分配之後,已是非 SSA 形式的 MachineInstr 之上。其目的是希望重建近似 SSA 的數據流分析,以便類似於 [copy propagation](https://en.wikipedia.org/wiki/Copy_propagation) 和 [dead code elimination](https://en.wikipedia.org/wiki/Dead_code_elimination) 優化的展開。 6 | 7 | RDF 基本分成三個部分: 8 | 9 | - [RDFGraph](http://llvm.org/doxygen/RDFGraph_8h_source.html): 對程序建圖,圖中的節點分成兩類。一類是 `CodeNode`,代表程序的結構,由上至下分別為: `FuncNode`,`BlockNode` 和 `StmtNode`。分別對應到 `MachineFunction`,`MachineBasicBlock` 和 `MachineInstr`。另一類是 `RefNode` 用來表示指令中暫存器的 define 和 use,分別由 `DefNode` 和 `UseNode` 表示。 10 | 11 | 12 | 節點之間鏈結成一個 circular list。鏈結透過 Node Id 表示。 13 | 14 | 入口點為 [DataFlowGraph::build](http://llvm.org/doxygen/structllvm_1_1rdf_1_1DataFlowGraph.html#ac9b5de0f7d97d6989883bf591b1ba113) 15 | 16 | - [RDFLiveness](http://llvm.org/doxygen/RDFLiveness_8h_source.html): 基於 RDFGraph 重建 [liveness](https://en.wikipedia.org/wiki/Live_variable_analysis) 訊息。 17 | 18 | - [RDFCopy](http://llvm.org/doxygen/RDFCopy_8cpp_source.html) 和 [RDFDeadCode](http://llvm.org/doxygen/RDFDeadCode_8cpp_source.html): 根據 RDFLiveness 所計算的 liveness 所做的 copy propagation 和 dead code elimination。 19 | 20 | - reaching def: 此暫存器的 producer 是誰。 21 | - reached def: 此暫存器又被誰定義。 22 | - reached use: 此暫存器的 consumer 是誰。 23 | -------------------------------------------------------------------------------- /LLVM/LLVM-exception-handling-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | LLVM 3.0 其中一個較大的改動是重新設計和改寫對例外處理 (exception handling) 的支 4 | 援。新的异常处理机制增加數條 LLVM (原生,first-class) 指令,並新增/移除數條 LLVM 5 | intrinsic 函式。如果想要擴展 LLVM,通常的建議是先增加 intrinsic 函式。在經過一 6 | 段時間確認該擴展有必要加入 LLVM 原生支援,才會新增 LLVM IR。這是因為新增 LLVM IR 7 | 需要對 LLVM 整套框架做較大的修改。[1] 本篇文章主要以 Duncan Sands 今年在英國舉辦 8 | 的 2011 European User Group Meeting 投影片 [2] 作為例子說明 LLVM IR 對例外處理的 9 | 支援。 10 | 11 | 程式語言中的例外處理主要是針對程式中極少發生的例外狀況提供處理機制。因此理想上, 12 | 例外處理不應該對程式中正常流程造成任何影響,這是所謂的 zero-cost exception 13 | handling。目前所見的例外處理,其實作上基本遵循 Itanium C++ ABI: Exception 14 | Handling。[3] "The new LLVM exception handling scheme" 這份投影片是展示如何將 15 | C++ 例外處理的語法對映至 LLVM IR。 16 | 17 | 底下是很典型的 C++ try/catch 述句。 18 | 19 | try { 20 | ... 21 | MayThrowSomething(); 22 | AnotherFunctionCall(); 23 | ... 24 | } catch (int i) { 25 | DoSomethingWithInt(i); 26 | } catch (class A a) { 27 | DoSomethingWithA(a); 28 | } 29 | 30 | 如果函式 MayThrowSomething 丟出例外,會依序匹配 catch clause 看是否符合該例外的 31 | 型別。如果都不匹配,則會做 stack unwinding,清空發生例外的函式其堆疊並將該例外傳 32 | 遞給 caller 繼續處理該例外。[4] 針對 try 區塊中的函式呼叫,或是會丟出例外的函式 33 | 其中的函式呼叫都改用 invoke 而非 call。所以 "MayThrowSomething()" 相對映的 LLVM 34 | IR 如下: 35 | 36 | invoke void @_Z17MayThrowSomethingv() to label %cont unwind label %lpad 37 | 38 | 根據例外是否發生,執行流程會跳到 %cont 區塊或是 %lpad 區塊。這裡的重點在於負責 39 | 例外處理的 %lpad。在例外處理中,有個名詞叫 "landingpad",中文譯名為停機坪。之所 40 | 以稱為 landingpad,是因為當例外發生時,執行流程會跳轉到這裡做一些準備,再轉發到 41 | 其它地方。我們來看一下 %lpad 的內容。 42 | 43 | lpad: 44 | %info = landingpad { i8*, i32 } personality @__gxx_personality_v0 catch @_ZTIi %catch @_ZTl1A 45 | %except = extractvalue { i8*, i32 } %info, 0 46 | %selector = extractvalue { i8*, i32 } %info, 1 47 | %typeid = call i32 @llvm.eh.typeid.for(@_ZTIi) 48 | %match = icmp eq i32 %selector, %typeid 49 | br i1 %match, label %run_catch, label %try_next 50 | 51 | 我們先看第一道指令: 52 | 53 | %info = landingpad { i8*, i32 } personality @__gxx_personality_v0 catch @_ZTIi %catch @_ZTl1A 54 | 55 | 在 Itanium C++ ABI: Exception Handling 中定義所謂的 personality routine。它是語言 56 | 執行時期函示庫中的函式,用來作為 system unwind library 和 language-specific 57 | exception handling semantics 的介面,這是因為不同的程式語言對於例外處理有不同的行 58 | 為。landingpad 這條 LLVM IR 負責透過 personality routine 取得例外型別和其它資訊, 59 | 在 C++ 中,該 routine 稱為 __gxx_personality_v0。 60 | 61 | { i8*, i32 } 是 %info 的型別,分別是指向 exception structure 的指針和 selector value。 62 | 這在 LLVM 型別系統中是一個 struct type,LLVM 3.0 另一項重大修改就是它的型別系統。 63 | landingpad 最後列出所有的 catch clause。目前 LLVM 的設計會列出 nest try 中所有的 64 | catch clause,請見 [2] 第 29 頁。 65 | 66 | 接著用 extractvalue 從 %info 中分別取出指向 exception structure 的指針和 selector 67 | value。 68 | 69 | %except = extractvalue { i8*, i32 } %info, 0 70 | %selector = extractvalue { i8*, i32 } %info, 1 71 | 72 | llvm.eh.typeid.for 是負責例外處理的 intrinsic 函式,它是用來將 catch clause 內的 73 | 型別 (@_ZTIi) 對映成 selector value (%typeid), 74 | 75 | %typeid = call i32 @llvm.eh.typeid.for(@_ZTIi) 76 | 77 | 接著比較例外和 catch clause 的 selector value 是否一致。 78 | 79 | %match = icmp eq i32 %selector, %typeid 80 | 81 | 如果匹配成功,表示由該 catch clause 處理該例外,跳至 %run_catch 區塊; 否則跳至 82 | %try_next 繼續下一次的匹配。 83 | 84 | br i1 %match, label %run_catch, label %try_next 85 | 86 | 這邊提一下 personality routine,在 C++ 稱為 __gxx_personality_v0。它知道如何進行 87 | catch clause 的匹配,因為這需要比較 C++ 的型別。@_ZTIi 和 @_ZTI1A 分別是語言特定 88 | 的全域變數,在此是 C++,分別代表 int 和 class A 兩個型別的型別資訊 (typeinfo)。 89 | 90 | 我們先來看 %run_catch 區塊。 91 | 92 | run_catch: 93 | %thrown = call i8* @__cxa_begin_catch(%except) 94 | %tmp = bitcast i8* %thrown to i32* 95 | %i = load i32* %tmp 96 | call void @_Z18DoSomethingWithInti(i32 %i) 97 | call void @__cxa_end_catch() 98 | br label %finished 99 | 100 | 它對映的是 catch (int i) 裡面的代碼。其中, 101 | 102 | %thrown = call i8* @__cxa_begin_catch(%except) 103 | call void @__cxa_end_catch() 104 | 105 | 分別是語言特定的函示呼叫。還記得之前我們從 %info 取出 exception structure 的指針 106 | 並賦值給 %except 嗎? __cxa_begin_catch 透過該指針取回 exception structure (仍然 107 | 是指針),此外它還額外做了一些事。__cxa_end_catch 則是做一些清除的工作。這部分詳 108 | 細內容請見 [5],這是 LLVM 實現 C++ runtime 的子計畫。 109 | 110 | 以下三條指令是將 i8* 轉型成 i32*,把 i32* 所指內存的內容讀出,最後傳給 catch(int 111 | i) 中的 DoSomethingWithInt 函式 (_Z18DoSomethingWithInti)。 112 | 113 | %tmp = bitcast i8* %thrown to i32* 114 | %i = load i32* %tmp 115 | call void @_Z18DoSomethingWithInti(i32 %i) 116 | 117 | 最後跳至 %finished 區塊,完成例外處理。 118 | 119 | br label %finished 120 | 121 | 如果該例外不匹配 catch (int i),則會跳至 %try_next 進行下一次匹配。 122 | 123 | try_next: 124 | %typeid2 = call i32 @llvm.eh.typeid.for(@_ZTI1A) 125 | %match2 = icmp eq i32 %selector, %typeid2 126 | br i1 %match2, label %run_catch2, label %end 127 | 128 | 這裡的重點在於,如果當前丟出例外的函式其 catch clause 無法處理該例外,就會跳至 129 | %end 做 stack unwinding。 130 | 131 | end: 132 | resume { i8*, i32 } %info 133 | 134 | 這裡小節一下目前提到有關例外處理的 LLVM IR 和 intrinsics。 135 | 136 | invoke - 以 C++ 為例,在 try block 中的函式呼叫,或是在會丟出例外的函式中的函式呼 137 | 叫,一律改用 invoke 而非 call。 138 | 139 | landingpad - 負責取出例外的相關資訊以供後續處理,並列出所有 catch clause。它取代 140 | 原本的 llvm.eh.exception 和 llvm.eh.selector intrinsic。 141 | 142 | llvm.eh.typeid.for - 把 catch clause 內的型別對映成 selector value,以便將來跟例 143 | 外的 selector value 做比較。 144 | 145 | resume - 如果例外無法被目前函式處理,做 stack unwinding 並將該例外傳遞至 caller。 146 | 它取代原本的 llvm.eh.resume intrinsic。 147 | 148 | [1] http://llvm.org/docs/ExtendingLLVM.html 149 | [2] http://llvm.org/devmtg/2011-09-16/EuroLLVM2011-ExceptionHandling.pdf 150 | [3] http://www.codesourcery.com/cxx-abi/abi-eh.html 151 | [4] http://en.wikipedia.org/wiki/Call_stack#Unwinding 152 | [5] http://libcxxabi.llvm.org/spec.html 153 | -------------------------------------------------------------------------------- /LLVM/LLVM-exception-handling-02.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | 我們繼續接著看 LLVM 針對 nested try、exception specification 和 destructor 的處 4 | 理。這邊需要對 C++ 的語法和例外處理有點認識,請見 [1][2]。首先來看 nested try。 5 | 6 | C++ nested try 的例子如下 (請見 [3] 第 29 頁): 7 | 8 | try { 9 | ... 10 | try { 11 | ... 12 | MayThrowSomething(); 13 | ... 14 | } catch (int i) { 15 | DoSomethingWithInt(i); 16 | } catch (class A a) { 17 | DoSomethingWithA(a); 18 | } 19 | ... 20 | } catch (class B b) { 21 | DoSomethingWithB(b); 22 | } catch (...) { 23 | DoSomethingElse(); 24 | } 25 | 26 | 會丟出例外的 MayThrowSomething 函式其 landingpad 區塊的 LLVM IR 如下: 27 | 28 | lpad: 29 | %info = landingpad { i8*, i32 } 30 | personality @__gxx_personality_v0 31 | catch @_ZTIi // catch (int) 32 | catch @_ZTI1A // catch (class A) 33 | catch @_ZTI1B // catch (class B) 34 | catch null // catch (...) 35 | 36 | 這裡我們可以看到 landingpad 會列出所有可能會接住 MayThrowSomething 例外的 catch 37 | clause。 38 | 39 | C++ 允許程序員規範一個函式所能丟出例外其型別 (exception specification),例如 40 | ([3] 第 30 頁): 41 | 42 | // foo 不允許丟出任何例外 43 | int foo() throw () { 44 | bar(); 45 | return 0; 46 | } 47 | 48 | LLVM 中用 filter 來實現 exception specification,其對映的 LLVM IR 如下: 49 | 50 | invoke void @_Z3barv() to label %cont unwind label %lpad 51 | 52 | cont: 53 | ret i32 0 54 | 55 | lpad: 56 | %info = landingpad { i8*, i32 } 57 | personality @__gxx_personality_v0 58 | filter [0 x i8*] zeroinitializer 59 | %except = extractvalue { i8*, i32 } %info, 0 60 | tail call void @__cxa_call_unexpected(%except) 61 | unreachable 62 | 63 | 我們可以看到針對 bar 的呼叫是用 invoke, 64 | 65 | invoke void @_Z3barv() to label %cont unwind label %lpad 66 | 67 | 這裡的重點在 %lpad 區塊。landingpad 後面有一個 filter, 68 | 69 | filter [0 x i8*] zeroinitializer 70 | 71 | 它後面是一個 typeinfo 的數組 (array) [4]。如果丟出的例外不匹配該數組中的任一個 72 | typeinfo,landingpad 會傳回負值。這種情況下,__cxa_call_unexpected 或是 73 | _Unwind_Resume 應該被呼叫 [5]。這裡我們可以看到,針對不會丟出例外的函式,filter 74 | 帶的數組是 [0 x i8*],這是一個沒有元素的數組。 75 | 76 | tail call void @__cxa_call_unexpected(%except) 77 | 78 | call 指令之前 tail 標記代表此處的函式呼叫可以做 tail call optimization 或是 79 | sibling call optimization。詳細請見 [6]。其後的 unreachable 是告知優化器執行流程 80 | 不會到達此處。以此例而言,__cxa_call_unexpected 具有 noreturn 的屬性,因此其後 81 | 會加上 unreachable,代表 __cxa_call_unexpected 函式呼叫不會返回。 82 | 83 | 最後,我們來看 LLVM IR 如何處理 destructor。在做 stack unwinding 的時候,當前 84 | stack 上的物件 (object) 的解構子 (destructor) 會被執行,這是 C++ 的情況。其它 85 | 語言或是語言擴展也有提供類似機制。這邊以 GCC cleanup attribute 為例 [7] ([3] 86 | 第 31 頁): 87 | 88 | void oof(void *); 89 | void bar(void) { 90 | int x __attribute__((cleanup(oof))); 91 | foo(); 92 | ... 93 | } 94 | 95 | 當變數 x 離開目前的範圍,oof 會被呼叫。有兩種情況會導致變數 x 離開目前的範圍, 96 | 97 | 1. bar 函式正常執行完畢。 98 | 99 | 2. foo 函式丟出例外,或是其它會導致 stack unwinding 的事情發生。 100 | 101 | 我們只考慮後者,並看 LLVM 怎麼實現 destructor/cleanup,其對映的 LLVM IR 如下: 102 | 103 | define void @bar() { 104 | entry: 105 | %x = alloca i32 106 | invoke void @foo() to label %cont unwind label %lpad 107 | 108 | cont: 109 | ... 110 | 111 | lpad: 112 | %info = landingpad { i8*, i32 } 113 | personality @__gcc_personality_v0 114 | cleanup 115 | %var_ptr = bitcast i32* %x to i8* 116 | call void @oof(i8* %var_ptr) 117 | resume { i8*, i32 } %info 118 | } 119 | 120 | landingpad 後面帶的 cleanup 指明當前的區塊 (%lpad) 有 cleanup 需要執行,這裡是指 121 | oof 函式。因為當前函式沒有合適的 catch clause 處理該例外,最後執行 resume。這裡 122 | 要注意到,如果 foo 正常執行,走到 %cont 區塊,oof 最終還是會被執行。上述 LLVM IR 123 | 只看 foo 會丟出例外的情況。 124 | 125 | 眼尖的人會看到 personality routine 改叫 __gcc_personality_v0,請見 [9]。 126 | 127 | LLVM 代碼中有展示例外處理的範例,有興趣的人可以參考 examples/ExceptionDemo/ExceptionDemo.cpp 128 | ,並遵照底下流程編譯,最後執行檔放在 $INSTALL 目錄。 129 | 130 | --- 131 | $ wget http://llvm.org/releases/3.0/llvm-3.0.tar.gz; tar xvf llvm-3.0.tar.gz 132 | $ mkdir build; cd build 133 | $ ../llvm-3.0.src/configure --prefix=$INSTALL 134 | $ make install 135 | $ cd example 136 | $ make install 137 | --- 138 | 139 | 限於個人才疏學淺,有興趣的人可以參考底下文件。 140 | 141 | - http://www.llvm.org/docs/ExceptionHandling.html 142 | - http://blog.llvm.org/2011/11/llvm-30-exception-handling-redesign.html 143 | - http://stackoverflow.com/questions/329059/what-is-gxx-personality-v0-for 144 | - http://www.airs.com/blog/archives/464 145 | - http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-July/041748.html 146 | 147 | erlv 整理了一下 LLVM 3.0 的新特色,有興趣的人請見 148 | 149 | http://www.lingcc.com/2011/12/08/11882/ 150 | 151 | [1] http://www.cplusplus.com/doc/tutorial/exceptions/ 152 | [2] http://en.wikibooks.org/wiki/C++_Programming/Exception_Handling 153 | [3] http://llvm.org/devmtg/2011-09-16/EuroLLVM2011-ExceptionHandling.pdf 154 | [4] http://llvm.org/docs/LangRef.html#t_array 155 | [5] http://libcxxabi.llvm.org/spec.html 156 | [6] http://llvm.org/releases/3.0/docs/LangRef.html#i_call 157 | [7] http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html 158 | [8] http://llvm.org/releases/3.0/docs/LangRef.html#i_invoke 159 | [9] http://people.cs.nctu.edu.tw/~chenwj/log/GCC/richi-2011-12-16.txt 160 | -------------------------------------------------------------------------------- /LLVM/LLVM-introduction-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | 前言 4 | 5 | LLVM 全名為 Low Level Virtual Machine。各位千萬不可被它的名稱所誤導,它跟虛擬 6 | 機器 (virtual machine) 基本上沒有關係。它是一個編譯器基礎設施。換句話說,它可以 7 | 用來建立一個編譯器。雖然有些計畫,如 VMKit,使用 LLVM 建立類似於 JVM 的虛擬機器 8 | ,但是 LLVM 跟虛擬機器沒有關係。在研究所時代,我就有接觸過 LLVM。畢業後做的工作 9 | 仍與 LLVM 有關。由於我本身有點編譯器的背景,在介紹 LLVM 的同時,我希望能盡可能的 10 | 將編譯器相關的知識融入進去。希望各位不吝指教。 11 | 12 | 0. 建置環境 13 | 14 | 我先介紹如何建置 LLVM。 15 | 16 | 1. 使用 llvm-top。llvm-top 是一個鮮為人知的小工具。注意! 它抓取的是 svn 版本, 17 | 而非 release 版本。詳細的使用方法請見 llvm-top/README.txt。底下是使用範例: 18 | 19 | --- 20 | $ svn co http://llvm.org/svn/llvm-project/llvm-top/trunk/ llvm-top 21 | $ cd llvm-top 22 | $ ./build OPTIMIZED=1 PREFIX=$INSTALL llvm 23 | --- 24 | 25 | 2. 使用 configure。 26 | 27 | --- 28 | $ wget http://llvm.org/releases/2.9/llvm-2.9.tgz; tar xvf llvm-2.9.tgz 29 | $ mkdir build; cd build 30 | $ ../llvm-2.9/configure --prefix=$INSTALL --enable-optimized 31 | $ make install 32 | $ export PATH=$INSTALL/bin:$PATH 33 | --- 34 | 35 | 3. 使用 CMake。 36 | 37 | --- 38 | $ wget http://llvm.org/releases/2.9/llvm-2.9.tgz; tar xvf llvm-2.9.tgz 39 | $ mkdir build; cd build 40 | $ CFLAGS=-fno-strict-aliasing CXXFLAGS=-fno-strict-aliasing cmake -i ../llvm-2.9 41 | 42 | ... 設定 ... 43 | 44 | $ make intall 45 | $ export PATH=$INSTALL/bin:$PATH 46 | --- 47 | 48 | 對於一般平台,LLVM 提供預編譯好的執行檔,抓下來直接使用亦可。 49 | 50 | 1. LLVM IR 51 | 52 | 這邊簡單複習一下編譯器的流程。前端 (front end) 將代碼轉成中介碼 (intermediate 53 | representation,簡稱 IR),後端將 (back end) 將中介碼轉成二進制碼 (binary)。LLVM 54 | 做為一個編譯器基礎設施,基本上只等同 2/3 個編譯器。它還需要前端 (算 1/3 個編譯 55 | 器) 將代碼轉成 LLVM IR,之後再進行跟底層架構無關/相關的優化,最後生成二進制碼。 56 | 57 | LLVM IR 有底下三種形式: 58 | 59 | - in-memory compiler IR 60 | - on-disk bitcode representation 61 | - human readable assembly language representation 62 | 63 | 這樣說太模糊了,我們馬上編譯一個小程式來看看。:-) 64 | 65 | --- 66 | $ cd llvm-2.9/example/ModuleMaker 67 | $ g++ ModuleMaker.cpp `llvm-config --cxxflags --ldflags --libs all` -o ModuleMaker 68 | $ ModuleMaker > module.bc 69 | # 將 bitcode 反匯編成 LLVM human readable assembly 70 | $ llvm-dis module.bc 71 | # 讓 vim 能夠高亮顯示 LLVM IR 72 | $ cp llvm-2.9/utils/vim/llvm.vim $HOME/.vim/syntax/ 73 | $ vim module.ll 74 | ; ModuleID = 'module.bc' 75 | 76 | define i32 @main() { 77 | EntryBlock: 78 | %addresult = add i32 2, 3 79 | ret i32 %addresult 80 | } 81 | --- 82 | 83 | 一般來說,*.bc 存放的是 "on-disk bitcode representation"; *.ll 存放的是 "human 84 | readable assembly language representation"。在 LLVM 內部處理的就是 "in-memory 85 | compiler IR"。 86 | 87 | 就我所知,LLVM 最被廣為使用的方式,就是生成 LLVM IR,再交由其後端生成二進制碼。 88 | Module 是裝載 LLVM IR 的容器 (container),其中包含函式 (function)、全域變數 ( 89 | global variable) 和符號表項 (symbol table entry)。一般會在 Module 中建立數個函式 90 | ,函式中有數個基本塊 (basic block),最後在基本塊中填入 LLVM IR。ModuleMaker.cpp 91 | 中的註解寫得很完整,基本上就是前述的流程。 92 | 93 | LLVM 一個常被宣傳的特色就是 JIT (Just In Time)。我們還是來看個例子。 94 | 95 | --- 96 | $ cd llvm-2.9/examples/HowToUseJIT 97 | $ g++ HowToUseJIT.cpp `llvm-config --cxxflags --ldflags --libs all` -o HowToUseJIT 98 | $ ./HowToUseJIT 99 | --- 100 | 101 | HowToUseJIT.cpp 的前半部就是在建立一個 Module,內含兩個函式: foo 和 add1。建立好 102 | Module 之後,再針對這個 Module 生成 ExecutionEngine,即是 JIT。透過 ExecutionEngine 103 | 將該 Module 的某個函式動態編譯成 binary 並執行,最後傳回執行後的結果。 104 | 105 | 最後簡單介紹一下 example 目錄底下其它的範例。 106 | 107 | - Fibonacci: 展示如何於 Module 中生成遞迴函式計算 fibonacci。 108 | - BrainF: 實現程式語言 Brainfuck [1][2][3]。 109 | - Kaleidoscope: [4] 教程上所展示的範例語言。 110 | - OCaml-Kaleidoscope: [4] 教程上所展示的範例語言。 111 | - ParallelJIT: 展示多個執行緒可同時執行 JIT。 112 | - ExceptionDemo: 展示 LLVM 如何處理 C++ 的例外 (exception) [5][6]。 113 | 114 | [1] http://www.profibing.de/lisp/brainfuck/Llvm.pdf 115 | [2] http://blog.linux.org.tw/~jserv/archives/2011/04/build_programmi_1.html 116 | [3] http://blog.linux.org.tw/~jserv/archives/2011/04/_llvm_brainfuck.html 117 | [4] http://llvm.org/docs/tutorial/ 118 | [5] http://llvm.org/docs/ExceptionHandling.html 119 | [6] http://llvm.org/devmtg/2011-09-16/EuroLLVM2011-ExceptionHandling.pdf 120 | -------------------------------------------------------------------------------- /LLVM/LLVM-introduction-03.txt: -------------------------------------------------------------------------------- 1 | 2. 實驗 2/2 - 使用 LLVM JIT 2 | 3 | ExecutionEngine 是一個關鍵的類別。它能把 Module 中的特定 Function 4 | 動態編譯成 host binary。此外,它還提供介面供使用者提取 JIT-ted 5 | function (host binary) 的資訊。 6 | 7 | 本實驗補上前篇實驗的後半部。附件是完整的代碼。 8 | 9 | --------------------- tut2.cpp (cont'd) ----------------------------- 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // LLVM JIT 所需的頭文件 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | using namespace llvm; 26 | 27 | Module* makeLLVMModule(LLVMContext& ctx); 28 | 29 | int main(int argc, char**argv) { 30 | 31 | // 根據 host 初始化 LLVM target,即針對哪一個 target 生成 binary。 32 | InitializeNativeTarget(); 33 | 34 | LLVMContext Context; 35 | 36 | Module* Mod = makeLLVMModule(Context); 37 | 38 | verifyModule(*Mod, PrintMessageAction); 39 | 40 | PassManager PM; 41 | 42 | PM.add(createPrintModulePass(&outs())); 43 | PM.run(*Mod); 44 | 45 | // 根據給定的 Module 生成 ExecutionEngine。 46 | ExecutionEngine* EE = EngineBuilder(Mod).create(); 47 | 48 | // 在 Module 中插入新的函式 (Function)。若該函式已存在,返回該函式。 49 | // 因此這邊取回 makeLLVMModule 所插入的 gcd 函式。 50 | Function *GCD = 51 | cast(Mod->getOrInsertFunction("gcd", 52 | /* 返回型別 */ Type::getInt32Ty(Context), 53 | /* 參數 */ Type::getInt32Ty(Context), 54 | /* 參數 */ Type::getInt32Ty(Context), 55 | /* 結尾 */ (Type *)0)); 56 | 57 | // 準備傳給 GCD 的參數。 58 | std::vector Args(2); 59 | Args[0].IntVal = APInt(32, 17); 60 | Args[1].IntVal = APInt(32, 4); 61 | 62 | // 將參數傳給 gcd。將其 JIT 成 host binary 並執行。取得執行後的結果。 63 | GenericValue GV = EE->runFunction(GCD, Args); 64 | 65 | outs() << "---------\nstarting GCD(17, 4) with JIT...\n"; 66 | outs() << "Result: " << GV.IntVal << "\n"; 67 | 68 | // 釋放 ExecutionEngine 中相對於 GCD JIT'ed machine code 的內存。 69 | EE->freeMachineCodeForFunction(GCD); 70 | delete EE; 71 | llvm_shutdown(); // 釋放用於 LLVM 內部的資料結構。 72 | return 0; 73 | } 74 | --------------------------------------------------------------------- 75 | 76 | 眼尖的你應該會發現我似乎忘了 delete Mod。很好! 但是你一但加上 delete Mod,執行之後 77 | 會出現 segmentation fault。試試看! ;-) 78 | 79 | 答案在 EngineBuilder 的註解裡 "the created engine takes ownership of the module"。 80 | 81 | --- 82 | /// EngineBuilder - Constructor for EngineBuilder. If create() is called and 83 | /// is successful, the created engine takes ownership of the module. 84 | EngineBuilder(Module *m) : M(m) { 85 | InitEngine(); 86 | } 87 | --- 88 | 89 | 我還漏了底下這一行沒講清楚。 90 | 91 | verifyModule(*Mod, PrintMessageAction); 92 | 93 | 什麼時候 Module 是非法的? 舉例來說,假設 Module 裡面有底下這樣一條 LLVM 指令。 94 | 95 | %x = add i32 1, %x ; x = 1 + x 96 | 97 | 直覺上來看很正常。但是 LLVM IR 滿足 SSA (Static Single Assignment) 的形式,亦即 98 | 每條賦值指令都會生成一個新的變數。所以上面這條指令應該是底下這樣: 99 | 100 | %x1 = add i32 1, %x ; x1 = 1 + x 101 | 102 | SSA 能夠簡化資料流 (data flow) 的分析,有助於後續的優化。大部分的編譯器 (我只確定 103 | GCC 和 LLVM) 的 IR 主要都是採 SSA 的形式。 104 | 105 | 最後,各位要注意 LLVM API 不穩定。看各位是要緊跟 svn 版本進行開發,又或是只跟 106 | release 版本開發。這其中各有利弊,但是別忘了 LLVM 算是很熱心的一個 community, 107 | 我想有問題都可以到郵件列表或聊天室詢問。[1][2] 108 | 109 | [1] http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev 110 | [2] http://llvm.org/docs/#irc 111 | -------------------------------------------------------------------------------- /LLVM/LLVM-register-allocation-00.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | LLVM 3.0 其中一個較大幅度的改動就是更換它長久使用的 linear scan 暫存器配置器 4 | (register allocator),改用 basic allocator 和 greedy allocator。本篇文章主要 5 | 依據 [1] 進行介紹。我們先來一點基礎知識。 6 | 7 | 一個程序的執行可以被視為計算機狀態 (state) 的一連串轉換。每一條述句 (statement) 8 | 的執行都是將此前的狀態當作輸入,並輸出新的狀態。兩個述句之間的點被稱為 program 9 | point,每一個 program point 都有與之相對應的狀態。一個變數被 define,代表該變數 10 | 被寫入值; 一個變數被 use,代表該變數的值被讀出 (參考 [22] 9.2 Introduction to 11 | Data-Flow Analysis)。針對變數 a 而言, 12 | 13 | ---------- program point 1 14 | a = 1; // 變數 a 被 define 15 | ---------- program point 2 16 | b = a; // 變數 a 被 use 17 | ---------- program point 3 18 | 19 | 一個變數被稱為 live,代表該變數其值在將來會被使用到,亦即在某個時間點被讀出。 20 | 一個變數的 live range 是一個 program point 的集合,該變數在這些 program ponit 21 | 為 live。一個變數的 live interval 是一個最小區間,該區間包含該變數所有的 live 22 | range。live interval 相較於 live range 較不精確,但是其 register allocation 23 | 較為簡單 (參考 [3] 第 39 頁)。Register Allocation [3] 第 64 頁演示的 linear 24 | scan 算法就是針對各個變數的 live interval 配置暫存器。編譯器在做 register 25 | allocation 時,會視情況針對某變數做 live range splitting,其目的是希望在 liver 26 | ange splitting 之後,能減少各變數之間 live range 重疊的情況。如果兩個變數的 live 27 | range 有所重疊,則代表這兩個變數無法分配到同一個暫存器,這會增加暫存器使用量。 28 | 29 | Register allocation 包含兩個步驟: 首先,register allocation 釐清哪些變數將可存 30 | 入暫存器; 接著,register assignment 將物理暫存器分配給那些變數 [2] 8.1.4 [4][5]。不過一般不會 31 | 特別區分兩者,通稱為 register allocation。如果途中發現暫存器不敷使用,register 32 | allocation 演算法會選擇將某些暫存器存入內存,這個動作稱之為 spill。register 33 | allocation 演算法目標之一就是要降低 spill 的次數。一般來說,register allocation 34 | 演算法會選擇把擁有較長 live interval 的變數 spill 到內存。這是因為如果將暫存器分 35 | 配給該變數,則該暫存器將有較長時間不能再被使用。所以,一般會將暫存器優先分配給 36 | live interval 較短的變數。有時候,如果某暫存器其中的值可以在之後簡單的計算出來 37 | ,算法可以選擇 spill 該暫存器,且之後不須從內存將其值讀出,只需要重新計算即可, 38 | 這稱之為 rematerialization [6]。 39 | 40 | 41 | graph coloring? [3] p.104 42 | 43 | 相較於 linear scan 是利用 live interval 此一較不精確的描述來分配暫存器,Register 44 | Allocation 第 109 頁演示的 graph coloring 算法利用 live range 45 | 分配暫存器。每個變數代表一個節點。如果變數 A 和變數 B 的 live range 46 | 有所重疊,則節點 A 和 B 之間有連線。針對各個變數的 live range 所建立的圖稱為 47 | register interference graph (RIG)。此算法目的是要替 RIG 上的節點著色 48 | (代表暫存器),有連線的節點必須著以不同的顏色。 49 | 50 | [1] http://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html 51 | [2] Compilers: Principles, Techniques, and Tools - 2nd 52 | [3] http://www.stanford.edu/class/cs143/lectures/170_Register_Allocation.pdf 53 | [4] http://compilers.iecc.com/comparch/article/94-03-093 54 | [5] http://www.scribd.com/doc/49837633/10/Register-Allocation-and-Assignment 55 | [6] http://en.wikipedia.org/wiki/Rematerialization 56 | -------------------------------------------------------------------------------- /LLVM/LLVM-register-allocation-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | 我們開始介紹 LLVM linear scan register allocator。以下提到的 linear scan 專指 4 | LLVM linear scan register allocator,主要參考 [1] 和 [3][4] 第 16 頁。 5 | 6 | 7 | 在進到 Allocator 之前,必須要先經過幾個流程。 8 | 9 | PHI Elimination -> Two-Address -> Liveness -> Coalescing 10 | -> Allocator (Spiller) -> Rewriter 11 | 12 | LLVM IR 屬於 SSA (Static Single Assignment) 的形式。簡單來說,一個賦值表示式會創 13 | 建出一個新的變數。之所以採用 SSA 的形式是因為此種形式使得之後的優化更加好做。比 14 | 如說底下這段代碼: 15 | 16 | y := 1 17 | y := 2 18 | x := y 19 | 20 | 將其轉為 SSA 形式之後會變成: 21 | 22 | y_1 := 1 23 | y_2 := 2 24 | x_1 := y_2 25 | 26 | 這樣一來,編譯器很容易看出 "y_1 := 1" 這條賦值語句是多餘的。在 SSA 中,為了判定 27 | 某一個變數的 def 是從哪一個執行路徑過來,有所謂的 PHI Node。我們拿 [6] 當例子。 28 | 29 | --- 30 | if (...) { 31 | *A = 5; 32 | P = A; 33 | } else { 34 | *B = 9; 35 | P = B; 36 | } 37 | use(*P); 38 | --- 39 | 40 | --- 41 | BB1: 42 | store i32 5, i32* %A 43 | br label %Merge 44 | 45 | BB2: 46 | store i32 9, i32* %B 47 | br label %Merge 48 | 49 | Merge: 50 | %P = phi i32* [%A, %BB1], [%B, %BB2] 51 | %Y = load i32 *%P 52 | ... 53 | ... use %Y ... 54 | --- 55 | 56 | 但是一般的機器並不支援類似 PHI Node 的運算。因此我們必須將 PHI Node 換成普通的 57 | 運算,這就是所謂的 PHI Elimination。請見 [4] 第 4 - 8 頁。 58 | 59 | LLVM IR 大致分為底下六類: 60 | 61 | 1. Three-address code (TAC) 指令: 算術運算指令,比較運算指令和位元 (bitwise)運算指令。 62 | 63 | 2. 控制流指令: 條件和無條件跳轉指令,返回指令和 PHI。 64 | 65 | 3. 函式呼叫指令: call。 66 | 67 | 4. 求址指令: getelementptr。 68 | 69 | 5. 其它: 轉型 (cast),有號/無號擴展和 truncation instruction。 70 | 71 | 72 | 目標機器不見得支援 Three-address code,例如 x86。Two-Address 負責將 Three-address code 73 | 傳換成 Two-address code。 74 | 75 | 接著 Liveness 計算各變數的 live range (live interval)。 76 | 77 | Coalescing 負責消除冗餘的拷貝。但是這會使得變數的 live range 拉長。這會對之後 78 | 的 allocator 造成壓力,還記得前面提到 allocator 偏好 live range 較短的變數嗎? 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | llvm-2.9/lib/CodeGen/* 88 | 89 | 90 | [1] http://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html 91 | [2] http://www.stanford.edu/class/cs143/lectures/170_Register_Allocation.pdf 92 | [3] http://www.llvm.org/devmtg/2008-08/Cheng_RegisterAllocation.pdf 93 | [4] http://llvm.org/devmtg/2009-10/RegisterAllocationFutureWorks.pdf 94 | [5] http://en.wikipedia.org/wiki/Static_single_assignment_form 95 | [6] http://blog.llvm.org/2009/12/advanced-topics-in-redundant-load.html 96 | [7] http://baldur.iti.uka.de/~falke/papers/VSTTE12a.pdf (Termination Analysis of Imperative Programs Using Bitvector Arithmetic) 97 | 98 | [] http://llvm.org/devmtg/2011-11/Olesen_RegisterAllocation.pdf 99 | [] http://llvm.org/devmtg/2011-11/Grosbach_Anderson_LLVMMC.pdf 100 | 101 | [1] http://llvm.org/docs/CodeGenerator.html#machineinstr 102 | 103 | [] http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-December/046073.html 104 | -------------------------------------------------------------------------------- /LLVM/LLVM-register-allocation-02.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | 我們開始介紹 LLVM linear scan register allocator。以下提到的 linear scan 專指 4 | LLVM linear scan register allocator,主要參考 [1] 和 [3][4] 第 16 頁。 5 | 6 | 7 | 在進到 Allocator 之前,必須要先經過幾個流程。 8 | 9 | PHI Elimination -> Two-Address -> Liveness -> Coalescing 10 | -> Allocator (Spiller) -> Rewriter 11 | 12 | LLVM IR 屬於 SSA (Static Single Assignment) 的形式。簡單來說,一個賦值表示式會創 13 | 建出一個新的變數。之所以採用 SSA 的形式是因為此種形式使得之後的優化更加好做。比 14 | 如說底下這段代碼: 15 | 16 | y := 1 17 | y := 2 18 | x := y 19 | 20 | 將其轉為 SSA 形式之後會變成: 21 | 22 | y_1 := 1 23 | y_2 := 2 24 | x_1 := y_2 25 | 26 | 這樣一來,編譯器很容易看出 "y_1 := 1" 這條賦值語句是多餘的。 27 | 28 | 29 | llvm-2.9/lib/CodeGen/* 30 | 31 | 32 | Linear Scan 33 | Allocator <----> Spiller 34 | 35 | 36 | Linear scan 算法維護一個稱之為 active list 的資料結構,其內存放在當前 function 37 | 所在目前的這一點,所有屬於 live 的 live range。Linear scan 透過 active list 偵測 38 | 有無 interference 的發生,不需要計算整個 interference graph。這對 linear scan 39 | 的速度是一個關鍵,因為它不需要計算整個 interference graph,但這同時也是它的阿基 40 | 里斯之踵。live interval 和 live range 混用。 41 | 42 | 43 | 1. 尋訪 live ranges in a linear order。 44 | 2. 45 | 46 | 我們這裡提及的 machine instruction/code 指的是 LLVM MachineInstr (MI),不要和 47 | LLVM Machine Code (MC) 搞混。MC 負責操作 MI 進行目的檔的輸出。這裡先給一個 LLVM 48 | 後端粗略的概圖: 49 | 50 | LLVM IR -> DAG -> MI -> MC 51 | 52 | Linear scan 算法簡單,它很容易透過一些修改進而得到效能增進。Linear scan 依賴後 53 | 續的 virtual register rewriter 替它清理做完 register allocation 的 machine code。 54 | 原則上,rewriter 的工作應只限於將 virtual register 替換成 physical register,跟 55 | 據之前 register allocator 所得結果。但是在這個階段,rewriter 知道一些小把戲 (trick) 56 | 來清理 register allocator 做的一些蠢事。比如說,register allocator 可能會從堆疊上 57 | 重複讀取一個變數,但是該變數其值並未改變。這時,rewriter 可以消除第二個以後的 58 | load 指令。在 LLVM 中,這個 rewriter 是 local 的,這意味著它只能清理基本塊之內 59 | 的 machine code,它無法做跨基本塊的清理。Linear scan allocator 如此依賴 rewriter 60 | 會產生一個問題,rewriter 變得臃腫,這會拉長編譯時做 register allocation (也計入 61 | rewriter) 所花時間,同時 rewriter 代碼難以維護。 62 | 63 | 64 | Linear scan 的複雜度不再是 linear。 65 | 66 | --- RALinScan::runOnMachineFunction --- 67 | spiller_.reset(createSpiller(*this, *mf_, *vrm_)); 68 | 69 | initIntervalSets(); 70 | 71 | linearScan(); 72 | 73 | // Rewrite spill code and update the PhysRegsUsed set. 74 | rewriter_->runOnMachineFunction(*mf_, *vrm_, li_); 75 | --- 76 | 77 | --- RALinScan::linearScan --- 78 | while (!unhandled_.empty()) { 79 | // pick the interval with the earliest start point 80 | LiveInterval* cur = unhandled_.top(); 81 | unhandled_.pop(); 82 | ++NumIters; 83 | 84 | processActiveIntervals(cur->beginIndex()); 85 | processInactiveIntervals(cur->beginIndex()); 86 | 87 | // Allocating a virtual register. try to find a free 88 | // physical register or spill an interval (possibly this one) in order to 89 | // assign it one. 90 | assignRegOrStackSlotAtInterval(cur); 91 | } 92 | --- 93 | 94 | --- RALinScan::assignRegOrStackSlotAtInterval --- 95 | // If the current has the minimum weight, we need to spill it and 96 | // add any added intervals back to unhandled, and restart 97 | // linearscan. 98 | --- 99 | 100 | "When all physical registers are blocked by interfering live ranges in the 101 | active list, a live range is selected for spilling. Live ranges being spilled 102 | without being split first cause the mess that the rewriter is working so hard to 103 | clean up." 104 | 105 | 106 | 使 live range splitting 更加靈活。 107 | 108 | "A new register allocation algorithm needs to preserve this simplicity. It must 109 | be possible to change the machine code while the algorithm is running." 110 | 111 | "A new register allocator should avoid making obvious mistakes so the rewriter 112 | can concentrate on rewriting registers." 113 | 114 | basic & greedy。 115 | 116 | 新增 priority queue 和 live interval unions 兩個資料結構 117 | 118 | 我們這裡提及的 machine instruction 指的是 LLVM MachineInstr (MI),不要和 machine 119 | code (LLVM Machine Code,又稱 MC) 搞混。MC 負責操作 MI 進行目的檔的輸出。 120 | 121 | 122 | 底下是目前 LLVM 後端 codegen 的流程。 123 | 124 | 0. LLVM IR to DAG 125 | 1. DAG to MI lowering (and pre-RA schedule) 126 | 2. MI optimizations (LICM, CSE, etc.) 127 | 3. Register allocation super pass 128 | 3a. De-ssa (2-address, phi slim) 129 | 3b. Coalescing 130 | 3c. Actual register allocation 131 | 4. Post-RA optimizations 132 | 5. PEI 133 | 6. Post-RA scheduling 134 | 135 | blog 文章重點放在 "3c. Actual register allocation" 這個階段。 136 | 137 | 138 | 139 | [1] http://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html 140 | [2] http://www.stanford.edu/class/cs143/lectures/170_Register_Allocation.pdf 141 | [3] http://www.llvm.org/devmtg/2008-08/Cheng_RegisterAllocation.pdf 142 | [4] http://llvm.org/devmtg/2009-10/RegisterAllocationFutureWorks.pdf 143 | [5] http://en.wikipedia.org/wiki/Static_single_assignment_form 144 | 145 | [] http://llvm.org/devmtg/2011-11/Olesen_RegisterAllocation.pdf 146 | [] http://llvm.org/devmtg/2011-11/Grosbach_Anderson_LLVMMC.pdf 147 | 148 | [1] http://llvm.org/docs/CodeGenerator.html#machineinstr 149 | 150 | [] http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-December/046073.html 151 | -------------------------------------------------------------------------------- /LLVM/LLVM-register-allocation-03.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | 我們開始介紹 LLVM linear scan register allocator。以下提到的 linear scan 專指 4 | LLVM linear scan register allocator,主要參考 [1] 和 [3][4] 第 16 頁。 5 | 6 | basic & greedy。 7 | 8 | --- RegAllocBase.h 9 | LiveUnionArray PhysReg2LiveUnion; // Live Interval Unions 10 | --- 11 | 12 | --- class RABasic : public MachineFunctionPass, public RegAllocBase 13 | class RABasic : public MachineFunctionPass, public RegAllocBase 14 | { 15 | // context 16 | MachineFunction *MF; 17 | 18 | // analyses 19 | LiveStacks *LS; 20 | RenderMachineFunction *RMF; 21 | 22 | // state 23 | std::auto_ptr SpillerInstance; 24 | std::priority_queue, 25 | CompSpillWeight> Queue; // Priority Queue 26 | } 27 | 28 | --- RABasic::runOnMachineFunction --- 29 | --- 30 | 31 | --- RegAllocBase::allocatePhysRegs() --- 32 | // Continue assigning vregs one at a time to available physical registers. 33 | while (LiveInterval *VirtReg = dequeue()) { 34 | } 35 | --- 36 | 37 | --- RAGreedy::runOnMachineFunction 38 | allocatePhysRegs(); 39 | --- 40 | 41 | 新增 priority queue 和 live interval unions 兩個資料結構 42 | 43 | 我們這裡提及的 machine instruction 指的是 LLVM MachineInstr (MI),不要和 machine 44 | code (LLVM Machine Code,又稱 MC) 搞混。MC 負責操作 MI 進行目的檔的輸出。 45 | 46 | 47 | 底下是目前 LLVM 後端 codegen 的流程。 48 | 49 | 0. LLVM IR to DAG 50 | 1. DAG to MI lowering (and pre-RA schedule) 51 | 2. MI optimizations (LICM, CSE, etc.) 52 | 3. Register allocation super pass 53 | 3a. De-ssa (2-address, phi slim) 54 | 3b. Coalescing 55 | 3c. Actual register allocation 56 | 4. Post-RA optimizations 57 | 5. PEI 58 | 6. Post-RA scheduling 59 | 60 | blog 文章重點放在 "3c. Actual register allocation" 這個階段。 61 | 62 | 63 | 64 | [1] http://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html 65 | [2] http://www.stanford.edu/class/cs143/lectures/170_Register_Allocation.pdf 66 | [3] http://www.llvm.org/devmtg/2008-08/Cheng_RegisterAllocation.pdf 67 | [4] http://llvm.org/devmtg/2009-10/RegisterAllocationFutureWorks.pdf 68 | [5] http://en.wikipedia.org/wiki/Static_single_assignment_form 69 | 70 | [] http://llvm.org/devmtg/2011-11/Olesen_RegisterAllocation.pdf 71 | [] http://llvm.org/devmtg/2011-11/Grosbach_Anderson_LLVMMC.pdf 72 | 73 | [1] http://llvm.org/docs/CodeGenerator.html#machineinstr 74 | 75 | [] http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-December/046073.html 76 | -------------------------------------------------------------------------------- /LLVM/LLVM-release-testing.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 陳韋任 (Chen Wei-Ren) 2 | 3 | *** 此篇文章技術性不高,純粹是分享替 LLVM 做測試的經驗。*** 4 | 5 | 一切都從 "[3.1 Release] Call For Testers!" 這一封徵求 LLVM 3.1 Release Tester 6 | 的信開始 [1]。我純粹抱著試試看的心態,回信給 Bill Wendling 問他新手如我是否也 7 | 能參與 LLVM 3.1 Release Testing。在約莫一個禮拜之後,Bill 通知所有曾經回信給他 8 | 的人關於 LLVM Testing 大致的流程以及相關注意事項。我這裡簡單做個摘要: 9 | 10 | 1. 一開始 Bill 會要求所有測試者寄給他測試平台的相關資訊,諸如作業系統和編譯器 11 | 版本。Bill 需要這個資訊以便知道測試覆蓋程度如何。以我的例子,我會回復他: 12 | 13 | Two pandaboard: 14 | 15 | arm linux (ubuntu 11.04), gcc 4.5.2 16 | 17 | 注意! 基本上不鼓勵在測試期間更新作業系統和編譯器版本。 18 | 19 | 2. Bill 會在郵件列表上公布測試的時程表,一般測試為期一個月。這次公布的時程如下 20 | 21 | April 16th: Branching for 3.1 release 22 | April 16-22: Phase 1 testing 23 | April 23-29: Bug fixing for Phase 1 issues, all features completed 24 | April 30-May 6: Phase 2 testing 25 | May 7-13: Addressing Phase 2 issues, final binary generation 26 | May 14th: Release of 3.1! 27 | 28 | 基本上是維持一個禮拜測試,下一個禮拜修正錯誤的循環。Bill 會視情況決定是否 29 | 延長測試的時間。 30 | 31 | 3. 在測試期間所發現的臭蟲,一律送交 Bugzilla (http://llvm.org/bugs)。Bill 會要 32 | 求你回報臭蟲的同時,將他加進 CC list 以便他監控臭蟲修正的進度。基本上,只要 33 | 不是太嚴重的臭蟲都不會阻礙 LLVM release 的時程。另外,非主流平台 (x86 除外) 34 | 上的臭蟲通常都需要測試者幫忙修正。測試者可以在郵件列表或是聊天室尋求幫助。 35 | 36 | 4. ${LLVM_SRC}/utils/release 目錄底下提供幾個腳本協助測試者進行測試。測試分為兩類: 37 | 一類是所謂的回歸測試 (regression test),這類測試位在 ${LLVM_SRC}/test 底下。主 38 | 要用來確保 LLVM/Clang 的正確性。第二類是評量 LLVM 在效能上是否有退步,這類測試 39 | 位在 ${LLVM_SRC}/projects/test-suite。測試分底下三個步驟: 40 | 41 | a. 以不同模式 (Debug、Assert 或是 Release) 編譯 LLVM/Clang 3.1 pre-release 42 | candidate,也就是所謂的 RC (release candidate),並進行回歸測試。這一步最為 43 | 重要,事實上我這次的測試也只有做到這一步。 44 | 45 | b. 下載 LLVM/Clang 3.0 代碼並編譯,運行 test-suite 得到一組分數作為比較基準 46 | (baseline)。 47 | 48 | c. 以編譯好的 LLVM/Clang 3.1 pre-release candidate "手動" 運行 test-suite, 49 | 並得到一組分數。將此分數與前述 baseline 做比較,若有退步便回報。 50 | 51 | 整個測試工具的使用可以參考 LLVM Testing Infrastructure Guide [3]。 52 | 53 | O.K.,現在就等 LLVM 從 trunk 替 3.1 release 開一個 branch,接著就可以準備測試。 54 | Bill 會在郵件列表上發布 "3.1 Has Branched" 的消息 [3]。但別急! 一切都等待 Bill 這位 55 | release manager 的指示,他會通知測試者 release candidate 的代碼該從哪裡下載,並 56 | 給出較之前為詳細的指示。你可能會收到類似 "Release Candidate 1 Ready for Testing" 57 | 的信件,我這裡簡單做個摘要: 58 | 59 | 1. 隨信的附件中會有幾個腳本供測試者使用,你同樣可以在 ${LLVM_SRC}/utils/release 60 | 找到底下幾個腳本: 61 | 62 | * test-release.sh 63 | * findRegressions-simple.py 64 | * findRegressions-nightly.py 65 | 66 | 後兩個是用來比較 test-suite 分數。我主要使用的是 test-release.sh 這支腳本。 67 | 68 | 2. Bill 會描述如何使用 test-release.sh。以測試 LLVM 3.1 RC1 為例, 69 | 70 | $ test-release.sh -release 3.1 -rc 1 -no-64bit -no-compare-files 71 | 72 | 因為我是測試 ARM 平台,所以不測試 64 bit。整個編譯流程跟 GCC 極為類似,總共 73 | 有三個階段。Phase 1 是以系統工具鏈編譯 LLVM/Clang,Phase 2 是使用 Phase 1 74 | 編譯所得的 clang 編譯 LLVM/Clang,Phase 3 是使用 Phase 2 編譯所得的 clang 75 | 編譯 LLVM/Clang,最後比較 Phase 2 和 Phase 3 的結果。因為某些不知名的原因, 76 | Bill 稱此次 Phase 2 與 Phase 3 的目的檔有所不同,但可忽略,他建議我們使用 77 | -no-compare-files 跳過 Phase 2 與 Phase 3 的比較。`test-release.sh -help` 78 | 可以得到更多訊息。注意! 請確定最後執行回歸測試所用的是 Phase 3 的 79 | LLVM/Clang。剛開始進行測試的時候,我多下了 "-disable-clang" 這個選項,導致 80 | 只有 Phase 1 的結果,用 Phase 1 進行回歸測試得到的結果是不準確的。回歸測試 81 | 的結果相當重要,有臭蟲皆需要立即回報給 Bugzilla (http://llvm.org/bugs), 82 | 並 CC 給 Bill,由他決定該臭蟲是否會阻礙 LLVM release 的時程。 83 | 84 | 3. 分別以 LLVM 3.0 和 LLVM 3.1 RC 運行 test suite,並以 findRegressions-simple.py 85 | 和 findRegressions-nightly.py 比較兩者的結果。這次我並沒有認真地運行 test 86 | suite。 87 | 88 | 4. 打包 Phase 3 所得結果,並交由 Bill 上傳至 LLVM 官網。基本指令如下: 89 | 90 | $ cd rc1/Phase3/Release 91 | $ tar zcvf llvmCore-3.1-rc1.install-arm-ubuntu_11.04.tar.gz llvmCore-3.1-rc1.install/ 92 | 93 | 至此就算是告一個段落,爾後的 RC2 亦或是 RC3 基本上都是依循上述流程。這次測試過程 94 | 中有個小插曲,RC2 的代碼在 ARM 上編譯不過。這時要盡快回報給 Bill,同時自己這邊也 95 | 要做 bisect 抓出出錯的版本號。基本流程如下: 96 | 97 | 1. 取得正常可運作的版本號,這裡我取 RC1 (r155062)。 98 | 99 | $ svn log http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_31/rc1 --stop-on-copy 100 | ------------------------------------------------------------------------ 101 | r155062 | void | 2012-04-19 06:10:56 +0800 (四, 19 4 2012) | 1 line 102 | 103 | Creating release candidate rc1 from release_31 branch 104 | 105 | 2. 取得出錯的版本號,這裡我取 RC2 (r156037)。 106 | 107 | $ svn log http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_31/rc2 --stop-on-copy 108 | ------------------------------------------------------------------------ 109 | r156037 | void | 2012-05-03 08:11:00 +0800 (四, 03 5 2012) | 1 line 110 | 111 | Creating release candidate rc2 from release_31 branch 112 | 113 | 3. 透過 svn-bisect 用二分法找出一開始出問題的版本。注意! LLVM 和 Clang 114 | 分屬不同的 SVN,但是在糾錯時請保持 LLVM 和 Clang 的版本一致。 115 | 116 | $ cd rc2/llvm.src 117 | # 好 壞 118 | $ svn-bisect start 155062 156037 119 | Switching to r155672 ... 120 | # 手動將 Clang 切換至 r155672 121 | $ cd rc2/cfe.src 122 | $ svn up -r 155672 123 | # 開始編譯。-no-checkout 是要求直接使用當前目錄下的代碼,不從 SVN checkout。 124 | $ cd rc2 125 | $ ./test-release.sh -release 3.1 -rc 2 -no-checkout -no-64bit -no-compare-files 126 | # 如果 r155672 編譯出錯,將其標定為 bad,svn-bisect 會切換到下一個應該要檢測的 127 | # 版本。 128 | $ svn-bisect bad 155672 129 | Switching to r155374 ... 130 | 131 | 幸運的是,在我 bisect 出錯誤版本之前,Bill 已經先我一步找到出問題的版本號。 132 | 要知道在 ARM 機器上編譯 LLVM/Clang 可以頗為耗時的一件事。:-) 133 | 134 | 4. 等待 Bill 重新發布下一個修正過後的測試版本,RC3。 135 | 136 | 最後進入 final release 時,測試者只需編譯好 LLVM/Clang,無須再跑回歸測試或是 137 | test suite,直接將 Phase 3 打包送給 Bill 即可。剩下的就是靜待 LLVM 3.1 Release 138 | 的發布。我這裡提一下提交臭蟲至 Bugzilla (http://llvm.org/bugs) 時,應該要有的 139 | 格式。以 "Test case Sema/arm-neon-types.c fail on ARM" [4] 為例,標題寫明是 140 | 在 ARM 平台上,Sema/arm-neon-types.c 這個測試失敗。在內容描述裡面,把 `make 141 | check-all` 所吐出的訊息貼上去。基本上 test-release.sh 這支腳本在編譯完 Phase 142 | 3 LLVM/Clang 後會運行回歸測試,即 `make check-all`,同時會留下 log。你只需要 143 | 將該 log 的內容檢視一遍,將出錯的測試回報給 Bugzilla 即可。非 x86 平台的測試 144 | 者需要自己主動修正錯誤,你可以在郵件列表和聊天室尋求幫助。 145 | 146 | 修正臭蟲之後就可以送 patch,以 "Fix test case failure due to C++ ABI difference 147 | on ARM" 為例 [5],標題簡明扼要的描述這個 patch 修正了什麼問題,信件正文再較為 148 | 詳細的敘述這個 patch 的意圖,必要時附帶上輔助資料,最後以附件形式附上 patch。 149 | 150 | 整個測試過程中,如果有 "任何" 疑問,不要遲疑,發信到郵件列表或是 release manager 151 | 尋求協助。最後,你可以在 Chris Lattner 發布的 "LLVM 3.1 Release!" 公告上看見你的 152 | 名字 [6]。 153 | 154 | 本篇文章沒有技術含量,純粹是希望鼓勵更多人參與 LLVM 的開發,不論是以何種形式。:-) 155 | 156 | 157 | [1] http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-March/048355.html 158 | [2] http://llvm.org/docs/TestingGuide.html 159 | [3] http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-April/048961.html 160 | [4] http://llvm.org/bugs/show_bug.cgi?id=12694 161 | [5] http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20120430/056982.html 162 | [6] http://lists.cs.uiuc.edu/pipermail/llvm-announce/2012-May/000041.html 163 | -------------------------------------------------------------------------------- /MISC/Why-We-Created-Julia.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | 原文: http://julialang.org/blog/2012/02/why-we-created-julia/ 4 | 5 | 簡單來說,因為我們很貪心。 6 | 7 | 我們是重度 Matlab 使用者。其中有些人是 Lisp 黑客,有些人是 Python 愛好者,其他人 8 | 是 Ruby 愛好者,甚至有人是 Perl 黑客。我們其中有些人在我們毛還沒長齊之前就在使用 9 | Mathematica。有些人甚至是女性。我們使用 R 語言 [1] 產生許多統計圖型。C 是我們最為 10 | 喜愛的語言 [2]。 11 | 12 | 我們熱愛所有這些語言,它們是如此美妙且強大。就我們所工作的領域 - 科學計算,機器 13 | 學習,資料探勘,大規模線性計算,分散與平行計算 - 每一種語言對某些領域來說堪稱完 14 | 美,但對其它領域卻是惡夢。使用一種語言都是一種權衡的藝術。 15 | 16 | 我們很貪心,我們想要更多。 17 | 18 | 我們想要一種語言,它必須是開源的,採自由授權。我們希望 C 的速度,Ruby 的動態性。 19 | 我們想要一種語言,其資料和代碼同一格式 (homoiconic),同 Lisp 一般擁有真的巨集, 20 | 但卻使用像 Matlab 那樣明顯和熟悉的數學符號來加以表示。我們想要一種語言,對於各 21 | 類領域的問題都如此好用,如 Python。對於統計方面,像 R 一樣容易使用。對於字串處 22 | 理,又像使用 Perl 一般自然。對於線性代數,像 Matlab 般強大。像 shell 一樣,可以 23 | 用來膠合程序中其它的組件。易於學習,卻又能讓最為嚴肅的黑客高興。我們希望它是互 24 | 動式,又同時是編譯式的語言。 25 | 26 | (我們有提到它必須有 C 一般的速度,對吧?) 27 | 28 | 我們是苛刻的,我們相要一種語言能提供如 Hadoop 那樣強大的分散式計算 - 沒有臃腫的 29 | Java 和 XML 代碼; 不需要被強迫去過濾放在數以百計機器上的海量日誌來找出臭蟲。我 30 | 們想要語言有強大的表達能力,卻又沒有令人費解的複雜性。我們想要寫出一個簡單、做 31 | 純量計算的迴圈,它可以被編譯成只使用到暫存器的機器碼。我們想要寫出 A * B 的矩陣 32 | 計算,並同時在數千台機器上發起數千個計算,計算龐大的矩陣乘積。 33 | 34 | 我們不想提到型別,當我們不喜歡它的時候。但當我們需要多型函式,我們希望使用範型編 35 | 程撰寫演算法,只寫一次,並將該演算法套用在無窮多的型別上。我們希望使用多分派來有 36 | 效的根據函式所有參數選擇一個最適當的實現,並為截然不同的型別提供共通的功能。僅管 37 | 擁有如此強大的能力,我們希望這個語言能簡單且乾淨。 38 | 39 | 我們似乎要求太多了,是嗎? 40 | 41 | 即使我們認識到我們是無可救藥的貪婪,我們仍舊想要上述所有的特性。大約在兩年半以前, 42 | 我們開始創造一種滿足我們貪婪的語言。它還不完備,但是該是時候發布 1.0 正式版了 - 43 | 我們所創造的語言叫做 Julia。它已經滿足我們 90% 無理的要求,現在它需要其他人無理的 44 | 要求讓它更完美。所以,如果你同樣也是一個貪婪、不可理喻、苛刻的程序員,我們希望 45 | 你前來一試。 46 | 47 | 48 | [1] http://en.wikipedia.org/wiki/R_(programming_language) 49 | [2] http://people.cs.nctu.edu.tw/~chenwj/log/LLVM/jey-2012-03-08.txt 50 | -------------------------------------------------------------------------------- /QEMU/KVM-allocate-vmcs.txt: -------------------------------------------------------------------------------- 1 | KVM_CREATE_VCPU --> kvm_vm_ioctl (kvm_main.c) 2 | 3 | | 4 | v 5 | 6 | kvm_vm_ioctl_create_vcpu (kvm_main.c) 7 | 8 | | 9 | v 10 | 11 | kvm_arch_vcpu_create (x86.c) 12 | 13 | | 14 | v 15 | 16 | kvm_x86_ops->vcpu_create (vmx_create_vcpu in vmx.c) 17 | 18 | | 19 | v 20 | 21 | alloc_vmcs (vmx.c) ---> vmx_vcpu_setup (vmx.c) 22 | 23 | | | 24 | v v 25 | 26 | alloc_vmcs_cpu (vmx.c) vmcs_writel (vmx.c) 27 | -------------------------------------------------------------------------------- /QEMU/KVM-flow.txt: -------------------------------------------------------------------------------- 1 | QEMU (user mode) KVM (kernel mode) Guest VM (guest mode) 2 | 3 | Issue Guest 4 | --> ------------- 5 | | Execution ioctl | 6 | | | 7 | | (1) | 8 | | v 9 | | VMLAUNCH/VMRESUME 10 | | --> Enter Guest Mode --------------- 11 | | | | 12 | | | | 13 | | | | 14 | | | v 15 | | | 16 | | | Execute natively 17 | | | 18 | | | in Guest Mode 19 | | | 20 | | | | 21 | | | | 22 | | | VMEXIT | 23 | | | Handle Exit <--------------- 24 | | | VMCALL/HW trigger 25 | | | | 26 | | | | 27 | | | | 28 | | | v 29 | | Y | 30 | | ------------------- I/O? 31 | | | | 32 | | | | | 33 | | | | | N 34 | | v | | 35 | | | Y v 36 | ---- Handle I/O <----------- Signal 37 | | 38 | (2) | Pending? 39 | | 40 | | | 41 | | | N 42 | | | 43 | --------- 44 | -------------------------------------------------------------------------------- /QEMU/KVM-handle-KVM_RUN.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KVM_RUN --> kvm_vcpu_ioctl (kvm_main.c) 5 | 6 | | 7 | v 8 | 9 | kvm_arch_vcpu_ioctl_run (x86.c) 10 | 11 | | 12 | v 13 | 14 | __vcpu_run (x86.c) 15 | 16 | | 17 | v 18 | 19 | vcpu_enter_guest (x86.c) 20 | 21 | | 22 | v 23 | 24 | kvm_x86_ops->run(vcpu) (vmx_vcpu_run in vmx.c) 25 | -------------------------------------------------------------------------------- /QEMU/KVM-handle-guest-io-instruction.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KVM_RUN ------> __vcpu_run ------> KVM_EXIT ------> Handle IO ------- 5 | | 6 | ^ ^ | 7 | | | v 8 | | | Y 9 | | -------- KVM handle PIO <----------- Can be handled in KVM? 10 | | 11 | | | 12 | | | N 13 | | | 14 | -------------------- QEMU handle PIO <-------------------------- 15 | 16 | 17 | IO emulation in QEMU 18 | ---> KVM_RUN (kvm_cpu_exec in QEMU) 19 | | 20 | | | 21 | | v 22 | | 23 | | kvm_vcpu_ioctl (kvm_main.c) 24 | | 25 | | | 26 | | v 27 | | 28 | | kvm_arch_vcpu_ioctl_run (x86.c) 29 | | 30 | | | 31 | | v 32 | | IO emulation done in KVM, continue vcpu_enter_guest 33 | | __vcpu_run (x86.c) <--------------------------------------------------------------------------------------------------- 34 | | | 35 | | | | 36 | | v | 37 | | | 38 | | vcpu_enter_guest (x86.c) | 39 | | | 40 | | | | 41 | | v | 42 | | VMExit | 43 | | kvm_x86_ops->run (vmx_vcpu_run in vmx.c) --------> kvm_x86_ops->handle_exit (vmx_handle_exit in vmx.c) | 44 | | VMLAUNCH/VMRESUME | 45 | | | | 46 | | v | 47 | | | 48 | | kvm_vmx_exit_handlers[exit_reason] (handle_io in vmx.c) | 49 | | | 50 | | | | 51 | | v | 52 | | | 53 | | kvm_fast_pio_out (x86.c) | 54 | | | 55 | | | | 56 | | v | 57 | | | 58 | | emulator_pio_out_emulated (x86.c) | 59 | | | 60 | | | | 61 | | v | 62 | | | 63 | | emulator_pio_in_out (x86.c) | 64 | | | 65 | | | | 66 | | --------------------------------- | 67 | | | | | 68 | | v v | 69 | | | 70 | | setup kvm_run kernel_pio (x86.c) | 71 | | (return 0) (return 1) | 72 | | | 73 | | | | | 74 | -------------------------------------------- --------------------------------------------- 75 | 76 | 77 | * return 0 means we need to go back to QEMU; return 1 means we can continue vcpu_enter_guest 78 | -------------------------------------------------------------------------------- /QEMU/KVM-introduction-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | 這裡先複習一下,在虛擬化還沒出現之前,整個系統可以劃分成用戶態 (user mode) 和 5 | 內核態 (kernel mode)。 6 | 7 | QEMU (user mode) 8 | -------------------------- 9 | Linux (kernel mode) 10 | 11 | 為了因應虛擬化的要求,x86 多加了一層特權級: 根模式 (root mode) 和非根模式 12 | (non-root mode)。非根模式一般又稱 guest mode。 13 | 14 | ------------------------------ 15 | | Guest App (user mode) | 16 | non-root | -------------------------- | Gust VM 17 | | Guest OS (kernel mode) | 18 | ------------------------------------------ 19 | QEMU (user mode) 20 | root -------------------------- 21 | Linux/KVM (kernel mode) 22 | 23 | 一般我們在運行應用程序時,是處在根模式,只有在運行虛擬機的時候,才會由根模式 24 | 切換至非根模式運行客戶機。客戶機在運行仍有其用戶態和內核態,只是這是非根模式 25 | 下的用戶態和內核態。 26 | 27 | 使用 KVM 只需要在 QEMU 命令行中加上 "-enable-kvm"。 28 | 29 | $ qemu-system-i386 -enable-kvm linux-0.2.img -vnc 0.0.0.0:1 30 | 31 | QEMU 內部原本使用動態翻譯 (binary translation) 即 TCG 的方式運行客戶機,在將 32 | KVM 的邏輯加入 QEMU 之後,僅在 CPU 虛擬化的部分有所更動,至於內存和 IO 虛擬化 33 | 基本上不變。在 QEMU 代碼中,你會看到如下的條件分支: 34 | 35 | if (kvm_enabled()) { 36 | 37 | ... KVM way ... 38 | 39 | } else if (tcg_enabled()) { 40 | 41 | ... TCG way ... 42 | 43 | } 44 | 45 | 它會用 kvm_enabled 和 tcg_enabled 來區分現在要走 KVM 亦或是 TCG 的代碼。 46 | 47 | 這裡先概述 QEMU/KVM 的運作方式 [1][2]。 48 | 49 | --------------------- 50 | | ---------------- | 51 | | | | | 52 | | | Guest App | | 53 | | |---------------| | 54 | | | | | 55 | | | Guest OS | | 56 | | |_______________| | 57 | | Guest VM | 58 | |___________________| QEMU 59 | /dev/kvm (user mode) 60 | ------------------------------------------------------------- 61 | kvm.ko 62 | --------------------------- Linux (kernel mode) 63 | kvm-intel.ko | kvm-amd.ko 64 | 65 | 66 | KVM 以內核模組 (kvm.ko) 的形式掛載進 Linux 內核。以 x86 為例,分別有 Intel 的 67 | VMX 和 AMD 的 SVM 對 CPU 虛擬化的實現。所以 kvm.ko 下層視情況會是 vmx.ko 或是 68 | svm.ko。先忽略 QEMU 的部分,我們先來看 KVM。KVM 將自身以 /dev/kvm 的形式暴露給 69 | 上層應用程序。一般使用的流程如下: 70 | 71 | 1. 開啟 /dev/kvm 取得 KVM fd。 72 | 73 | 2. QEMU 對 KVM fd 發出 KVM_CREATE_VM 命令,取得 VM fd。VM fd 代表一個客戶機。 74 | 75 | 3. QEMU 對 VM fd 發出 KVM_CREATE_VCPU 命令,取得 VCPU fd。客戶機中每一個 VCPU 76 | 都要有一個相對應的 VCPU fd。 77 | 78 | 4. QEMU 對 VCPU fd 發出 KVM_RUN 命令,從根模式用戶態切換至根模式內核態進入 79 | KVM,KVM 再透過 VMEntry 運行客戶機,此時從根模式內核態切換至非根模式。 80 | 81 | [1] http://blog.vmsplice.net/2011/03/qemu-internals-big-picture-overview.html 82 | [2] http://blog.vmsplice.net/2011/03/qemu-internals-overall-architecture-and.html 83 | -------------------------------------------------------------------------------- /QEMU/KVM-mmu.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | 5 | Y 6 | mov (%ebx) %eax ---> TLB Hit? ---> physical address ---> 7 | ^^^^^ | Memory 8 | virtual address | N data <--- 9 | | 10 | v 11 | Walk 12 | Page Table 13 | (VA -> PA) 14 | 15 | cr3 --> ________ 16 | | | 17 | | | --> ________ 18 | | | | | 19 | |________| | | --> physical address 20 | | | 21 | |________| 22 | 23 | 24 | VA: Virtual Address 25 | PA: Physical Address 26 | 27 | 28 | 1. Shadow Page Table (影子頁表) 29 | 30 | guest 31 | page table (GVA -> GPA) 32 | ________ 33 | | | 34 | | | 35 | | | 36 | --> |________| 37 | | 38 | | 39 | fake cr3 (guest vm) 40 | ---------------------------------------------- 41 | mov (%ebx) %eax --> cr3 (hardware) 42 | | shadow 43 | | page table (GVA -> HPA) 44 | v ________ 45 | | | 46 | | | 47 | | | 48 | |________| 49 | 50 | 51 | 2. EPT/NPT 52 | 53 | guest 54 | page table (GVA -> GPA) 55 | ________ 56 | | | 57 | | | 58 | | | ---> yyyy 59 | (1) --> |________| 60 | | 61 | xxxx | 62 | mov (%ebx) %eax --> cr3 (guest vm) 63 | ------------------------------------------- 64 | eptp 65 | | extended 66 | | page table (GPA -> HPA) 67 | v ________ 68 | (2) | | 69 | | | 70 | yyyy --> | | ---> zzzz ---> Memory 71 | |________| 72 | 73 | 74 | -------------------------------------------------------------------------------- /QEMU/KVM-module-entry.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vmx_init (vmx.c) 6 | 7 | | 8 | v 9 | 10 | kvm_init (kvm_main.c) 11 | 12 | | 13 | v 14 | 15 | kvm_arch_init (kvm_main.c) --> kvm_arch_hardware_setup (x86.c) 16 | 17 | | | 18 | v v 19 | 20 | kvm_timer_init (x86.c) kvm_x86_ops->hardware_setup (hardware_setup in vmx.c) 21 | 22 | | 23 | v 24 | 25 | setup_vmcs_config (vmx.c) --> alloc_kvm_area (vmx.c) 26 | 27 | | For each VCPU, do the following 28 | v 29 | 30 | alloc_vmcs_cpu (vmx.c) 31 | 32 | -------------------------------------------------------------------------------- /QEMU/QEMU - block chaining.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/QEMU/QEMU - block chaining.ppt -------------------------------------------------------------------------------- /QEMU/QEMU - cpu-interrupt.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/QEMU/QEMU - cpu-interrupt.ppt -------------------------------------------------------------------------------- /QEMU/QEMU-access-guest-memory-01.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 目前 QEMU 存取客戶機虛擬位址 (GVA) 時,會先將 GVA -> HVA,再用 mov 指令 4 | 讀取該 HAV 上的資料。 5 | 6 | (1) TLB check 7 | (2) If hit fall through, else jump to TLB miss case (5) 8 | (3) TLB hit case: Load value from host memory 9 | (4) Jump to next code (6) 10 | (5) TLB miss case: call MMU helper 11 | (6) ... (next code) 12 | 13 | IN: 14 | 0x000f1e6b: mov (%edi),%dl 15 | 16 | OP: 17 | ---- 0xf1e6b 18 | mov_i32 tmp2,edi 19 | qemu_ld8u tmp0,tmp2,$0x0 20 | deposit_i32 edx,edx,tmp0,$0x0,$0x8 21 | 22 | OUT: 23 | 0x41e27980: mov 0x1c(%r14),%ebp // mov_i32 tmp2,edi 24 | 0x41e27984: mov %ebp,%esi // (1) %ebp = guest virtual addr 25 | 0x41e27986: mov %ebp,%edi 26 | 0x41e27988: shr $0x7,%esi 27 | 0x41e2798b: and $0xfffff000,%edi // %edi = guest page frame 28 | 0x41e27991: and $0x1fe0,%esi // %esi = [19..12] of guest virtual addr 29 | 0x41e27997: lea 0x378(%r14,%rsi,1),%rsi // use %esi to index tlb_table, get CPUEntry addr 30 | 0x41e2799f: cmp (%rsi),%edi // same page frame with guest virtual addr? 31 | 0x41e279a1: mov %ebp,%edi // %edi = guest virtual addr, since latter we will store host virtual addr into %ebp 32 | 0x41e279a3: jne 0x41e279ae // (2) ---------- 33 | 0x41e279a5: add 0x10(%rsi),%rdi // (3) | %rdi = host virtual addr 34 | 0x41e279a9: movzbl (%rdi),%ebp // (a) | 35 | 0x41e279ac: jmp 0x41e279b8 // (4) -------|------ 36 | 0x41e279ae: xor %esi,%esi // (5) <--------- | 37 | 0x41e279b0: callq 0x63a6c1 // | 38 | 0x41e279b5: movzbl %al,%ebp | 39 | 0x41e279b8: mov 0x8(%r14),%ebx // (6) <------------- 40 | 0x41e279bc: mov %ebp,%ecx 41 | 0x41e279be: mov %cl,%bl 42 | 43 | - tcg_out_tlb_load (tcg/i386/tcg-target.c) 44 | 45 | static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx, 46 | int mem_index, int s_bits, 47 | const TCGArg *args, 48 | uint8_t **label_ptr, int which) 49 | { 50 | 51 | tcg_out_mov(s, type, r1, addrlo); // mov %ebp,%esi 52 | tcg_out_mov(s, type, r0, addrlo); // mov %ebp,%edi 53 | 54 | // shr $0x7,%esi 55 | tcg_out_shifti(s, SHIFT_SHR + rexw, r1, 56 | TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); 57 | 58 | // and $0xfffff000,%edi 59 | tgen_arithi(s, ARITH_AND + rexw, r0, 60 | TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0); 61 | 62 | // and $0x1fe0,%esi 63 | tgen_arithi(s, ARITH_AND + rexw, r1, 64 | (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0); 65 | 66 | // lea 0x378(%r14,%rsi,1),%rsi 67 | tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, r1, TCG_AREG0, r1, 0, 68 | offsetof(CPUArchState, tlb_table[mem_index][0]) 69 | + which); 70 | 71 | /* cmp 0(r1), r0 */ 72 | tcg_out_modrm_offset(s, OPC_CMP_GvEv + rexw, r0, r1, 0); // cmp (%rsi),%edi 73 | 74 | tcg_out_mov(s, type, r0, addrlo); // mov %ebp,%edi 75 | 76 | // jne 0x41e279ae 77 | /* jne label1 */ 78 | tcg_out8(s, OPC_JCC_short + JCC_JNE); 79 | label_ptr[0] = s->code_ptr; // 留待 tcg_out_qemu_ld 改寫 80 | s->code_ptr++; 81 | 82 | /* TLB Hit. */ 83 | 84 | // add 0x10(%rsi),%rdi 85 | /* add addend(r1), r0 */ 86 | tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r0, r1, 87 | offsetof(CPUTLBEntry, addend) - which); 88 | } 89 | 90 | - tcg_out_qemu_ld (tcg/i386/tcg-target.c) 91 | 92 | static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, 93 | int opc) 94 | { 95 | 96 | tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args, 97 | label_ptr, offsetof(CPUTLBEntry, addr_read)); 98 | 99 | // movzbl (%rdi),%ebp 100 | /* TLB Hit. */ 101 | tcg_out_qemu_ld_direct(s, data_reg, data_reg2, 102 | tcg_target_call_iarg_regs[0], 0, opc); - (a) 103 | 104 | // jmp 0x41e279b8 105 | /* jmp label2 */ 106 | tcg_out8(s, OPC_JMP_short); 107 | label_ptr[2] = s->code_ptr; 108 | s->code_ptr++; 109 | 110 | /* TLB Miss. */ 111 | 112 | /* label1: */ 113 | *label_ptr[0] = s->code_ptr - label_ptr[0] - 1; 114 | 115 | // http://lists.gnu.org/archive/html/qemu-devel/2012-07/msg00578.html 116 | /* XXX: move that code at the end of the TB */ 117 | 118 | // callq 0x63a6c1 119 | tcg_out_calli(s, (tcg_target_long)qemu_ld_helpers[s_bits]); 120 | 121 | /* movzbl %al,%ebp */ 122 | 123 | /* label2: */ 124 | *label_ptr[2] = s->code_ptr - label_ptr[2] - 1; 125 | 126 | } 127 | -------------------------------------------------------------------------------- /QEMU/QEMU-access-guest-memory-02.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TCG IR -> host binary 7 | 8 | - tcg_gen_code_common (tcg/tcg.c) 9 | 10 | static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, 11 | long search_pc) 12 | { 13 | ... 略 ... 14 | 15 | for(;;) { 16 | opc = gen_opc_buf[op_index]; 17 | def = &tcg_op_defs[opc]; 18 | 19 | switch(opc) { 20 | case INDEX_op_mov_i32: 21 | 22 | ... 略 ... 23 | 24 | default: 25 | /* Sanity check that we've not introduced any unhandled opcodes. */ 26 | if (def->flags & TCG_OPF_NOT_PRESENT) { 27 | tcg_abort(); 28 | } 29 | /* Note: in order to speed up the code, it would be much 30 | faster to have specialized register allocator functions for 31 | some common argument patterns */ 32 | dead_args = s->op_dead_args[op_index]; 33 | tcg_reg_alloc_op(s, def, opc, args, dead_args); 34 | break; 35 | } 36 | args += def->nb_args; 37 | next: 38 | if (search_pc >= 0 && search_pc < s->code_ptr - gen_code_buf) { 39 | return op_index; 40 | } 41 | op_index++; 42 | } 43 | } 44 | 45 | - tcg_reg_alloc_op 46 | 47 | static void tcg_reg_alloc_op(TCGContext *s, 48 | const TCGOpDef *def, TCGOpcode opc, 49 | const TCGArg *args, 50 | unsigned int dead_args) 51 | { 52 | /* copy constants */ 53 | memcpy(new_args + nb_oargs + nb_iargs, 54 | args + nb_oargs + nb_iargs, 55 | sizeof(TCGArg) * def->nb_cargs); 56 | 57 | /* satisfy input constraints */ 58 | 59 | if (def->flags & TCG_OPF_BB_END) { 60 | tcg_reg_alloc_bb_end(s, allocated_regs); 61 | } else { 62 | 63 | if (def->flags & TCG_OPF_CALL_CLOBBER) { 64 | for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { 65 | if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { 66 | tcg_reg_free(s, reg); 67 | } 68 | } 69 | save_globals(s, allocated_regs); 70 | } 71 | 72 | } 73 | 74 | /* emit instruction */ 75 | tcg_out_op(s, opc, new_args, const_args); 76 | 77 | } 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /QEMU/QEMU-access-guest-memory-flow.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | (2) Hit 5 | Save Dirty State ---> Lookup TLB Cache ------> Access Memory 6 | (1) ^ 7 | | | 8 | | | 9 | | | 10 | | v 11 | | (3) Miss 12 | | Lookup Page Table -------> Raise Guest Exception, 13 | | Leave Code Cache 14 | | | 15 | | | 16 | | | 17 | | v 18 | | 19 | ------ Set TLB Cache 20 | 21 | 22 | 23 | qemu_ld/qemu_st (GVA xxx) 24 | 25 | | 26 | v 27 | Y 28 | env->tlb_table (GVA -> HVA) -------> TLB Hit? -----> (HVA yyy) mov yyy %r1 29 | | 30 | | | N 31 | v 32 | tlb_set_page 33 | __{st,ld}{b,w,l,q}_mmu 34 | ^ 35 | | | 36 | | v 37 | | Y 38 | -------- tlb_fill 39 | (walk guest page table) 40 | 41 | | N (guest page fault) 42 | v 43 | 44 | cpu_restore_state 45 | 46 | | 47 | v 48 | 49 | raise_exception_err 50 | 51 | | 52 | v 53 | 54 | go back to cpu_exec to translate/execute 55 | guest OS page fault handler 56 | 57 | 58 | -------------------------------------------------------------------------------- /QEMU/QEMU-block-chaining-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | 先複習一下 QEMU 的流程,目前的情況如底下這樣: 5 | 6 | QEMU -> code cache -> QEMU -> code cache -> ... 7 | 8 | 之前提到,QEMU 是以一個 translation block 為單位進行翻譯並執行。這也就是說,每當 9 | 在 code cache 執行完一個 translation block 之後,控制權必須交還給 QEMU。這很沒有 10 | 效率。理想情況下,大部分時間應該花在 code cache 中執行,只要在必要情況下才需要回 11 | 到 QEMU。基本想法是把 code cache 中的 translation block 串接起來。只要 translation 12 | block 執行完後,它的跳躍目標確定且該跳躍目標也已經在 code cache 裡,那我們就把這 13 | 兩個 translation block 串接起來。這個就叫做 block chaining/linking。 14 | 15 | 在 QEMU 中,達成 block chaining 有兩種做法: 第一,採用 direct jump。此法直接修 16 | 改 code cache 中分支指令的跳躍目標,因此依據 host 有不同的 patch 方式。第二,則 17 | 是透過修改 TranslationBlock 的 tb_next 欄位達成 block chaining。exec-all.h 中定 18 | 義那些 host 支援使用 direct jump。底下是描述 block chaining 的圖示,direct jump 19 | 的做法,就是將 tb1 尾端 jmp 指令後的跳躍位址 0 寫成 tb2 在 code cache 中的起始 20 | 位址 xxx。 21 | 22 | code cache 23 | ---------------------- 24 | prolog/epilog | | 25 | ----------------- | tb1 | 26 | -> | | -> | ---------------- | 27 | cpu_exec -> tcg_qemu_tb_exec | | | | | | 28 | (as function call) | | | |jmp 0 | | 29 | | | | |jmp tb_ret_addr | | 30 | <- | tb_ret_addr | <- | ---------------- | 31 | ----------------- | | | 32 | | v | 33 | | tb2 (xxx) | 34 | | | 35 | ---------------------- 36 | 37 | 這裡只介紹 direct jump。我們先回到 cpu_exec (cpu-exec.c) 的內層迴圈。 38 | 39 | tb = tb_find_fast(env); 40 | 41 | if (tb_invalidated_flag) { 42 | next_tb = 0; // 注意! next_tb 也被用來控制是否要做 block chaining。 43 | tb_invalidated_flag = 0; 44 | } 45 | 46 | // 注意!! next_tb 的名字會讓人誤解。block chaining 的方向為: next_tb -> tb。 47 | // next_tb 不為 NULL 且 tb (guest binary) 不跨 guest page 的話,做 block 48 | // chaining。原因之後再講。 49 | if (next_tb != 0 && tb->page_addr[1] == -1) { 50 | // 這邊利用 TranlationBlock 指針的最低有效位後兩位指引 block chaining 的方向。 51 | tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); 52 | } 53 | 54 | // 執行 TB,也就是 tc_ptr 所指到的位址。注意,產生 TCG IR 的過程中,在 block 的最後會是 55 | // exit_tb addr,此 addr 是正在執行的 TranslationBlock 指針,同時也是 tcg_qemu_tb_exec 的 56 | // 回傳值。該位址後兩位會被填入 0、1 或 2 以指示 block chaining 的方向。 57 | // http://people.cs.nctu.edu.tw/~chenwj/log/QEMU/pm215-2012-07-12.txt 58 | next_tb = tcg_qemu_tb_exec(tc_ptr); 59 | 60 | 是不是有點暈頭轉向? 我們先了解 tcg_qemu_tb_exec 回傳值到底為何。我們來仔細檢驗 61 | guest binary -> TCG IR -> host binary 到底怎麼做的。在一個 translation block 的 62 | 結尾,TCG 都會產生 TCG IR "exit_tb"。我們來看看。:-) 63 | 64 | - tcg_gen_exit_tb (tcg/tcg-op.h) 呼叫 tcg_gen_op1i 生成 TCG IR,其 opcode 為 65 | INDEX_op_exit_tb (還記得 tcg.i 裡的 TCGOpcode 嗎?),operand 為 val。 66 | 67 | // 一般是這樣呼叫: tcg_gen_exit_tb((tcg_target_long)tb + tb_num); 68 | // 注意! 請留意其參數: (tcg_target_long)tb + tb_num。 69 | static inline void tcg_gen_exit_tb(tcg_target_long val) 70 | { 71 | // 將 INDEX_op_exit_tb 寫入 gen_opc_buf; val 寫入 gen_opparam_buf。 72 | tcg_gen_op1i(INDEX_op_exit_tb, val); 73 | } 74 | 75 | 我們看個 exit_tb 的小例子。 76 | 77 | IN: 78 | 0x000f1e78: je 0xf1e82 79 | 80 | OP: 81 | ---- 0xf1e78 82 | movi_i32 cc_op,$0xe 83 | movi_i32 tmp12,$0xff 84 | and_i32 tmp4,cc_dst,tmp12 85 | movi_i32 tmp12,$0x0 86 | brcond_i32 tmp4,tmp12,eq,$0x0 87 | goto_tb $0x0 88 | movi_i32 tmp4,$0xf1e7a 89 | st_i32 tmp4,env,$0x20 90 | exit_tb $0x7fb63a81e6a0 <--- (tcg_target_long)tb + tb_num 91 | set_label $0x0 92 | goto_tb $0x1 93 | movi_i32 tmp4,$0xf1e82 94 | st_i32 tmp4,env,$0x20 95 | exit_tb $0x7fb63a81e6a1 <--- (tcg_target_long)tb + tb_num 96 | 97 | - tcg/ARCH/tcg-target.c 根據 TCG IR 產生對應 host binary。以 i386 為例: 98 | (關於每一行的作用,我是憑經驗用猜的。如有錯請指正。) 99 | 100 | static inline void tcg_out_op(TCGContext *s, int opc, 101 | const TCGArg *args, const int *const_args) 102 | { 103 | // 總和效果: 返回 QEMU。 104 | // next_tb = tcg_qemu_tb_exec(tc_ptr); 105 | // 106 | // 圖示: 107 | // struct TranslationBlock* code cache 108 | // -------------- 109 | // next_tb->tc_ptr --->|--------------| 110 | // | tb1 | 111 | // |--------------| 112 | // -------------- 113 | // 114 | // next_tb 的末兩位編碼 next_tb 條件分支的方向。 115 | // 等待下一次迴圈取得 tb = tb_find_fast(), 116 | // 試圖做 block chaining: next_tb -> tb 117 | // 118 | case INDEX_op_exit_tb: 119 | // QEMU 把跳至 code cache 執行當作是函式呼叫,EAX 存放返回值。 120 | // 將 val 寫進 EAX,val 是 (tcg_target_long)tb + tb_num。 121 | tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EAX, args[0]); 122 | 123 | // e9 是 jmp 指令,後面的 operand 為相對偏移量,將會加上 eip。 124 | // 底下兩條的總和效果是跳回 code_gen_prologue 中 tb_ret_addr 的位置。 125 | tcg_out8(s, 0xe9); /* jmp tb_ret_addr */ 126 | 127 | // tb_ret_addr 在 tcg_target_qemu_prologue 初始成指向 code_gen_prologue 128 | // 中 prologue 以後的位置。 129 | // 生成 host binary 的同時,s->code_ptr 會移向下一個 code buffer 的位址。 130 | // 所以要減去 4。jmp 後的 offset 加上 eip 等於目的位址,即 tb_ret_addr。 131 | // 132 | // s->code_ptr = 0x1001 133 | // | 134 | // v 135 | // 0x1000 jmp offset 136 | // eip -> 0x1005 // 執行到 jmp 時,eip 指向下一條指令。 137 | // 138 | // eip + offset = tb_ret_addr 139 | // offset = tb_ret_addr - eip 140 | // = tb_ret_addt - (s->code_ptr + 4) 141 | // 142 | tcg_out32(s, tb_ret_addr - s->code_ptr - 4); 143 | break; 144 | } 145 | 146 | o tcg_out_movi 將 arg 移至 ret 代表的暫存器。 147 | 148 | static inline void tcg_out_movi(TCGContext *s, TCGType type, 149 | int ret, int32_t arg) 150 | { 151 | if (arg == 0) { 152 | /* xor r0,r0 */ 153 | tcg_out_modrm(s, 0x01 | (ARITH_XOR << 3), ret, ret); 154 | } else { 155 | // move arg 至 ret 156 | tcg_out8(s, 0xb8 + ret); // 0xb8 為 move,ret 代表目地暫存器。0xb8 + ret 合成一個 opcode。 157 | tcg_out32(s, arg); 158 | } 159 | } 160 | 161 | 小結一下,QEMU 函式名稱命名慣例為: 162 | 163 | tcg_gen_xxx - 產生 TCG Opcode 和 operand 至 gen_opc_buf 和 gen_opparam_buf。 164 | 165 | tcg_out_xxx - 產生 host binary 至 TCGContext 所指的 code cache。 166 | 167 | -------------------------------------------------------------------------------- /QEMU/QEMU-block-chaining-02.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | 我們再回到 cpu_exec (cpu-exec.c) 的內層迴圈。這邊主要看 tb_add_jump。 5 | 6 | if (next_tb != 0 && tb->page_addr[1] == -1) { 7 | // 這邊利用 TranlationBlock 指針的最低有效位後兩位指引 block chaining 的方向。 8 | // block chaining 方向: next_tb -> tb 9 | tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); 10 | } 11 | 12 | // 這邊要注意到,QEMU 利用 TranslatonBlock 指針後兩位必為零的結果 13 | // 做了一些手腳。QEMU 將其末兩位編碼成 0、1 或 2 來指引 block chaing 14 | // 的方向。這種技巧在 QEMU 用得非常嫻熟。 15 | next_tb = tcg_qemu_tb_exec(tc_ptr); 16 | 17 | - tb_add_jump (exec-all.h) 做 block chaining 的方向為: tb -> tb_next。 18 | 19 | static inline void tb_add_jump(TranslationBlock *tb, int n, 20 | TranslationBlock *tb_next) 21 | { 22 | if (!tb->jmp_next[n]) { 23 | tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc_ptr); 24 | 25 | tb->jmp_next[n] = tb_next->jmp_first; 26 | tb_next->jmp_first = (TranslationBlock *)((uintptr_t)(tb) | (n)); 27 | } 28 | } 29 | 30 | o tb_add_jump 呼叫 tb_set_jmp_target 做 block chaining 的 patch。另外,會利用 31 | tb 的 jmp_next 和 tb_next 的 jmp_first 把 block chaining 中的 TranslationBlock 32 | 串成一個 circular list。這是以後做 block *unchaining* 之用。再複習一下 33 | TranslationBlock, 34 | code cache 35 | struct TranslationBlock* (host binary) 36 | ---------------- 37 | tb->tc_ptr --> |----------------| 38 | | TB | 39 | |----------------| 40 | | | 41 | ---------------- 42 | 43 | 請注意! 當我們講 TB,可能是指 TranslationBlock,也可能是指 code cache 中的 TB。 44 | 好! 當我們 patch code cache 中的 TB,將它們串接在一起。我們同時也要把它們相對應 45 | 的 TranslationBlock 做點紀錄,利用 jmp_first 和 jmp_next 這兩個欄位。 46 | 47 | 這邊我先回到 tb_link_page,還記得我留下一些地方沒講嘛? 開始吧! ;-) 48 | 49 | // jmp_first 代表跳至此 tb 的其它 TB 中的頭一個。jmp_first 初值為自己,末兩位為 10 (2)。 50 | // 將來做 block chaining 時,jmp_first 指向跳至此 tb 的其它 TB 中的頭一個,tb1,末兩位 51 | // 為 00 或 01,這代表從 tb1 的哪一個分支跳至此 tb。 52 | tb->jmp_first = (TranslationBlock *)((long)tb | 2); 53 | 54 | // jmp_next[n] 代表此 tb 條件分支的目標 TB。 55 | // 注意! 如果目標 TB,tb2,孤身一人,jmp_next 就真的指向 tb2 (符合初次看到 jmp_next 所想 56 | // 的語意)。 57 | // 58 | // 如果已有其它 TB,tb3,跳至 tb2,則賦值給 tb->jmp_next 的是 tb2 的 jmp_first,也就是 59 | // tb3 (末兩位編碼 tb3 跳至 tb2 的方向)。 60 | tb->jmp_next[0] = NULL; 61 | tb->jmp_next[1] = NULL; 62 | 63 | // tb_next_offset 代表此 TB 在 code cache 中分支跳轉要被 patch 的位址 (相對於其 code cache 64 | // 的偏移量),為了 block unchaining 之用。 65 | if (tb->tb_next_offset[0] != 0xffff) 66 | tb_reset_jump(tb, 0); 67 | if (tb->tb_next_offset[1] != 0xffff) 68 | tb_reset_jump(tb, 1); 69 | 70 | 我們再回來看看 tb_add_jump 做了什麼。:-) 71 | 72 | // block chaining 方向為: tb -> tb_next。n 用來指示 tb 條件分支的方向。 73 | static inline void tb_add_jump(TranslationBlock *tb, int n, 74 | TranslationBlock *tb_next) 75 | { 76 | // jmp_next[0]/jmp_next[1] 代表 tb 條件分支的目標。 77 | if (!tb->jmp_next[n]) { 78 | /* patch the native jump address */ 79 | tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr); 80 | 81 | // tb_jmp_remove 會用到 jmp_next 做 block unchaining,這個以後再講。 82 | 83 | // tb_next->jmp_first 初值為自己,末兩位設為 10 (2)。 84 | // 如果已有其它 TB,tb1,跳至 tb_next,則下面這條語句會使得 85 | // tb->jmp_next 指向 tb1 (末兩位代表 tb1 跳至 tb_next 的方向)。 86 | // tb_next->jmp_first 由原本指向 tb1 改指向 tb。 87 | // 有沒有 circular list 浮現在你腦海中? ;-) 88 | tb->jmp_next[n] = tb_next->jmp_first; 89 | 90 | // tb_next 的 jmp_first 指回 tb,末兩位代表由 tb 哪一個條件分支跳至 tb_next。 91 | tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n)); 92 | } 93 | } 94 | 95 | 我們再來看是怎麼 patch code cache 中分支指令的目標地址。:-) 96 | 依據是否採用 direct jump,tb_set_jmp_target (exec-all.h) 有不同做法。 97 | 採用 direct jump 的話,tb_set_jmp_target 會根據 host 呼叫不同的 98 | tb_set_jmp_target1。tb_set_jmp_target1 會用到 TB 的 tb_jmp_offset。 99 | 如果不採用 direct jump 做 block chaining,tb_set_jmp_target 會直接 100 | 修改 TB 的 tb_next。 101 | 102 | - tb_set_jmp_target (exec-all.h)。 103 | 104 | static inline void tb_set_jmp_target(TranslationBlock *tb, 105 | int n, unsigned long addr) 106 | { 107 | unsigned long offset; 108 | 109 | // n 可以為 0 或 1,代表分支跳轉的分向 taken 或 not taken。 110 | offset = tb->tb_jmp_offset[n]; // tb 要 patch 的位址相對於 tb->tc_ptr 的偏移量。 111 | tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr); 112 | } 113 | 114 | - tb_set_jmp_target1 (exec-all.h)。 115 | 116 | #elif defined(__i386__) || defined(__x86_64__) 117 | static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr) 118 | { 119 | /* patch the branch destination */ 120 | *(uint32_t *)jmp_addr = addr - (jmp_addr + 4); // jmp 的參數為 jmp 下一條指令與目標地址的偏移量。 121 | /* no need to flush icache explicitly */ 122 | } 123 | 124 | 你會有這個疑問嗎? 在分支跳轉位址被 patch 到分支跳轉指令之前,它是要跳去哪裡? :-) 125 | 126 | -------------------------------------------------------------------------------- /QEMU/QEMU-block-chaining-limit-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | QEMU 在做 block chaining 時,在兩個地方有所限制。 5 | 6 | - gen_goto_tb (target-i386/translate.c) 要求 next_tb 和 tb 同屬一個 guest page。 7 | 不允許做跨 guest page 的 block chaining。 8 | 9 | static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) 10 | { 11 | if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) || - (a) 12 | (pc & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK)) { - (b) 13 | tcg_gen_goto_tb(tb_num); 14 | gen_jmp_im(eip); 15 | tcg_gen_exit_tb((tcg_target_long)tb + tb_num); 16 | } else { 17 | ... 略 ... 18 | } 19 | } 20 | 21 | (a) (b) 22 | tb->pc --> ________ tb->pc --> ________ 23 | | | | | guest page A 24 | | | --------------------------- 25 | | | | | 26 | |________| s->pc --> |________| 27 | 28 | 29 | pc --> ________ ________ 30 | | | | | 31 | | | pc --> | | 32 | | | | | 33 | |________| |________| 34 | 35 | guest page A guest page B 36 | ------------------------- ------------------------------------ 37 | 38 | - cpu_exec (cpou-exec.c) 不准 next_tb 跳至跨 guest page 的 tb。 39 | 40 | int cpu_exec(CPUArchState *env) 41 | { 42 | for(;;) { 43 | 44 | for(;;) { 45 | 46 | if (next_tb != 0 && tb->page_addr[1] == -1) { 47 | tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); 48 | } 49 | 50 | } 51 | } 52 | } 53 | 54 | ________ 55 | | | next_tb 56 | | | 57 | | | 58 | |________| 59 | 60 | | 61 | v 62 | ________ 63 | guest | | tb 64 | page A | | 65 | -------------------------- 66 | guest | | 67 | page B |________| 68 | 69 | 70 | 總結: 71 | 72 | 1. 不允許做跨 guest page 的 block chaining。 73 | 74 | 2. 不准 next_tb 跳至跨 guest page 的 tb。 75 | -------------------------------------------------------------------------------- /QEMU/QEMU-block-chaining-limit-02.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 為何底下情況 QEMU 能正確處理? 4 | 5 | ____________ 6 | | | 7 | | | guest page 1 (4MB) 8 | | | 9 | tb.a | 10 nop | 10 | ------------------------------------- 11 | tb.b | 10 nop | 12 | | 1 ret | guest page 2 (4MB) 13 | | | (unmapped) 14 | |____________| 15 | 16 | 17 | clear env->tb_jmp_cache 18 | invlpg --> tlb_flush_page/tlb_flush -------------------------> tb_find_fast 19 | 20 | (1) 21 | -----> tb_find_slow ---> get_page_addr_code (ldub_code -> __ldb_cmmu -> tlb_fill) 22 | 23 | (2) 24 | -----> guest page fault handler 25 | 26 | (1) tb_find_fast failed since tb_jmp_cache is clear. 27 | 28 | (2) tlb_table changed, raise guest exception 29 | 30 | 31 | The whole point is the control needed to be gave back to QEMU, 32 | so that it has chance to call tb_find_fast/tb_find_slow, then in 33 | tb_find_slow it will raise guest exception to handle the problem. 34 | If we direct link to a TB spanning guest pages, QEMU has no chance 35 | to call tb_find_fast/tb_find_slow. 36 | 37 | 38 | $ git clone git://jcmvbkbc.spb.ru/dumb/qemu-test-kernel.git 39 | $ cd qemu-test-kernel 40 | $ ./autogen.sh 41 | $ ./configure 42 | $ make 43 | $ qemu-system-x86_64 -kernel docs/kernel -D qemu.log -d in_asm,cpu,exec,int 44 | 45 | - doc/kernel.c 46 | 47 | uint32_t page_directory[1024] __attribute__((aligned(4096))); // 4MB 頁目錄表 48 | uint32_t page_table[1024] __attribute__((aligned(4096))); // 4MB 頁表 49 | 50 | static void start_paging(void) 51 | { 52 | unsigned i; 53 | // 填充頁表項 54 | for (i = 0; i < ARRAY_SIZE(page_table); ++i) 55 | page_table[i] = (i << 12) | 3; 56 | 57 | // 將頁目錄表首項指向頁表 58 | page_directory[0] = ((uint32_t)page_table) | 3; 59 | 60 | // 將 cr3 指向頁目錄表,開啟分頁功能 61 | asm __volatile__ ( 62 | "movl %0, %%cr3\n" 63 | "movl %%cr0, %0\n" 64 | "orl $0x80000000, %0\n" 65 | "movl %0, %%cr0\n" 66 | : : "r"(page_directory) : "memory"); 67 | } 68 | 69 | void 70 | cmain (unsigned long magic, unsigned long addr) 71 | { 72 | multiboot_info_t *mbi; 73 | 74 | start_paging(); // 開啟分頁 75 | make_test_code(); // 填充 code cache 76 | test_code(); // 跳至 code cache 執行 77 | 78 | ... 79 | 80 | } 81 | 82 | - make_test_code 83 | 84 | uint8_t code_buf[8192] __attribute__((aligned(4096))); // 8MB,兩頁的 code cache 85 | 86 | static void make_test_code(void) 87 | { 88 | unsigned i; 89 | for (i = 0; i < 20; ++i) 90 | code_buf[4096 - 10 + i] = 0x90; 91 | code_buf[4096 + 10] = 0xc3; 92 | 93 | } 94 | 95 | code_buf ---> ____________ 96 | | | 97 | | | guest page 1 (4MB) 98 | | | 99 | f ---> | 10 nop | tb.a 100 | ------------------------------------ 101 | code_pfn ---> | 10 nop | tb.b 102 | | 1 ret | 103 | | | guest page 2 (4MB) 104 | |____________| 105 | 106 | 107 | - test_code 108 | 109 | static void test_code(void) 110 | { 111 | void (*f)(void) = (void*)(code_buf + 4096 - 10); 112 | uint32_t code_pfn = (uint32_t)(code_buf + 4096) >> 12; 113 | f(); // 順利執行 ret 114 | 115 | page_table[code_pfn] = 0; // 將 guest page 2 invalid 116 | 117 | asm __volatile__ ( 118 | "invlpg (%0)\n" 119 | : : "r"(code_buf + 4096) :"memory"); // QEMU flush_tlb 120 | 121 | // tb_find_fast -> tb_find_slow -> get_page_addr_code -> raise guest page fault 122 | f(); 123 | } 124 | 125 | 上述範例展示 QEMU 為何能正確處理跨 guest page 的 TranslationBlock。下面的範例 126 | 展示當我們移除前文第二條限制時,QEMU 會死機。 127 | 128 | - 129 | static void make_test_code(void) 130 | { 131 | unsigned i; 132 | code_buf[4096 - 20] = 0xeb; 133 | code_buf[4096 - 19] = 8; 134 | for (i = 0; i < 20; ++i) 135 | code_buf[4096 - 10 + i] = 0x90; 136 | code_buf[4096 + 10] = 0xc3; 137 | 138 | } 139 | 140 | - 141 | static void test_code(void) 142 | { 143 | void (*f)(void) = (void*)(code_buf + 4096 - 20); 144 | 145 | ... 146 | 147 | } 148 | 149 | code_buf ---> ____________ 150 | | | 151 | | | guest page 1 (4MB) 152 | f ---> | jmp 8 | 153 | | 10 nop | tb.a 154 | ------------------------------------ 155 | code_pfn ---> | 10 nop | tb.b 156 | | 1 ret | 157 | | | guest page 2 (4MB) 158 | |____________| 159 | 160 | - 161 | -------------------------------------------------------------------------------- /QEMU/QEMU-block-chaining-limit-chart.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | guest guest 5 | physical memory virtual memory code cache 6 | ----------- ----------- ------------------- 7 | | | | A | | ------- | 8 | | 1 | |\\\\\\\\\\\| | | tb1 | | 9 | | | | | | |\\\\\\\| | 10 | |-----------| Guest |-----------| | ------- | 11 | | | Page Table | | | | | 12 | | 2 | <---------> | B | | ------- | 13 | | | |///////////| | | tb2 | | 14 | |-----------| |-----------| | |///////| | 15 | | | |///////////| | |///////| | 16 | swap out <-- | 3 | | | | ------- | 17 | | | | C | ------------------- 18 | ----------- ----------- 19 | 20 | 21 | Before: 1 - A, 2 - B, 3 - C 22 | After : 1 - A, 2 - B, 3 - X 23 | 24 | qemu_ld/qemu_st (GVA xxx) 25 | 26 | | 27 | v 28 | Y 29 | env->tlb_table (GVA -> HVA) -------> TLB Hit? -----> (HVA yyy) mov yyy %r1 30 | | 31 | | | N 32 | v 33 | * tlb_set_page 34 | (change GVA -> HVA) __{st,ld}{b,w,l,q}_mmu 35 | 36 | | | 37 | | v 38 | | Y 39 | -------- tlb_fill 40 | (walk guest page table) 41 | 42 | | N (guest page fault) 43 | v 44 | 45 | cpu_restore_state 46 | 47 | | 48 | v 49 | 50 | raise_exception_err 51 | 52 | | 53 | v 54 | 55 | go back to cpu_exec to translate/execute 56 | guest OS page fault handler 57 | 58 | 59 | * tlb_set_page makes old mapping invalid. 60 | 61 | 62 | -------------------------------------------------------------------------------- /QEMU/QEMU-block-unchaining-01.txt: -------------------------------------------------------------------------------- 1 | 3. Block unchaining 2 | 3 | 世界並不總是那麼美好。雖然說 block chaining 可以使大部分的時間都花在 4 | code cache。但在某些情況下,控制權必須交還給 QEMU。這時就需要把之前串 5 | 接起來的 block 打斷。讀者可以再想想看,假設不將串接起來的 block 打斷會 6 | 怎樣? 如果不把串接起來的 block 打斷,那麼 CPU 無法從 code cache 中跳離 7 | 出來。 8 | 9 | - cpu_interrupt 10 | 11 | - cpu_exit 12 | 13 | - tb_phys_invalidate 14 | -------------------------------------------------------------------------------- /QEMU/QEMU-handle-self-modifying-code-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | 自修改代碼 (self-modifying code, SMC) 基本上指的是一個進程 (process) 在執行時期 4 | 修改自身位於內存中的代碼。自修改代碼在以前的年代是必要的。但隨著時代的演進,自修改 5 | 代碼被視為有害。關於自修改代碼的介紹請見 [1][2][3]。然而本篇文章重點不在自修改代碼 6 | 本身,而是在探討 QEMU 如何處理自修改代碼。這裡以 [4] 當作自修改代碼的範例。在現今 7 | 的系統中,基本上會把進程的代碼段設為不可寫。因此該範例會利用 mprotect 將欲修改的代 8 | 碼段設為可寫,再用 memcopy 將新的代碼覆寫掉舊的代碼。 9 | 10 | 對於 QEMU 這樣的 dynamic binary translator 而言,它必須要有能力偵測出自修改代碼。 11 | 首先在前面的介紹中,我們知道 QEMU 會將 guest binary 翻譯成 host binary 並將其存放 12 | 在 code cache 中。第二,我們也知道為了儘量在 code cache 中執行 host binary,QEMU 13 | 會做 block chaining。假設 QEMU 無法偵測出自修改代碼正在修改其代碼,那 QEMU 執行 14 | 的將會是舊的 guest binary (相對應的 host binary),這樣一來無法表現出 guest binary 15 | 新的行為。 16 | 17 | QEMU 以底下兩個步驟處理自修改代碼。 18 | 19 | 1. guest program 先透過 mprotect 改變其頁面權限 (如範例所示)。QEMU 處理的路徑會是 20 | 底下這樣。 21 | 22 | do_syscall (linux-user/syscall.c) -> target_mprotect (linux-user/mmap.c) 23 | -> page_set_flags (exec.c) -> tb_invalidate_phys_page (exec.c) 24 | 25 | void page_set_flags(target_ulong start, target_ulong end, int flags) 26 | { 27 | for (addr = start, len = end - start; 28 | len != 0; 29 | len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { 30 | 31 | // 以 guest pc 查詢二級頁表 l1_map 取得 PageDesc。 32 | PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, 1); 33 | 34 | /* If the write protection bit is set, then we invalidate 35 | the code inside. */ 36 | if (!(p->flags & PAGE_WRITE) && 37 | (flags & PAGE_WRITE) && 38 | p->first_tb) { 39 | tb_invalidate_phys_page(addr, 0, NULL); 40 | } 41 | p->flags = flags; 42 | } 43 | } 44 | 45 | 如同 page_set_flags 中註解所說的,如果該客戶機頁面 (guest page) 的 "寫" 權限被設置 46 | 起來,則與該客戶機頁面相對應的 TranslatonBlock 都將被沖掉。你可能需要回顧一下 [5] 複 47 | 習一下 PageDesc 做何用途。;-) 48 | 49 | 2. guest program 做 SMC 的時候觸發 SIGSEGV,這是因為 guest program 沒有先透過 50 | mprotect 設置代碼段的 "寫" 權限。QEMU 會先註冊 host signal handler,這時就會收到 51 | host 傳來的 SIGSEGV。page_unprotect 將該 guest page 相應的 tb 清掉 (step 1),再 52 | 將該內存區段設成可寫 (step 2)。底下是 QEMU 的處理流程。 53 | 54 | host_signal_handler (linux-user/signal.c) -> cpu_x86_signal_handler (user-exec.c) 55 | -> handle_cpu_signal (user-exec.c) -> page_unprotect (exec.c) 56 | -> tb_invalidate_phys_page (exec.c) 57 | 58 | QEMU 0.15 將部份於 process (user) mode 會用到的函式從 cpu-exec.c 移至 user-exec.c。 59 | 60 | int page_unprotect(target_ulong address, unsigned long pc, void *puc) 61 | { 62 | if ((p->flags & PAGE_WRITE_ORG) && !(p->flags & PAGE_WRITE)) { 63 | 64 | for () { 65 | tb_invalidate_phys_page(addr, pc, puc); // step 1 66 | } 67 | 68 | // step 2 69 | mprotect((void *)g2h(host_start), qemu_host_page_size, 70 | prot & PAGE_BITS); 71 | 72 | return 1; 73 | } 74 | } 75 | 76 | QEMU 在這兩個方向上偵測自修改代碼,最終都會執行到底下的流程: 77 | 78 | tb_invalidate_phys_page (exec.c) -> tb_invalidate_phys_page (exec.c) 79 | 80 | 我們先來看 tb_invalidate_phys_page。基本上 QEMU 是以一個 guest page 為 81 | 單位清掉與之相關聯的 tb。我建議你複習一下 [5] 底下對 QEMU 一些資料結構的 82 | 解釋,這樣你會更清楚。 83 | 84 | static void tb_invalidate_phys_page(tb_page_addr_t addr, 85 | unsigned long pc, void *puc) 86 | { 87 | addr &= TARGET_PAGE_MASK; 88 | p = page_find(addr >> TARGET_PAGE_BITS); 89 | // 取得該 guest page 的第一個 tb。 90 | // tb 末兩位如果是 01 (1),代表 tb 對應的 guest bianry 跨 page。 91 | tb = p->first_tb; 92 | 93 | while (tb != NULL) { 94 | n = (long)tb & 3; // 取得 block chaing 的方向 95 | tb = (TranslationBlock *)((long)tb & ~3); // 去掉末兩位的編碼,還原回真正的 tb 96 | tb_phys_invalidate(tb, addr); // tb_phys_invalidate 做真正的清除工作 97 | tb = tb->page_next[n]; // 取得 tb 所屬 page (或下一個 page) 的下一個 tb 98 | } 99 | p->first_tb = NULL; 100 | 101 | } 102 | 103 | tb_invalidate_phys_page 會呼叫 tb_phys_invalidate 做真正的清除工作。下一篇 104 | 再作介紹。 105 | 106 | * 於 GDB 運行 QEMU 時,會遇到 SIGSEGV。請下 c 忽略它,讓 QEMU 繼續後續的流程。 107 | (gdb) r smc 108 | Starting program: qemu-i386 smc 109 | [Thread debugging using libthread_db enabled] 110 | PAGE(0x8048000): cp <0x804849a>[0..57] <0x80484f3> 111 | 112 | Program received signal SIGSEGV, Segmentation fault. 113 | 0x0000000060239e8c in static_code_gen_buffer () 114 | (gdb) 115 | 116 | 117 | [1] http://en.wikipedia.org/wiki/Self-modifying%20code 118 | [2] http://c2.com/cgi/wiki?SelfModifyingCode 119 | [3] http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=44953 120 | [4] http://web.archive.org/web/20080618094733/http://public.carnet.hr/~jbrecak/sm.html 121 | [5] http://www.hellogcc.org/archives/248 122 | -------------------------------------------------------------------------------- /QEMU/QEMU-handle-self-modifying-code-02.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | 3 | tb_invalidate_phys_page 會呼叫 tb_phys_invalidate 做真正的清除工作。 4 | 這裡複習一下和 tb 相關聯的一些資料結構 (詳細請見 [1]) : 5 | 6 | 1. QEMU 替每個 virtual CPU (env) 維護一個以 guest virtual pc 為索引的快 7 | 取 tb_jmp_cache。tb_find_fast 會查找 tb_jmp_cache 看是否已有翻譯過的 8 | host binary 存放在 code cache。 9 | 10 | 2. 一但 tb_find_fast 查找失敗,tb_find_slow 改以 guest virtual pc 對映 11 | 的 guest physical address 查找 tb_phys_hash。 12 | 13 | 3. QEMU 維護一個二級頁表 l1_map,其中存放 PageDesc。PageDesc 保存 guest 14 | page 中 tb 的資訊。 15 | 16 | 我們先來看 tb_phys_invalidate 的前半部。 17 | 18 | void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) 19 | { 20 | // 將該 tb 從 tb_phys_hash 中移除 21 | phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); // virtual addr 中 page offset 的部分和 physical addr 一樣 22 | h = tb_phys_hash_func(phys_pc); 23 | tb_remove(&tb_phys_hash[h], tb, 24 | offsetof(TranslationBlock, phys_hash_next)); 25 | 26 | // 將 tb 從相應的 PageDesc 中移除 27 | if (tb->page_addr[0] != page_addr) { 28 | p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); 29 | tb_page_remove(&p->first_tb, tb); 30 | invalidate_page_bitmap(p); 31 | } 32 | 33 | // 將 tb 從 tb_jmp_cache 移除 34 | h = tb_jmp_cache_hash_func(tb->pc); 35 | // 因為每一個 env 都有一份自己的 tb_jmp_cache,全部清除。 36 | for(env = first_cpu; env != NULL; env = env->next_cpu) { 37 | if (env->tb_jmp_cache[h] == tb) 38 | env->tb_jmp_cache[h] = NULL; 39 | } 40 | 41 | ... 42 | } 43 | 44 | 假設某個 tb 相對應的 guest binary 因為 SMC 而被改寫,則該 tb 除了 45 | 必須從 tb_phys_hash、tb_jmp_cache 和 PageDesc 被移除之外,所有因 46 | block chaining 而與這個 tb 相連的其它 tb 的連結都需要被打斷。請複習 47 | [2][3]。我這裡簡單描述一下 block chaining。QEMU 除了 patch tb 在 code 48 | cache 中的跳躍位址使其跳至同樣位於 code cache 中的其它 tb,同時也用 49 | TranslationBlock 中的 jmp_next 和 jmp_first 兩個欄位追蹤 block chaining 50 | 的情況。我們繼續看 tb_phys_invalidate 的後半段。:-) 51 | 52 | void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) 53 | { 54 | // 打斷 block chaining 55 | 56 | // 處理 tb1 (tb -> tb1) 57 | tb_jmp_remove(tb, 0); 58 | tb_jmp_remove(tb, 1); 59 | 60 | // 處理 tb1 (tb1 -> tb) 61 | tb1 = tb->jmp_first; 62 | for(;;) { 63 | n1 = (long)tb1 & 3; 64 | if (n1 == 2) // tb1 末兩位如果為 10 (2),代表 tb1 沒有跳至其它 tb 65 | break; 66 | tb1 = (TranslationBlock *)((long)tb1 & ~3); // 還原回原本的 tb1 67 | tb2 = tb1->jmp_next[n1]; // 處理 tb2 (tb1 -> tb2) 68 | tb_reset_jump(tb1, n1); // 將 tb1 至其它的 tb 的 block chaining 打斷 (code cache) 69 | tb1->jmp_next[n1] = NULL; 70 | tb1 = tb2; 71 | } 72 | tb->jmp_first = (TranslationBlock *)((long)tb | 2); // 將 jmp_first 再次指向自己 73 | } 74 | 75 | o tb_jmp_remove 將該 tb 移出 circular list。 76 | 77 | static inline void tb_jmp_remove(TranslationBlock *tb, int n) 78 | { 79 | ptb = &tb->jmp_next[n]; // n (0 或 1) 指示 tb 下一個 block chaining 的方向 80 | tb1 = *ptb; // 處理 tb1 (tb -> tb1) 81 | if (tb1) { 82 | /* find tb(n) in circular list */ 83 | for(;;) { 84 | tb1 = *ptb; 85 | n1 = (long)tb1 & 3; // 取出 tb1 末兩位 86 | tb1 = (TranslationBlock *)((long)tb1 & ~3); 還原回原本的 tb1 87 | if (n1 == n && tb1 == tb) // 代表 tb 沒有跳至其它 tb 88 | break; 89 | if (n1 == 2) { 90 | ptb = &tb1->jmp_first; // 代表沒有其它 tb 跳至 tb1 91 | } else { 92 | ptb = &tb1->jmp_next[n1]; // 處理 tb2 (tb1 -> tb2) 93 | } 94 | } 95 | /* now we can suppress tb(n) from the list */ 96 | *ptb = tb->jmp_next[n]; 97 | 98 | tb->jmp_next[n] = NULL; 99 | } 100 | } 101 | 102 | SMC 是 QEMU process mode 中比較 ugly 的部分。QEMU system mode 同樣也需要 103 | 做 block unchaining 或是 invalidate tb,只是 system mode 的情況要複雜得多了 104 | 。這部份以後有機會再談。:-) 105 | 106 | [1] http://www.hellogcc.org/archives/326 107 | [2] http://www.hellogcc.org/archives/344 108 | [3] http://www.hellogcc.org/archives/357 109 | -------------------------------------------------------------------------------- /QEMU/QEMU-io-emulation.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | 自 QEMU 1.0 版之後,主執行緒 main (vl.c) 即為 IO thread,不再同時負責運行客戶 5 | 機代碼和模擬 IO。底下是概略的圖示: 6 | 7 | 8 | 1. aio_thread (posix-aio-compat.c) 9 | ----------------------------------------> 10 | | (worker thread) 11 | | 12 | | main (vl.c) 13 | ------------------------------------------------------------------------> 14 | | | (io thread) 15 | | | 16 | | | 3. vnc_worker_thread (ui/vnc-jobs.c) 17 | | --------------------------> 18 | | (worker thread) 19 | | 20 | | 21 | | 2. vcpu_thread (qemu_tcg_cpu_thread_fn, cpus.c) 22 | --------------------------> 23 | 24 | 當執行以下指令,登入之後,會有三個執行緒在運行。 25 | 26 | $ qemu-system-i386 linux-0.2.img -vnc 0.0.0.0:1 27 | 28 | 分別是: main、vnc_worker_thread 和 vcpu_thread。 29 | 30 | 1. main -> bdrv_init_with_whitelist -> bdrv_init -> module_call_init 31 | 32 | -> bdrv_file_init -> cdrom_open -> raw_open_common -> paio_init -> do_spawn_thread -> aio_thread 33 | 34 | 35 | 1. main -> qemu_opts_foreach( , drive_init_func, , ) -> drive_init_func -> drive_init 36 | 37 | -> bdrv_open (block.c) -> find_image_format -> bdrv_pread -> bdrv_read -> bdrv_rw_co (corutine) 38 | 39 | -> qemu_aio_wait (aio.c) -> qemu_bh_poll (async.c) -> spawn_thread_bh_fn (posix-aio-compat.c) 40 | ^^^^^^^^^^^^^ 41 | 42 | -> do_spawn_thread -> aio_thread (*) -> do_spawn_thread (?) -> handle_aiocb_rw (posix-aio-compat.c) 43 | 44 | -> posix_aio_notify_event (notify the io is completed?) 45 | 46 | 47 | 2. main -> pc_init_pci -> pc_init1 -> pc_cpus_init -> pc_new_cpu -> cpu_x86_init 48 | 49 | -> x86_cpu_realize -> qemu_init_vcpu -> qemu_tcg_init_vcpu -> qemu_tcg_cpu_thread_fn 50 | 51 | 52 | 3. main -> vnc_display_init -> vnc_start_worker_thread 53 | 54 | 55 | 為避免 IO thread 因執行 IO 而被阻塞 (同步 IO,synchronous I/O,blocking IO)。 56 | QEMU 提供兩種方案: 57 | 58 | 1. 非同步 IO (asynchronous I/O, non-blocking IO)。透過 qemu_set_fd_handler 註冊 59 | fd 的回調函式。 60 | 61 | - qemu-aio.h aio.c 62 | - linux-aio.c (需安裝 libaio [1]) 63 | 64 | 2. 將 IO 交給所謂的 worker thread 執行。QEMU 透過 qemu_paio_submit (posix-aio-compat.c) 65 | 把 IO 請求寫入佇列,worker thread 再從佇列讀取 IO 請求並執行。採用 worker 66 | thread 模式的有底下兩個範例。 67 | 68 | - posix-aio-compat.c 69 | - ui/vnc-jobs.c 70 | 71 | 72 | 73 | [1] http://lse.sourceforge.net/io/aio.html 74 | [] http://blog.vmsplice.net/2011/03/qemu-internals-big-picture-overview.html 75 | [] http://blog.vmsplice.net/2011/03/qemu-internals-overall-architecture-and.html 76 | -------------------------------------------------------------------------------- /QEMU/QEMU-memory.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | // memory.h 6 | struct AddressSpace { 7 | MemoryRegion *root; 8 | FlatView current_map; 9 | int ioeventfd_nb; 10 | MemoryRegionIoeventfd *ioeventfds; 11 | }; 12 | 13 | // memory.c 14 | static AddressSpace address_space_memory; 15 | static AddressSpace address_space_io; 16 | 17 | MemoryRegion 18 | -------------------------------------------------------------------------------- /QEMU/QEMU-precise-exception-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 陳韋任 (Chen Wei-Ren) 2 | 3 | 一般計算機架構會定義當例外/中斷發生時,當下的 CPU 狀態應當為何,這個稱之為 4 | precise exception。假設當下流水線是底下這樣: 5 | 6 | A. addl %eax,(%esp) 7 | B. addl %ebx,(%esp) 8 | C. movl %esi,(%ebp) 9 | D. subl %ecx,5 10 | 11 | 當執行到指令 (C),欲從 %ebp 所指的內存位址讀取資料至 %esi 時,發生頁缺失例外。 12 | 以 x86 對 precise exception 的定義,在指令 (C) 之前的指令,即 (A) 和 (B) 其 13 | 結果必須完成。也就是說,暫存器/內存的內容應該更新; 在指令 (C) 之後的指令, 14 | 即 (D) 的結果必須捨棄。此範例取自 [1]。 15 | 16 | Precise exception 在 binary translation 中佔有重要地位。在此以 QEMU 為例,說 17 | 明 QEMU 如何確保 pecise exception。由以上對 pecise exception 的說明,我們可以 18 | 知道 precise exception 必須考量暫存器和內存。在 binary translation 中,我們 19 | 關注的是客戶 (guest) 的 precise exception。因此,我們必須確保當 guest 代碼發生 20 | 例外時,guest 的暫存器和內存其內容必須滿足 precise exception 的要求。這樣 guest 21 | 的 exception handler 才能正確處理該例外。 22 | 23 | 就 guest 暫存器而言,在 QEMU 中需要考量的是 CPUState。QEMU 在每一個可能會發生 24 | 例外的指令或是 helper function 之前,會將 CPUState 中的大部分內容更新,少數 25 | 未更新的內容會在 guest 真正發生例外時重新再計算。以 x86 為例,pc 和 condition 26 | code 屬於後者。在 binary translation 中,出於效能上的考量,通常會在一個 basic 27 | block 的結尾才更新 guest pc,而非每翻譯一個 guest 指令就更新 guest pc。 28 | 29 | 就 guest 內存而言,我們將重點放在 guest memory store operation 上,因為只有 30 | memory store operation 會修改 guest 內存的內容。第一,QEMU 會依照 guest 原本 31 | memory store operation 的順序進行翻譯。第二,針對所有潛在會發生例外的 guest 32 | 指令,QEMU 保留其相對於 guest memory store operation 的順序。簡單來說,QEMU 33 | 不會 reorder guest 指令順序。這簡化了 QEMU 維護 precise exception 的複雜度, 34 | 但同時也喪失了一些潛在可能的優化。[2][3] 35 | 36 | 接下來以 guest 頁缺失例外為例,讓我們來觀察當 guest 發生頁缺失時,QEMU 是如何 37 | 維護正確的 guest 暫存器和內存。 38 | 39 | [1] The Technology Behind Crusoe™ Processor 40 | [2] http://lists.gnu.org/archive/html/qemu-devel/2012-02/msg04138.html 41 | [3] http://people.cs.nctu.edu.tw/~chenwj/log/QEMU/agraf-2012-03-02.txt 42 | -------------------------------------------------------------------------------- /QEMU/QEMU-precise-exception-02.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 陳韋任 (Chen Wei-Ren) 2 | 3 | 這裡以 linux-0.11 當範例,請至 [1][2] 下載代碼和硬盤映像編譯。我們觀察以 guest 4 | pc 0xe4c0 為開頭的 basic block。使用 QEMU 0.13 [3] 運行 linux-0.11。 5 | 6 | ---------------- 7 | $ mkdir build; cd build 8 | $ ../qemu-0.13.0/configure --prefix=$INSTALL --target-list=i386-softmmu \ 9 | --enable-debug --extra-cflags="--save-temps" 10 | $ make install 11 | $ gdb qemu 12 | (gdb) r -boot a -fda Image -hda hdc-0.11-new.img -vnc 0.0.0.0:1 -d in_asm,op,out_asm 13 | ---------------- 14 | 15 | 登入後,下 `ls`。觀察 qemu.log 並定位至 0xe4c0。首先,我們可以看到如下內容: 16 | 17 | ---------------- 18 | IN: 19 | 0x0000e4c0: sub $0x4,%esp 20 | 0x0000e4c3: mov 0x8(%esp),%eax 21 | 0x0000e4c7: mov %al,(%esp) 22 | 0x0000e4ca: movzbl (%esp),%eax 23 | 0x0000e4ce: mov 0xc(%esp),%edx 24 | 0x0000e4d2: mov %al,%fs:(%edx) 25 | 0x0000e4d5: add $0x4,%esp 26 | 0x0000e4d8: ret 27 | 28 | OP: 29 | ---- 0xe4c0 30 | movi_i32 tmp1,$0x4 31 | mov_i32 tmp0,esp 32 | sub_i32 tmp0,tmp0,tmp1 33 | mov_i32 esp,tmp0 34 | mov_i32 cc_src,tmp1 35 | mov_i32 cc_dst,tmp0 36 | 37 | ... 略 ... 38 | 39 | OUT: [size=450] 40 | 0x40bbeff0: mov 0x10(%r14),%ebp 41 | 0x40bbeff4: sub $0x4,%ebp 42 | 43 | ... 略 ... 44 | 45 | 0x4011813b: callq 0x54d38a 46 | 0x40118140: mov 0x10(%r14),%ebp 47 | 48 | ... 略 ... 49 | 50 | ---------------- 51 | 52 | 這是 QEMU 第一次遇到尚未翻譯,以 guest pc 0xe4c0 開頭的 basic block 時所產生 53 | 的輸出,這包括 guest binary (IN: 以下內容)、TCG IR (OP: 以下內容) 和 host 54 | binary (OUT: 以下內容)。 55 | 56 | 再繼續往下搜尋 0xe4c0,會看到以下內容: 57 | 58 | ---------------- 59 | IN: 60 | 0x0000e4c0: sub $0x4,%esp 61 | 0x0000e4c3: mov 0x8(%esp),%eax 62 | 0x0000e4c7: mov %al,(%esp) 63 | 0x0000e4ca: movzbl (%esp),%eax 64 | 0x0000e4ce: mov 0xc(%esp),%edx 65 | 0x0000e4d2: mov %al,%fs:(%edx) 66 | 0x0000e4d5: add $0x4,%esp 67 | 0x0000e4d8: ret 68 | 69 | OP: 70 | ---- 0xe4c0 71 | movi_i32 tmp1,$0x4 72 | mov_i32 tmp0,esp 73 | sub_i32 tmp0,tmp0,tmp1 74 | mov_i32 esp,tmp0 75 | mov_i32 cc_src,tmp1 76 | mov_i32 cc_dst,tmp0 77 | 78 | ... 略 ... 79 | 80 | RESTORE: 81 | 0x0000: 0000e4c0 82 | 0x0007: 0000e4c3 83 | 0x000d: 0000e4c7 84 | 0x0011: 0000e4ca 85 | 0x0015: 0000e4ce 86 | 0x001b: 0000e4d2 87 | spc=0x4011813f pc_pos=0x1b eip=0000e4d2 cs_base=0 88 | ---------------- 89 | 90 | 這裡就是重點了。spc 指的是發生例外的 host pc,eip 指的是與其相對映發生例外的 91 | guest pc。這邊請注意,由於我們將 guest binary 翻譯成 host binary 並執行,真正 92 | 發生例外的是 host binary (位於 code cache),但是我們必須將它映射回 guest pc, 93 | 查出哪一條 guest 指令發生例外,並做後續處理。我們看一下第一次翻譯 0xe4d2 所 94 | 得的 host binary。 95 | 96 | ---------------- 97 | 0x4011813b: callq 0x54d38a 98 | 0x40118140: mov 0x10(%r14),%ebp 99 | ---------------- 100 | 101 | 我們可以看到 spc 0x4011813f == 0x40118140 - 1,也就是 callq 0x54d38a 下一條指令 102 | 所在位址減去 1。這裡做點弊,我們在 gdb 下 print __stb_mmu。 103 | 104 | --------------- 105 | (gdb) print __stb_mmu 106 | $1 = {void (target_ulong, uint8_t, int)} 0x54d38a <__stb_mmu> 107 | --------------- 108 | 109 | 可以得知,我們在呼叫 __stb_mmu 的時候發生例外。__{ld,st}{b,w,l,q}_{cmmu,mmu} 是用來存取 110 | guest 內存的 helper function。它們首先會查找 TLB (env1->tlb_table) 試圖取得 111 | guest virtual address 相對映的 host virtual address。如果 TLB 命中,可直接利用 112 | 該 host virtual address 存取 guest 內存內容。如果 TLB 不命中,則會呼叫 tlb_fill 113 | (target-i386/op_helper.c)。tlb_fill 會呼叫 cpu_x86_handle_mmu_fault 查找客戶頁表。 114 | 如果命中,代表該 guest virtual address 所在的頁已存在,tlb_fill 會將該頁項目 115 | 填入 TLB 以便後續查找。如果不命中,代表發生頁缺失,QEMU 會回復 guest CPUState, 116 | 並拉起 guest exception index (env->exception_index) 通知 guest 頁缺失發生。最後 117 | 交由 guest 頁缺失 handler 將該頁載入。 118 | 119 | 接下來,我們來看代碼。;) 120 | 121 | 122 | [1] http://www.oldlinux.org/oldlinux/viewthread.php?tid=13681&extra=page%3D1 123 | [2] http://oldlinux.org/Linux.old/bochs/linux-0.11-devel-060625.zip 124 | [3] http://wiki.qemu.org/download/qemu-0.13.0.tar.gz 125 | -------------------------------------------------------------------------------- /QEMU/QEMU-precise-exception-03.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 陳韋任 (Chen Wei-Ren) 2 | 3 | 首先我們來看 __stb_mmu。請看 ${SRC}/softmmu_template.h 和 4 | ${BUILD}/i386-softmmu/op_helper.i。 5 | 6 | SOFTMMU 相關的 helper function 是透過 softmmu_* 檔案內的巨集加以 7 | 合成。這裡只挑部分加以描述。 8 | 9 | SUFFIX 可以是 b (byte, 8)、w (word, 16)、l (long word, 32) 和 q 10 | (quad word, 64),代表資料大小。MMUSUFFIX 可以是 cmmu 或是 mmu, 11 | 分別代表欲讀取的是 code 或是 data。mmu_idx 代表索引的是內核態 12 | 亦或是用戶態的 TLB。addr 代表 guest virtual address。 13 | 14 | ---------------- ${SRC}/softmmu_template.h ---------------- 15 | void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, 16 | DATA_TYPE val, 17 | int mmu_idx) 18 | { ... } 19 | ----------------------------------------------------------- 20 | 21 | 接著看展開巨集後的函式體。 22 | 23 | ---------------- ${BUILD}/i386-softmmu/op_helper.i -------- 24 | void __stb_mmu(target_ulong addr, uint8_t val, int mmu_idx) 25 | { 26 | redo: 27 | // 查找 TLB 28 | tlb_addr = env->tlb_table[mmu_idx][index].addr_write; 29 | if (...) { 30 | 31 | // TLB 命中 32 | 33 | } else { 34 | 35 | // TLB 不命中 36 | 37 | /* the page is not in the TLB : fill it */ 38 | // retaddr = GETPC(); 39 | retaddr = ((void *)((unsigned long)__builtin_return_address(0) - 1)); 40 | 41 | // 試圖填入 TLB entry。 42 | tlb_fill(addr, 1, mmu_idx, retaddr); 43 | goto redo; 44 | } 45 | } 46 | ---------------- 47 | 48 | 這裡 QEMU 利用 GCC 的 __builtin_return_address 擴展 [1] 來判定 tlb_fill 是從一般 49 | C 函式或是 code cache 中被呼叫。retaddr 若為 0,表前者,retaddr 若不為 0,表後者。 50 | 之後,我們會透過 GDB 更加清楚前面所述所代表的意思。我們關注 retaddr 不為 0,也就是 51 | 從 code cache 中呼叫 tlb_fill 的情況。 52 | 53 | 在看 tlb_fill 之前,我們先偷看 cpu_x86_handle_mmu_fault (target-i386/helper.c) 54 | 的註解。我們關注返回值為 1,也就是頁缺失的情況。 55 | 56 | ---------------- 57 | /* return value: 58 | -1 = cannot handle fault 59 | 0 = nothing more to do 60 | 1 = generate PF fault 61 | */ 62 | int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, ...) 63 | { ... } 64 | ---------------- 65 | 66 | 來看 tlb_fill。 67 | 68 | ---------------- 69 | void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) 70 | { 71 | ret = cpu_x86_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); 72 | if (ret) { 73 | if (retaddr) { 74 | 75 | // 當客戶發生頁缺失 (ret == 1) 且 tlb_fill 是從 code cache 中被 76 | // 呼叫 (retaddr != 0),我們會在這裡。 77 | 78 | /* now we have a real cpu fault */ 79 | pc = (unsigned long)retaddr; 80 | tb = tb_find_pc(pc); 81 | if (tb) { 82 | /* the PC is inside the translated code. It means that we have 83 | a virtual CPU fault */ 84 | cpu_restore_state(tb, env, pc, NULL); 85 | } 86 | } 87 | raise_exception_err(env->exception_index, env->error_code); 88 | } 89 | env = saved_env; 90 | } 91 | ----------------- 92 | 93 | 請注意! 如果 retaddr != 0,其值代表的 (幾乎) 是發生例外的 host binary 所在位址。 94 | QEMU 利用它來查找是哪一個 TranslationBlock 中的 host binary 發生例外。 95 | tb_find_pc (exec.c) 利用該 host binary pc 進行查找,取得 tb。 96 | 97 | ----------------- 98 | TranslationBlock *tb_find_pc(unsigned long tc_ptr) 99 | { 100 | // tbs 是 TranslationBlock * 數組。每一個在 code cache 中 (已翻譯好的) 101 | // basic block 都有相對應的 TranslationBlock 存放其相關資訊。 102 | 103 | /* binary search (cf Knuth) */ 104 | m_min = 0; 105 | m_max = nb_tbs - 1; 106 | while (m_min <= m_max) { 107 | m = (m_min + m_max) >> 1; 108 | tb = &tbs[m]; 109 | // tc_ptr 代表 host binary 在 code cache 的起始位址。 110 | v = (unsigned long)tb->tc_ptr; 111 | if (v == tc_ptr) 112 | return tb; 113 | else if (tc_ptr < v) { 114 | m_max = m - 1; 115 | } else { 116 | m_min = m + 1; 117 | } 118 | } 119 | return &tbs[m_max]; 120 | } 121 | ----------------- 122 | 123 | 一但找到該負責的 tb,QEMU 就會回復 guest CPUState 以便 guest exception handler 124 | 處理 guest 的頁缺失例外。 125 | 126 | ----------------- 127 | if (tb) { 128 | /* the PC is inside the translated code. It means that we have 129 | a virtual CPU fault */ 130 | cpu_restore_state(tb, env, pc, NULL); 131 | } 132 | ----------------- 133 | 134 | 接著我們看 cpu_restore_state (translate-all.c)。 135 | 136 | 137 | [1] http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html 138 | -------------------------------------------------------------------------------- /QEMU/QEMU-precise-exception-05.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 陳韋任 (Chen Wei-Ren) 2 | 3 | 最後,如同我之前所承諾的。我們來看 tlb_fill 從一般 C 函式和 code cache 被呼叫是 4 | 什麼意思。 5 | 6 | 我們可以看到 retaddr == 0 時,tlb_fill 是從一般 C 函式被呼叫。 7 | 8 | ---------------- 9 | (gdb) b tlb_fill 10 | (gdb) r -boot a -fda Image -hda hdc-0.11-new.img -vnc 0.0.0.0:1 -d in_asm,op,out_asm 11 | Breakpoint 1, tlb_fill (addr=4294967280, is_write=2, mmu_idx=0, retaddr=0x0) at 12 | /tmp/chenwj/qemu-0.13.0/target-i386/op_helper.c:4816 13 | 4816 { 14 | (gdb) bt 15 | #0 tlb_fill (addr=4294967280, is_write=2, mmu_idx=0, retaddr=0x0) at /tmp/chenwj/qemu-0.13.0/target-i386/op_helper.c:4816 16 | #1 0x000000000050ee86 in __ldb_cmmu (addr=4294967280, mmu_idx=0) at /tmp/chenwj/qemu-0.13.0/softmmu_template.h:134 17 | #2 0x000000000051045e in ldub_code (ptr=4294967280) at /tmp/chenwj/qemu-0.13.0/softmmu_header.h:87 18 | #3 0x000000000051054b in get_page_addr_code (env1=0x110e390, addr=4294967280) at /tmp/chenwj/qemu-0.13.0/exec-all.h:325 19 | #4 0x0000000000510986 in tb_find_slow (pc=4294967280, cs_base=4294901760, flags=68) at /tmp/chenwj/qemu-0.13.0/cpu-exec.c:139 20 | #5 0x0000000000510b9d in tb_find_fast () at /tmp/chenwj/qemu-0.13.0/cpu-exec.c:188 21 | #6 0x00000000005112db in cpu_x86_exec (env1=0x110e390) at /tmp/chenwj/qemu-0.13.0/cpu-exec.c:575 22 | #7 0x000000000040aabd in qemu_cpu_exec (env=0x110e390) at /tmp/chenwj/qemu-0.13.0/cpus.c:767 23 | #8 0x000000000040abc4 in cpu_exec_all () at /tmp/chenwj/qemu-0.13.0/cpus.c:795 24 | #9 0x000000000056e417 in main_loop () at /tmp/chenwj/qemu-0.13.0/vl.c:1329 25 | #10 0x00000000005721cc in main (argc=11, argv=0x7fffffffe1a8, envp=0x7fffffffe208) at /tmp/chenwj/qemu-0.13.0/vl.c:2992 26 | ---------------- 27 | 28 | 我們可以看到 retaddr != 0 時,tlb_fill 是從 code cache 中被呼叫。 29 | 30 | ---------------- 31 | Breakpoint 1, tlb_fill (addr=28668, is_write=1, mmu_idx=0, retaddr=0x4000020c) 32 | at /tmp/chenwj/qemu-0.13.0/target-i386/op_helper.c:4816 33 | 4816 { 34 | (gdb) bt 35 | #0 tlb_fill (addr=28668, is_write=1, mmu_idx=0, retaddr=0x4000020c) at /tmp/chenwj/qemu-0.13.0/target-i386/op_helper.c:4816 36 | #1 0x000000000054e511 in __stl_mmu (addr=28668, val=982583, mmu_idx=0) at /tmp/chenwj/qemu-0.13.0/softmmu_template.h:272 37 | #2 0x000000004000020d in ?? () <--- 我們在 code cache 裡! 38 | ---------------- 39 | 40 | 我們來看一下 qemu.log 驗證一下我們對 QEMU 的了解。;) 既然 retaddr = 發生例外的 41 | host binary 下一條指令位址減去 1,我們定位到 0x4000020d。 42 | 43 | ---------------- 44 | 0x40000208: callq 0x54e3a0 45 | 0x4000020d: movzwl %bx,%ebp 46 | ---------------- 47 | 48 | 瞧瞧 __stl_mmu 的位址,果然是 0x54e3a0。這代表我們在 code cache 呼叫 __stl_mmu。 49 | __stl_mmu 再去呼叫 tlb_fill 的時候發生例外。 50 | 51 | ---------------- 52 | (gdb) p __stl_mmu 53 | $1 = {void (target_ulong, uint32_t, int)} 0x54e3a0 <__stl_mmu> 54 | ---------------- 55 | 56 | 這裡我們可以看到 SOFTMMU 相關的 helper function 在各個地方都會被用到,不論是 57 | QEMU 本身的函式 (一般 C 函式) 或是 code cache 都會調用 __{ld,st}{b,w,l,q}_{cmmu,mmu}。 58 | 這些 helper function 又會調用 tlb_fill。tlb_fill 就是透過 retaddr 來判定是 59 | 否需要回復 guest CPUState。 60 | -------------------------------------------------------------------------------- /QEMU/QEMU-softmmu-01.txt: -------------------------------------------------------------------------------- 1 | 3. 2 | 3 | // tcg/i386/tcg-target.c 4 | 5 | #include "../../softmmu_defs.h" 6 | 7 | // target-i386/op_helper.c defines __ld*_mmu body 8 | // #define MMUSUFFIX _mmu 9 | // #define SHIFT 0 10 | // #include "softmmu_template.h" 11 | static void *qemu_ld_helpers[4] = { 12 | __ldb_mmu, 13 | __ldw_mmu, 14 | __ldl_mmu, 15 | __ldq_mmu, 16 | }; 17 | 18 | static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, 19 | int opc) 20 | { 21 | 22 | } 23 | 24 | 25 | // TCG IR -> host binary 26 | static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, 27 | const TCGArg *args, const int *const_args) 28 | { 29 | case INDEX_op_qemu_ld8u: 30 | tcg_out_qemu_ld(s, args, 0); 31 | break; 32 | } 33 | 34 | // guest binary -> TCG IR (qemu_ld/qemu_st) 35 | // tcg/tcg-op.h 36 | static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index) 37 | { 38 | tcg_gen_op3i_i32(INDEX_op_qemu_ld8u, ret, addr, mem_index); 39 | } 40 | -------------------------------------------------------------------------------- /QEMU/QEMU-softmmu-02.txt: -------------------------------------------------------------------------------- 1 | 3. Software MMU 2 | 3 | - softmmu_defs.h 4 | - softmmu_template.h 5 | 6 | // tcg/i386/tcg-target.c 7 | 8 | static void *qemu_ld_helpers[4] = { 9 | __ldb_mmu, // load byte 10 | __ldw_mmu, // load word 11 | __ldl_mmu, // load long 12 | __ldq_mmu, // load quad word 13 | }; 14 | 15 | // target-i386/op_helper.c 16 | 17 | #define MMUSUFFIX _mmu 18 | 19 | #define SHIFT 0 20 | #include "softmmu_template.h" 21 | 22 | // softmmu_template.h 23 | DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, 24 | int mmu_idx) 25 | { 26 | } 27 | 28 | - softmmu_header.h 29 | - softmmu_exec.h 30 | 31 | // target-xxx/cpu.h 自行定義 MMU_MODE?_SUFFIX。 32 | // 以 i386 為例: _kernel,_user。 33 | #define MEMSUFFIX MMU_MODE1_SUFFIX 34 | 35 | // target-i386/op_helper.c 36 | 37 | #include "cpu.h" 38 | #include "softmmu_exec.h" 39 | 40 | // softmmu_exec.h 41 | #include "softmmu_defs.h" 42 | 43 | #define ACCESS_TYPE 0 44 | #define MEMSUFFIX MMU_MODE0_SUFFIX 45 | #define DATA_SIZE 1 46 | #include "softmmu_header.h" 47 | 48 | // softmmu_header.h 49 | // op_helper.i -> ldub_kernel 50 | // ld/st 最後會呼叫到 __ld/__st 51 | // kernel mode: env->tlb_table[0] 52 | // user mode: env->tlb_table[1] 53 | // data: env->tlb_table[(cpu_mmu_index(env))] 54 | static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr) 55 | { 56 | } 57 | -------------------------------------------------------------------------------- /QEMU/QEMU-system-mode-01.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | o cpu_exec_init_all (exec.c) 22 | 23 | void cpu_exec_init_all(void) 24 | { 25 | // 初始化內存。 26 | memory_map_init(); 27 | // 初始化 IO 28 | io_mem_init(); 29 | } 30 | 31 | o memory_map_init (exec.c) 32 | 33 | static MemoryRegion *system_memory; 34 | static MemoryRegion *system_io; 35 | 36 | static void memory_map_init(void) 37 | { 38 | system_memory = g_malloc(sizeof(*system_memory)); 39 | memory_region_init(system_memory, "system", INT64_MAX); 40 | // 將 address_space_memory (memory.c) 的根設為 system_memory。 41 | set_system_memory_map(system_memory); 42 | 43 | system_io = g_malloc(sizeof(*system_io)); 44 | memory_region_init(system_io, "io", 65536); 45 | // 將 address_space_io (memory.c) 的根設為 system_io。 46 | set_system_io_map(system_io); 47 | 48 | memory_listener_register(&core_memory_listener, system_memory); 49 | memory_listener_register(&io_memory_listener, system_io); 50 | } 51 | 52 | o 53 | 54 | MemoryRegion io_mem_ram, io_mem_rom, io_mem_unassigned, io_mem_notdirty; 55 | 56 | static void io_mem_init(void) 57 | { 58 | memory_region_init_io(&io_mem_ram, &error_mem_ops, NULL, "ram", UINT64_MAX); 59 | memory_region_init_io(&io_mem_rom, &rom_mem_ops, NULL, "rom", UINT64_MAX); 60 | memory_region_init_io(&io_mem_unassigned, &unassigned_mem_ops, NULL, 61 | "unassigned", UINT64_MAX); 62 | memory_region_init_io(&io_mem_notdirty, ¬dirty_mem_ops, NULL, 63 | "notdirty", UINT64_MAX); 64 | memory_region_init_io(&io_mem_subpage_ram, &subpage_ram_ops, NULL, 65 | "subpage-ram", UINT64_MAX); 66 | memory_region_init_io(&io_mem_watch, &watch_mem_ops, NULL, 67 | "watch", UINT64_MAX); 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /QEMU/QEMU-tcg-01.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | 前言 5 | 6 | 因為工作上的關係,必須接觸 QEMU。雖然網路上有不少文件,但總覺得講得不夠深入。 7 | QEMU 是一個仿真器 (emulator),可以 process mode 或是 system mode 運行。process 8 | mode 可以運行不同 ISA 同一 OS 的 binary; system mode 可以在當前作業系統上運行 9 | 另外一個 OS。我在收集各方資料,閱讀代碼和在郵件列表上發問之後,覺得略有心得。 10 | 在此對 QEMU internal 作一個較為深入的介紹。憑我個人之力,難免有疏漏或是錯誤。 11 | 權且當作拋磚引玉吧。希望各位不吝指教。 12 | 13 | 0. 術語、線上資源和技巧 14 | 15 | 對 QEMU 而言,被仿真的平台被稱為 guest,又稱 target; 運行 QEMU 的平台稱為 host 16 | 。QEMU 是利用動態翻譯 (dynamic translation) 的技術將 guest binary 動態翻譯成 host 17 | binary,並交由 host 運行翻譯所得的 host binary。Tiny Code Generator (TCG) 是 QEMU 18 | 中負責動態翻譯的組件。對 TCG 而言,target 有不同的含意,它代表 TCG 是針對哪一個 19 | host 生成 host binary。 20 | 21 | 網路上對 QEMU 有較為完整描述的文件為: 22 | 23 | - QEMU, a Fast and Portable Dynamic Translator 24 | - Porting QEMU to Plan 9: QEMU Internals and Port Strategy 25 | 26 | 然而需要注意的是,上述文件在動態翻譯的部分均是針對 QEMU 0.9 版。QEMU 0.9 版以前是 27 | 使用 dyngen 技術; QEMU 0.10 版以後採用 TCG。雖說如此,但在 QEMU 的其它部分差異不大 28 | ,上述文件仍可供參考。http://qemu.sourcearchive.com/ 收集了自 QEMU 0.6.1 版至今的 29 | 所有 QEMU 源代碼。各位可以邊看文件邊看源代碼。 30 | 31 | QEMU 極為依賴 macro,這使得直接閱讀源代碼通常無法確定其函數呼叫,或是執行流程倒底 32 | 為何。請在編譯 QEMU 的時候加上 "--extra-cflags="-save-temps"",如此可得展開 marco 33 | 的 *.i 檔。 34 | 35 | 其餘部分請見: 36 | 37 | - http://wiki.qemu.org/Documentation/GettingStartedDevelopers 38 | - QEMU 目錄下的 HACKING、CODING_STYLE、tcg/README 和 doc/* 39 | - ISA reference manual。 40 | - http://www.mouseos.com/x64/index.html 41 | 42 | 1. TCG 43 | 44 | TCG 是 QEMU 的核心。其基本流程如下: 45 | 46 | guest binary -> TCG IR -> host binary 47 | 48 | 1.1 TCG IR 49 | 50 | TCG 定義了一組 IR (intermediate representation),熟悉 GCC 的各位對此應該不陌生。 51 | TCG IR 大致分成以下幾類: 52 | 53 | - Move Operation: mov, movi, ... 54 | - Logic Operation: and, or, xor, shl, shr, ... 55 | - Arithmetic Operation: add, sub, mul, div, ... 56 | - Branch Operation: jmp, br, brcond 57 | - Fuction call: call 58 | - Memory Operation: ld, st 59 | - QEMU specific Operation: tb_exit, goto_tb, qemu_ld/qemu_st 60 | 61 | 請見 tcg/*,特別是 tcg.i,可以看到 TCGOpcode。tcg/README 也別忘了。TCG 在翻譯 guest 62 | binary 的時候是以一個 translation block (tb) 為單位,其結尾通常是分支指令。 63 | 64 | target-ARCH/* 定義了如何將 ARCH binary 反匯編成 TCG IR。tcg/ARCH 定義了如何將 65 | TCG IR 翻譯成 ARCH binary。 66 | 67 | 1.2 TCG Flow 68 | 69 | 先介紹一些資料結構: 70 | 71 | - gen_opc_buf 和 gen_opparam_buf (translate-all.c) 分別放置 TCG Opcode 和 Operand。 72 | 73 | - 如果使用靜態配置的緩衝區,static_code_gen_buffer (exec.c) 即為 code cache,放置 74 | host binary。 75 | 76 | - 在跳入/出 code cache 執行之前/後,要執行 prologue/epilogue,請見 code_gen_prologue 77 | (exec.c)。這邊的 prologue/epilogue 就是指 function prologue/epilogue。QEMU 將跳至 78 | code cache (host binary) 執行的過程看成是函式呼叫,故有此 prologue/epilogue。 79 | 80 | 以 qemu-i386 為例,流程大致如下: 81 | 82 | main (linux-user/main.c) -> cpu_exec_init_all (exec.c) 83 | -> cpu_init/cpu_x86_init (target-i386/helper.c) 84 | -> tcg_prologue_init (tcg/tcg.c) -> cpu_loop (linux-user/main.c) 85 | 86 | 函式名之所以會出現 cpu_init/cpu_x86_init,是因為 QEMU 經常使用 #define 替換函式名。 87 | cpu_init 是 main 裡呼叫的函式,經 #define 替換後,實際上是 cpu_x86_init 88 | (target-i386/helper.c)。GDB 下斷點時請注意此種情況。 89 | 90 | 這邊只介紹 tcg_prologue_init (tcg/tcg.c) -> cpu_loop (linux-user/main.c) 這一段,因為 91 | 這一段跟 TCG 較為相關。容我先講 cpu_loop (linux-user/main.c)。 92 | 93 | - cpu_loop (linux-user/main.c) -> cpu_x86_exec/cpu_exec (cpu-exec.c) 94 | cpu_exec 是主要執行迴圈,其結構大致如下: 95 | 96 | /* prepare setjmp context for exception handling */ 97 | for(;;) { 98 | if (setjmp(env->jmp_env) == 0) { // 例外處理。 99 | } 100 | 101 | next_tb = 0; /* force lookup of first TB */ 102 | for(;;) { 103 | // 判斷是否有中斷。若有,跳回例外處理。 104 | 105 | next_tb = tcg_qemu_tb_exec(tc_ptr); // 跳至 code cache 執行。 106 | 107 | } 108 | } 109 | 110 | - tcg_prologue_init (tcg/tcg.c) -> tcg_target_qemu_prologue (tcg/i386/tcg-target.c) 111 | 如前所述,QEMU 將跳至 code cache (host binary) 執行的過程看成是函式呼叫。不同平台 112 | 的 calling convention 各有不同,tcg_prologue_init 將產生 prologue/epilogue 的工作 113 | 轉交 tcg_target_qemu_prologue。 114 | 115 | static void tcg_target_qemu_prologue(TCGContext *s) 116 | { 117 | /* QEMU (cpu_exec) -> 入棧 */ 118 | 119 | // OPC_GRP5 (0xff) 為 call,EXT5_JMPN_Ev 是其 opcode extension。 120 | // tcg_target_call_iarg_regs 是函式呼叫負責傳遞參數的暫存器。 121 | tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[0]); // 跳至 code cache 執行 122 | 123 | // 此時,s->code_ptr 指向 code_gen_prologue 中 prologue 和 jmp to code cache 之後的位址。 124 | // tb_ret_addr 是紀錄 code cache 跳回 code_gen_prologue 的哪個地方。 125 | tb_ret_addr = s->code_ptr; 126 | 127 | /* 出棧 -> 返回 QEMU (cpu_exec),確切的講是返回 tcg_qemu_tb_exec */ 128 | } 129 | 130 | 這邊小結一下 QEMU -> prologue -> code cache -> epilogue -> QEMU。tb_ret_addr 就是用來由 131 | code cache 返回至 code_gen_prologue,執行 epilogue,再返回 QEMU。 132 | 133 | 在介紹 cpu_exec 之前,我先介紹幾個 QEMU 資料結構,請善用 http://qemu.sourcearchive.com/ 。 134 | 我們要知道所謂仿真或是虛擬化一個 CPU (ISA),簡單來說就是用一個資料結構 (struct) 儲存該 135 | CPU 的狀態。執行該虛擬 CPU,就是從內存中讀取該虛擬 CPU 的資料結構,運算後再存回去。 136 | 137 | - CPUX86State: 保存 x86 register,eflags,eip,cs,...。不同 ISA 之間通用的資料結構被 138 | QEMU #define 成 CPU_COMMON。一般稱此資料結構為 CPUState。下文所提 env 即為 CPUState。 139 | QEMU 運行虛擬 CPU 都會利用 env 這個變數。 140 | 141 | - TranslationBlock: 之前說過,QEMU 是以一個 translation block 為單位進行翻譯。其中保存 142 | 此 translation block 對應 guest binary 的 pc, cs_base, eflags。另外,tc_ptr 指向 code 143 | cache (host binary)。其它欄位待以後再談。 144 | 145 | code cache (host binary) 146 | struct TranslationBlock's tb_ptr -> tb 147 | 148 | 149 | 上面是示意圖。注意! 依照上下文的不同,TB (tb) 可能是指 struct TranslationBlock,也有 150 | 可能是指 code cache (host binary) 中 TranslationBlock 所指向的 tb。 151 | 152 | - PageDesc: 主要保存 guest page 中的第一個 tb (TranslationBlock *)。這跟 QEMU 內部運作 153 | 機制有關。某些情況下,guest page (guest binary) 可能被替換或是被寫。這個時候,QEMU 154 | 會以 guest page (guest binary) 為單位,清空與它相關聯的 TB (code cache)。這時再回來講 155 | TranslationBlock。TranslationBlock 有底下兩個欄位: 156 | 157 | - page_addr[2]: 存放 TranslationBlock 對應 guest binary 所在的 guest page。注意! guest 158 | binary 有可能跨 guest page,故這裡有兩個欄位。 159 | 160 | - page_next[2]: 當透過 PageDesc->first_tb 找到該 guest page 的第一個 tb,tb->page_next 161 | 就被用來找尋該 guest page 的下一個 tb。 162 | 163 | 再回來講 PageDesc。QEMU 替 PageDesc 維護了一個二級頁表 l1_map。page_find 這個函式根據 164 | 輸入的 address 搜尋 l1_map,返回 PageDesc。這在以 guest page (guest binary) 為單位, 165 | 清空與它相關聯的 TB (code cache) 的時候會用到。 166 | 167 | 有一個名字很像的資料結構叫 PhysPageDesc,QEMU 也替它維護一個二級頁表 l1_phys_map。這是 168 | 在 system mode 做地址轉換之用,這邊不談。 169 | 170 | - TCGContext: 生成 TCG IR 時會用到。 171 | 172 | - DisasContext: 反匯編 guest binary 時會用到。 173 | -------------------------------------------------------------------------------- /QEMU/QEMU-tcg-02.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 陳韋任 (Chen Wei-Ren) 2 | chenwj at iis.sinica.edu.tw 3 | 4 | 1.2 TCG Flow 5 | 6 | 介紹完一些資料結構之後,我開始介紹 cpu_exec 的流程。底下複習一下 7 | process mode 的流程: 8 | 9 | cpu_loop (linux-user/main.c) -> cpu_x86_exec/cpu_exec (cpu-exec.c) 10 | 11 | cpu_exec 有兩層 for 迴圈。我們先看內層: 12 | 13 | next_tb = 0; /* force lookup of first TB */ 14 | for(;;) { 15 | 16 | tb = tb_find_fast(); 17 | 18 | tc_ptr = tb->tc_ptr; 19 | 20 | next_tb = tcg_qemu_tb_exec(tc_ptr); 21 | } 22 | 23 | tb_find_fast 會先試圖查看目前 pc (guest virtual address) 以否已有翻譯過 24 | 的 host binary 存放在 code cache。 25 | 26 | // pc = eip + cs_base 27 | cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); 28 | 29 | // CPUState 中的 tb_jmp_cache 即是做此用途。 30 | tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; 31 | 32 | // 檢查該 tb 是否合法。這是因為不同的 eip + cs_base 可能會得到相同的 pc。 33 | if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || 34 | tb->flags != flags)) { 35 | tb = tb_find_slow(pc, cs_base, flags); 36 | } 37 | 38 | return tb; // code cache 已有翻譯過的 host binary,返回 TranslationBlock。 39 | 40 | tb_find_slow 以 pc 對映的物理位址 (guest physcal address) 查找 TB。如果成功, 41 | 則將該 TB 寫入 env->tb_jmp_cache; 若否,則進行翻譯。 42 | 43 | phys_pc = get_page_addr_code(env, pc); 44 | phys_page1 = phys_pc & TARGET_PAGE_MASK; 45 | 46 | // 除了 env->tb_jmp_cache 這個以 guest virtual address 為索引的緩存之外, 47 | // QEMU 還維護了一個 tb_phys_hash,這個是以 guest physical address 為索引。 48 | h = tb_phys_hash_func(phys_pc); 49 | ptb1 = &tb_phys_hash[h]; 50 | for (;;) { 51 | 52 | not_found: 53 | 54 | found: 55 | 56 | // TranslationBlock 中的 phys_hash_next 用在這裡。 57 | // 如果 phys_pc 索引到同一個 tb_phys_hash 欄位,用 phys_hash_next 串接起來。 58 | ptb1 = &tb->phys_hash_next; 59 | } 60 | 61 | not_found: 62 | tb = tb_gen_code(env, pc, cs_base, flags, 0); 63 | 64 | found: 65 | env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; 66 | return tb; 67 | 68 | 這裡小結一下 cpu_exec (cpu-exec.c) -> tb_find_fast (cpu-exec.c) 69 | -> tb_find_slow (cpu-exec.c) 70 | 71 | QEMU 先以 guest virtual address (GVA) 查找是否已有翻譯過的 TB,再以 guest physical 72 | address (GPA) 查找是否已有翻譯過的 TB。 73 | 74 | 如果沒有翻譯過的 TB,開始進行 guest binary -> TCG IR -> host binary 的翻譯。 75 | 大致流程如下: 76 | 77 | tb_gen_code (exec.c) -> cpu_gen_code (translate-all.c) 78 | -> gen_intermediate_code (target-i386/translate.c) 79 | -> tcg_gen_code (tcg/tcg.c) -> tcg_gen_code_common (tcg/tcg.c) 80 | 81 | - tb_gen_code 配置內存給 TB (TranslationBlock),再交由 cpu_gen_code。 82 | 83 | // 注意! 這裡會將 GVA 轉成 GPA。phys_pc 將交給之後的 tb_link_page 使用。 84 | phys_pc = get_page_addr_code(env, pc); 85 | tb = tb_alloc(pc); 86 | if (!tb) { 87 | // 清空 code cache 88 | } 89 | 90 | // 初始 tb 91 | 92 | // 開始 guest binary -> TCG IR -> host binary 的翻譯。 93 | cpu_gen_code(env, tb, &code_gen_size); 94 | 95 | // 將 tb 加入 tb_phys_hash 和二級頁表 l1_map。 96 | // phys_pc 和 phys_page2 分別代表 tb (guest pc) 對映的 GPA 和所屬的第二個 97 | // 頁面 (如果 tb 代表的 guest binary 跨頁面的話)。 98 | tb_link_page(tb, phys_pc, phys_page2); 99 | return tb; 100 | 101 | 我底下分別針對 cpu_gen_code 和 tb_link_page 稍微深入的介紹一下。 102 | 103 | - cpu_gen_code 負責 guest binary -> TCG IR -> host binary 的翻譯。 104 | 105 | // 初始 TCGContext 的 gen_opc_ptr 和 gen_opparam_ptr,使其分別指向 106 | // gen_opc_buf 和 gen_opparam_buf。gen_opc_buf 和 gen_opparam_buf 107 | // 分別存放 TCGOpcode 和 operand。 108 | tcg_func_start(s); 109 | 110 | // 呼叫 gen_intermediate_code_internal 產生 TCG IR 111 | gen_intermediate_code(env, tb); 112 | 113 | // TCG IR -> host binary 114 | gen_code_size = tcg_gen_code(s, gen_code_buf); 115 | 116 | o gen_intermediate_code_internal (target-*/translate.c) 初始化並呼叫 117 | disas_insn 反組譯 guest binary 成 TCG IR。disas_insn 呼叫 tcg_gen_xxx 118 | (tcg/tcg-op.h) 產生 TCG IR。分別將 opcode 寫入 gen_opc_ptr 指向的緩衝 119 | 區 (translate-all.c 裡的 gen_opc_buf); operand 寫入 gen_opparam_ptr 120 | 指向的緩衝區 (translate-all.c 裡的 gen_opparam_buf)。 121 | 122 | o tcg_gen_code (tcg/tcg.c) 呼叫 tcg_gen_code_common (tcg/tcg.c) 將 TCG 123 | IR 轉成 host binary。 124 | 125 | tcg_reg_alloc_start(s); 126 | 127 | s->code_buf = gen_code_buf; 128 | // host binary 會寫入 TCGContext s 的 code_ptr 所指向的緩衝區。 129 | s->code_ptr = gen_code_buf; 130 | 131 | 至此,guest binary -> TCG IR -> host binary 算是完成了。剩下把 TranslationBlock 132 | (TB) 納入 QEMU 的管理,這是 tb_link_page 做的事。 133 | 134 | - tb_link_page (exec.c) 把新的 TB 加進 tb_phys_hash 和 l1_map 二級頁表。 135 | tb_find_slow 會用 pc 對映的 GPA 的哈希值索引 tb_phys_hash。 136 | 137 | /* 把新的 TB 加進 tb_phys_hash */ 138 | h = tb_phys_hash_func(phys_pc); 139 | ptb = &tb_phys_hash[h]; 140 | tb->phys_hash_next = *ptb; // 如果兩個以上的 TB 其 phys_pc 的哈希值相同,則做 chaining。 141 | *ptb = tb; // 新加入的 TB 放至 chaining 的開頭。 142 | 143 | // 在 l1_map 中配置 PageDesc 給 TB,並設置 TB 的 page_addr 和 page_next。 144 | tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK); 145 | if (phys_page2 != -1) // TB 對應的 guest binary 跨頁 146 | tb_alloc_page(tb, 1, phys_page2); 147 | else 148 | tb->page_addr[1] = -1; 149 | 150 | // 以下和 block chaining 有關,留待下次再講,這邊暫且不提。 151 | tb->jmp_first = (TranslationBlock *)((long)tb | 2); 152 | 153 | o tb_alloc_page (exec.c) 設置 TB 的 page_addr 和 page_next,並在 l1_map 中配置 PageDesc 154 | 給 TB。 155 | 156 | static inline void tb_alloc_page(TranslationBlock *tb, 157 | unsigned int n, tb_page_addr_t page_addr) 158 | { 159 | // 代表 tb (guest binary) 所屬 guest page。 160 | tb->page_addr[n] = page_addr; 161 | // 在 l1_map 中配置一個 PageDesc,返回該 PageDesc。 162 | p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1); 163 | // 將該頁面目前第一個 TB 串接到此 TB。將來有需要將某頁面所屬所有 TB 清空。 164 | tb->page_next[n] = p->first_tb; 165 | // n 為 1 代表 tb 對應的 guest binary 跨 page。 166 | p->first_tb = (TranslationBlock *)((long)tb | n); 167 | // PageDesc 會維護一個 bitmap,這是給 SMC 之用。這裡不提。 168 | invalidate_page_bitmap(p); 169 | } 170 | 171 | 這裡先回顧一下,QEMU 查看當前 env->pc 是否已翻譯過。若否,則進行 172 | 翻譯。 173 | 174 | tb_find_fast (cpu-exec.c) -> tb_find_slow (cpu-exec.c) -> tb_gen_code (exec.c) 175 | 176 | tb_gen_code 講到這裡,guest binary -> host binary 已翻譯完成,相關資料結構也已設置完畢。 177 | 返回 TB (TranslationBlock *) 給 tb_find_fast。 178 | 179 | tb = tb_find_fast(); 180 | 181 | tc_ptr = tb->tc_ptr; // tc_ptr 指向 code cache (host binary) 182 | 183 | next_tb = tcg_qemu_tb_exec(tc_ptr); 184 | 185 | 很好,我們準備從 QEMU 跳入 code cache 開始執行了。:-) tcg_qemu_tb_exec 被定義在 tcg/tcg.h。 186 | 187 | #define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM (*)(void *))code_gen_prologue)(tb_ptr) 188 | 189 | (long REGPARM (*)(void *)) 將 code_gen_prologue 轉型成函式指針,void * 為該函式的參數, 190 | 返回值為 long。REGPARM 指示 GCC 此函式透過暫存器而非棧傳遞參數。至此, 191 | (long REGPARM (*)(void *)) 將數組指針 code_gen_prologue 轉型成函式指針。tb_ptr 為該函式 192 | 指針的參數。綜合以上所述,code_gen_prologue 被視為一函式,其參數為 tb_ptr,返回當前 TB 193 | (tc_ptr 代表的 TB,等講到 block chaining 會比較清楚)。code_gen_prologue 所做的事為一般 194 | 函式呼叫前的 prologue,之後將控制交由 tc_ptr 指向的 host binary 並開始執行。 195 | -------------------------------------------------------------------------------- /QEMU/QEMU_Mascot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/QEMU/QEMU_Mascot.png -------------------------------------------------------------------------------- /QEMU/Tutorial.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/QEMU/Tutorial.ppt -------------------------------------------------------------------------------- /QEMU/zruan0-2012-07-16.txt: -------------------------------------------------------------------------------- 1 | 17:05 < chenwj> zruan0: you there? 2 | 17:05 < zruan0> chenwj: yeah! 3 | 17:06 < chenwj> http://www.cse.iitb.ac.in/~puru/courses/spring11/cs620/references/kvm.pdf <- I am not sure where the singal pending is handled in fig. 2. 4 | Any idea on that? 5 | 17:07 < chenwj> I don't know if the flow chart still correct or not. I see vmx_handle_exit -> handle_invalid_guest_state -> check if signal_pending -> 6 | handle io 7 | 17:08 < chenwj> the order is a little bit different 8 | 17:12 < zruan0> chenwj: I think it is correct, how do you think it is wrong? 9 | 17:12 < chenwj> what is correct? 10 | 17:13 < chenwj> the flow chart? 11 | 17:13 < zruan0> Yes 12 | 17:14 < zruan0> anywhere you found it was not follow the chart? 13 | 17:14 < chenwj> zruan0: what I see is below, 14 | 17:15 < chenwj> vmx_handle_exit -> handle_invalid_guest_state -> check if signal_pending 15 | 17:16 < chenwj> I am not sure if above does handle pending signal or not. If so, after that kvm call handle_io 16 | 17:16 < chenwj> so the order is kind of reverse 17 | 17:20 < zruan0> maybe the point you are arguing is that "Handle Exit" is processed before "Signal Pending" in chart, but the code is reverse this order? 18 | 17:21 < zruan0> because handle_invalid_guest_state is called probably before kvm_vmx_exit_handlers[exit_reason](vcpu)?? 19 | 17:22 < chenwj> no, I am talking about "I/O?" and "Singal Pending?" part 20 | 17:23 < zruan0> chenwj: where do you think "I/O" is processed in code? 21 | 17:25 < chenwj> in the end of vmx_handle_exit (vmx.c), it will lookup kvm_vmx_exit_handlers[exit_reason], if exit_reason is io, then handle_io is called 22 | 17:27 < zruan0> chenwj: I think "I/O" in this chart corresponds to a concept, such as "handling devices", not a VMEXIT caused by IO instructions. 23 | 17:28 < chenwj> not sure what "handling devices" means... 24 | 17:29 < zruan0> chenwj: In my view, it equals to handling external interrupts, etc. 25 | 17:31 < chenwj> what's the difference between it and "signal panding"? 26 | 17:32 < zruan0> Linux will take over execution to handle external interrupts after kvm exits guests and enables interrupts again. 27 | 17:35 < zruan0> actually, I am also confused about the usage to signal in kvm, but I think it is possible that other processes would send signals to vm 28 | process. You know guest is runing as qemu context (as I understood). . 29 | 17:35 < chenwj> yes 30 | 17:37 < zruan0> Signals in linux is also used to do some "process control" work, but I think sometime vm also would possibly be issued signals by other 31 | process for special intents. this is my understanding. 32 | 17:38 < chenwj> agraf: have time? ^^^ 33 | 17:43 < zruan0> chenwj: handling interrupts is triggered by hardware, but signal is also a kind of "software interrupts" to process. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slide 2 | My Slide 3 | 4 | - Microsoft PowerPoint 5 | - Subject: Arial 6 | - Context: Segoe UI 7 | - [Six safe fonts to use in your next presentation](http://presentitude.com/c-fonts/) 8 | - [5 Classic Presentation Fonts](https://thepresentationdesigner.co.uk/5-classic-presentation-fonts/) 9 | -------------------------------------------------------------------------------- /Talk/hellogcc-2012/Makefile: -------------------------------------------------------------------------------- 1 | # Generic makefile for processing Beamer files 2 | # 3 | # Rouben Rostamian 4 | # December 2004 5 | 6 | BASE = hellogcc-2012-chenwj 7 | 8 | FIGURES = \ 9 | 10 | $(BASE).pdf: $(BASE).tex $(FIGURES) 11 | @(\ 12 | AUX=$(BASE).aux; \ 13 | if [ ! -f $$AUX ]; then pdflatex $(BASE); fi; \ 14 | pages1=`sed -n '/inserttotalframenumber/{ s/[^0-9]//g; p; }' < $$AUX`; \ 15 | pdflatex $(BASE); \ 16 | pages2=`sed -n '/inserttotalframenumber/{ s/[^0-9]//g; p; }' < $$AUX`; \ 17 | if [ $$pages1 -ne $$pages2 ]; then pdflatex $(BASE); fi; \ 18 | ) 19 | 20 | clean: 21 | @rm -f $(BASE).{aux,log,nav,out,pdf,snm,toc} 22 | -------------------------------------------------------------------------------- /Talk/hellogcc-2012/hellogcc-2012-chenwj.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/Talk/hellogcc-2012/hellogcc-2012-chenwj.pdf -------------------------------------------------------------------------------- /Talk/mclinker-2013/CaseStudy.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/Talk/mclinker-2013/CaseStudy.pptx -------------------------------------------------------------------------------- /Talk/mclinker-2013/MCLinker-IR.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/Talk/mclinker-2013/MCLinker-IR.pptx -------------------------------------------------------------------------------- /Talk/mclinker-2013/Makefile: -------------------------------------------------------------------------------- 1 | # Generic makefile for processing Beamer files 2 | # 3 | # Rouben Rostamian 4 | # December 2004 5 | 6 | BASE = mclinker-2013-chenwj 7 | 8 | FIGURES = fig/* 9 | DOTS = dot/* 10 | CODE = code/* 11 | 12 | $(BASE).pdf: $(BASE).tex $(FIGURES) ${DOTS} ${CODE} 13 | @(\ 14 | AUX=$(BASE).aux; \ 15 | if [ ! -f $$AUX ]; then pdflatex $(BASE); fi; \ 16 | pages1=`sed -n '/inserttotalframenumber/{ s/[^0-9]//g; p; }' < $$AUX`; \ 17 | pdflatex $(BASE); \ 18 | pages2=`sed -n '/inserttotalframenumber/{ s/[^0-9]//g; p; }' < $$AUX`; \ 19 | if [ $$pages1 -ne $$pages2 ]; then pdflatex $(BASE); fi; \ 20 | ) 21 | 22 | clean: 23 | @rm -f *.aux 24 | @rm -f *.log 25 | @rm -f *.nav 26 | @rm -f *.out 27 | @rm -f *.snm 28 | @rm -f *.toc 29 | @rm -f *.vrb 30 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/agenda.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/Talk/mclinker-2013/agenda.pptx -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/builder.cc: -------------------------------------------------------------------------------- 1 | // Create LLVMContext for latter use. 2 | LLVMContext Context; 3 | 4 | // Create some module to put our function into it. 5 | Module *M = new Module("test", Context); 6 | 7 | // Create function inside the module 8 | Function *Add1F = 9 | cast(M->getOrInsertFunction("add1", Type::getInt32Ty(Context), 10 | Type::getInt32Ty(Context), 11 | (Type *)0)); 12 | 13 | // Add a basic block to the function. 14 | BasicBlock *BB = BasicBlock::Create(Context, "EntryBlock", Add1F); 15 | 16 | // Create a basic block builder. The builder will append instructions 17 | // to the basic block 'BB'. 18 | IRBuilder<> builder(BB); 19 | 20 | // ... prepare operands for Add instrcution. ... 21 | 22 | // Create the add instruction, inserting it into the end of BB. 23 | Value *Add = builder.CreateAdd(One, ArgX); 24 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/builder.cc.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c+c1}{// Create LLVMContext for latter use.} 3 | \PY{n}{LLVMContext} \PY{n}{Context}\PY{p}{;} 4 | 5 | \PY{c+c1}{// Create some module to put our function into it.} 6 | \PY{n}{Module} \PY{o}{*}\PY{n}{M} \PY{o}{=} \PY{k}{new} \PY{n}{Module}\PY{p}{(}\PY{l+s}{\PYZdq{}}\PY{l+s}{test}\PY{l+s}{\PYZdq{}}\PY{p}{,} \PY{n}{Context}\PY{p}{)}\PY{p}{;} 7 | 8 | \PY{c+c1}{// Create function inside the module} 9 | \PY{n}{Function} \PY{o}{*}\PY{n}{Add1F} \PY{o}{=} 10 | \PY{n}{cast}\PY{o}{\PYZlt{}}\PY{n}{Function}\PY{o}{\PYZgt{}}\PY{p}{(}\PY{n}{M}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{getOrInsertFunction}\PY{p}{(}\PY{l+s}{\PYZdq{}}\PY{l+s}{add1}\PY{l+s}{\PYZdq{}}\PY{p}{,} \PY{n}{Type}\PY{o}{:}\PY{o}{:}\PY{n}{getInt32Ty}\PY{p}{(}\PY{n}{Context}\PY{p}{)}\PY{p}{,} 11 | \PY{n}{Type}\PY{o}{:}\PY{o}{:}\PY{n}{getInt32Ty}\PY{p}{(}\PY{n}{Context}\PY{p}{)}\PY{p}{,} 12 | \PY{p}{(}\PY{n}{Type} \PY{o}{*}\PY{p}{)}\PY{l+m+mi}{0}\PY{p}{)}\PY{p}{)}\PY{p}{;} 13 | 14 | \PY{c+c1}{// Add a basic block to the function.} 15 | \PY{n}{BasicBlock} \PY{o}{*}\PY{n}{BB} \PY{o}{=} \PY{n}{BasicBlock}\PY{o}{:}\PY{o}{:}\PY{n}{Create}\PY{p}{(}\PY{n}{Context}\PY{p}{,} \PY{l+s}{\PYZdq{}}\PY{l+s}{EntryBlock}\PY{l+s}{\PYZdq{}}\PY{p}{,} \PY{n}{Add1F}\PY{p}{)}\PY{p}{;} 16 | 17 | \PY{c+c1}{// Create a basic block builder. The builder will append instructions} 18 | \PY{c+c1}{// to the basic block \PYZsq{}BB\PYZsq{}.} 19 | \PY{n}{IRBuilder}\PY{o}{\PYZlt{}}\PY{o}{\PYZgt{}} \PY{n}{builder}\PY{p}{(}\PY{n}{BB}\PY{p}{)}\PY{p}{;} 20 | 21 | \PY{c+c1}{// ... prepare operands for Add instrcution. ...} 22 | 23 | \PY{c+c1}{// Create the add instruction, inserting it into the end of BB.} 24 | \PY{n}{Value} \PY{o}{*}\PY{n}{Add} \PY{o}{=} \PY{n}{builder}\PY{p}{.}\PY{n}{CreateAdd}\PY{p}{(}\PY{n}{One}\PY{p}{,} \PY{n}{ArgX}\PY{p}{)}\PY{p}{;} 25 | \end{Verbatim} 26 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/builder_fib.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'fib.c' 2 | 3 | define i32 @fib(i32 %x) nounwind uwtable { 4 | entry: 5 | ... 6 | %cmp = icmp sle i32 %0, 2 7 | br i1 %cmp, label %if.then, label %if.else 8 | 9 | if.then: ; preds = %entry 10 | store i32 1, i32* %sum, align 4 11 | br label %if.end 12 | 13 | if.else: ; preds = %entry 14 | ... 15 | %call2 = call i32 @fib(i32 %sub1) 16 | %add = add nsw i32 %call, %call2 17 | store i32 %add, i32* %sum, align 4 18 | br label %if.end 19 | 20 | if.end: ; preds = %if.else, %if.then 21 | %3 = load i32* %sum, align 4 22 | ret i32 %3 23 | } 24 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/builder_fib.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c}{; ModuleID = \PYZsq{}fib.c\PYZsq{}} 3 | 4 | \PY{k}{define} \PY{k}{i32} \PY{n+nv+vg}{@fib}\PY{p}{(}\PY{k}{i32} \PY{n+nv}{\PYZpc{}x}\PY{p}{)} \PY{k}{nounwind} \PY{k}{uwtable} \PY{p}{\PYZob{}} 5 | \PY{n+nl}{entry:} 6 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 7 | \PY{n+nv}{\PYZpc{}cmp} \PY{p}{=} \PY{k}{icmp} \PY{k}{sle} \PY{k}{i32} \PY{n+nv+nvAnonymous}{\PYZpc{}0}\PY{p}{,} \PY{l+m}{2} 8 | \PY{k}{br} \PY{k}{i1} \PY{n+nv}{\PYZpc{}cmp}\PY{p}{,} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.then}\PY{p}{,} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.else} 9 | 10 | \PY{n+nl}{if.then:} \PY{c}{; preds = \PYZpc{}entry} 11 | \PY{k}{store} \PY{k}{i32} \PY{l+m}{1}\PY{p}{,} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}sum}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 12 | \PY{k}{br} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.end} 13 | 14 | \PY{n+nl}{if.else:} \PY{c}{; preds = \PYZpc{}entry} 15 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 16 | \PY{n+nv}{\PYZpc{}call2} \PY{p}{=} \PY{k}{call} \PY{k}{i32} \PY{n+nv+vg}{@fib}\PY{p}{(}\PY{k}{i32} \PY{n+nv}{\PYZpc{}sub1}\PY{p}{)} 17 | \PY{n+nv}{\PYZpc{}add} \PY{p}{=} \PY{k}{add} \PY{k}{nsw} \PY{k}{i32} \PY{n+nv}{\PYZpc{}call}\PY{p}{,} \PY{n+nv}{\PYZpc{}call2} 18 | \PY{k}{store} \PY{k}{i32} \PY{n+nv}{\PYZpc{}add}\PY{p}{,} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}sum}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 19 | \PY{k}{br} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.end} 20 | 21 | \PY{n+nl}{if.end:} \PY{c}{; preds = \PYZpc{}if.else, \PYZpc{}if.then} 22 | \PY{n+nv+nvAnonymous}{\PYZpc{}3} \PY{p}{=} \PY{k}{load} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}sum}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 23 | \PY{k}{ret} \PY{k}{i32} \PY{n+nv+nvAnonymous}{\PYZpc{}3} 24 | \PY{p}{\PYZcb{}} 25 | \end{Verbatim} 26 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/cmdline.sh: -------------------------------------------------------------------------------- 1 | $ clang -c relax.s 2 | $ objdump -d relax.o 3 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.c: -------------------------------------------------------------------------------- 1 | int fib(int x) { 2 | if (x <= 2) 3 | return 1; 4 | 5 | return fib(x - 1) + fib(x - 2); 6 | } 7 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.c.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{k+kt}{int} \PY{n+nf}{fib}\PY{p}{(}\PY{k+kt}{int} \PY{n}{x}\PY{p}{)} \PY{p}{\PYZob{}} 3 | \PY{k}{if} \PY{p}{(}\PY{n}{x} \PY{o}{\PYZlt{}}\PY{o}{=} \PY{l+m+mi}{2}\PY{p}{)} 4 | \PY{k}{return} \PY{l+m+mi}{1}\PY{p}{;} 5 | 6 | \PY{k}{return} \PY{n}{fib}\PY{p}{(}\PY{n}{x} \PY{o}{\PYZhy{}} \PY{l+m+mi}{1}\PY{p}{)} \PY{o}{+} \PY{n}{fib}\PY{p}{(}\PY{n}{x} \PY{o}{\PYZhy{}} \PY{l+m+mi}{2}\PY{p}{)}\PY{p}{;} 7 | \PY{p}{\PYZcb{}} 8 | \end{Verbatim} 9 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.c.tex.aux: -------------------------------------------------------------------------------- 1 | \relax 2 | \@setckpt{code/fib.c.tex}{ 3 | \setcounter{page}{3} 4 | \setcounter{equation}{0} 5 | \setcounter{enumi}{0} 6 | \setcounter{enumii}{0} 7 | \setcounter{enumiii}{0} 8 | \setcounter{enumiv}{0} 9 | \setcounter{footnote}{0} 10 | \setcounter{mpfootnote}{0} 11 | \setcounter{beamerpauses}{1} 12 | \setcounter{lecture}{0} 13 | \setcounter{part}{0} 14 | \setcounter{section}{0} 15 | \setcounter{subsection}{0} 16 | \setcounter{subsubsection}{0} 17 | \setcounter{subsectionslide}{3} 18 | \setcounter{framenumber}{3} 19 | \setcounter{figure}{0} 20 | \setcounter{table}{0} 21 | \setcounter{parentequation}{0} 22 | \setcounter{theorem}{0} 23 | \setcounter{lstlisting}{0} 24 | \setcounter{lstnumber}{1} 25 | \setcounter{FancyVerbLine}{0} 26 | \setcounter{section@level}{0} 27 | } 28 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.ll: -------------------------------------------------------------------------------- 1 | define i32 @fib(i32 %x) nounwind uwtable { 2 | entry: 3 | %retval = alloca i32, align 4 4 | %x.addr = alloca i32, align 4 5 | store i32 %x, i32* %x.addr, align 4 6 | %0 = load i32* %x.addr, align 4 7 | %cmp = icmp sle i32 %0, 2 8 | br i1 %cmp, label %if.then, label %if.end 9 | 10 | if.then: ; preds = %entry 11 | store i32 1, i32* %retval 12 | br label %return 13 | 14 | if.end: ; preds = %entry 15 | %1 = load i32* %x.addr, align 4 16 | %sub = sub nsw i32 %1, 1 17 | %call = call i32 @fib(i32 %sub) 18 | %2 = load i32* %x.addr, align 4 19 | %sub1 = sub nsw i32 %2, 2 20 | %call2 = call i32 @fib(i32 %sub1) 21 | %add = add nsw i32 %call, %call2 22 | store i32 %add, i32* %retval 23 | br label %return 24 | 25 | return: ; preds = %if.end, %if.then 26 | %3 = load i32* %retval 27 | ret i32 %3 28 | } 29 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{k}{define} \PY{k}{i32} \PY{n+nv+vg}{@fib}\PY{p}{(}\PY{k}{i32} \PY{n+nv}{\PYZpc{}x}\PY{p}{)} \PY{k}{nounwind} \PY{err}{u}\PY{err}{w}\PY{err}{t}\PY{err}{a}\PY{err}{b}\PY{err}{l}\PY{err}{e} \PY{p}{\PYZob{}} 3 | \PY{n+nl}{entry:} 4 | \PY{n+nv}{\PYZpc{}retval} \PY{p}{=} \PY{k}{alloca} \PY{k}{i32}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 5 | \PY{n+nv}{\PYZpc{}x.addr} \PY{p}{=} \PY{k}{alloca} \PY{k}{i32}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 6 | \PY{k}{store} \PY{k}{i32} \PY{n+nv}{\PYZpc{}x}\PY{p}{,} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}x.addr}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 7 | \PY{n+nv+nvAnonymous}{\PYZpc{}0} \PY{p}{=} \PY{k}{load} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}x.addr}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 8 | \PY{n+nv}{\PYZpc{}cmp} \PY{p}{=} \PY{k}{icmp} \PY{k}{sle} \PY{k}{i32} \PY{n+nv+nvAnonymous}{\PYZpc{}0}\PY{p}{,} \PY{l+m}{2} 9 | \PY{k}{br} \PY{k}{i1} \PY{n+nv}{\PYZpc{}cmp}\PY{p}{,} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.then}\PY{p}{,} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.end} 10 | 11 | \PY{n+nl}{if.then:} \PY{c}{; preds = \PYZpc{}entry} 12 | \PY{k}{store} \PY{k}{i32} \PY{l+m}{1}\PY{p}{,} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}retval} 13 | \PY{k}{br} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}return} 14 | 15 | \PY{n+nl}{if.end:} \PY{c}{; preds = \PYZpc{}entry} 16 | \PY{n+nv+nvAnonymous}{\PYZpc{}1} \PY{p}{=} \PY{k}{load} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}x.addr}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 17 | \PY{n+nv}{\PYZpc{}sub} \PY{p}{=} \PY{k}{sub} \PY{k}{nsw} \PY{k}{i32} \PY{n+nv+nvAnonymous}{\PYZpc{}1}\PY{p}{,} \PY{l+m}{1} 18 | \PY{n+nv}{\PYZpc{}call} \PY{p}{=} \PY{k}{call} \PY{k}{i32} \PY{n+nv+vg}{@fib}\PY{p}{(}\PY{k}{i32} \PY{n+nv}{\PYZpc{}sub}\PY{p}{)} 19 | \PY{n+nv+nvAnonymous}{\PYZpc{}2} \PY{p}{=} \PY{k}{load} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}x.addr}\PY{p}{,} \PY{k}{align} \PY{l+m}{4} 20 | \PY{n+nv}{\PYZpc{}sub1} \PY{p}{=} \PY{k}{sub} \PY{k}{nsw} \PY{k}{i32} \PY{n+nv+nvAnonymous}{\PYZpc{}2}\PY{p}{,} \PY{l+m}{2} 21 | \PY{n+nv}{\PYZpc{}call2} \PY{p}{=} \PY{k}{call} \PY{k}{i32} \PY{n+nv+vg}{@fib}\PY{p}{(}\PY{k}{i32} \PY{n+nv}{\PYZpc{}sub1}\PY{p}{)} 22 | \PY{n+nv}{\PYZpc{}add} \PY{p}{=} \PY{k}{add} \PY{k}{nsw} \PY{k}{i32} \PY{n+nv}{\PYZpc{}call}\PY{p}{,} \PY{n+nv}{\PYZpc{}call2} 23 | \PY{k}{store} \PY{k}{i32} \PY{n+nv}{\PYZpc{}add}\PY{p}{,} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}retval} 24 | \PY{k}{br} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}return} 25 | 26 | \PY{n+nl}{return:} \PY{c}{; preds = \PYZpc{}if.end, \PYZpc{}if.then} 27 | \PY{n+nv+nvAnonymous}{\PYZpc{}3} \PY{p}{=} \PY{k}{load} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}retval} 28 | \PY{k}{ret} \PY{k}{i32} \PY{n+nv+nvAnonymous}{\PYZpc{}3} 29 | \PY{p}{\PYZcb{}} 30 | \end{Verbatim} 31 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.s: -------------------------------------------------------------------------------- 1 | .file "fib.ll" 2 | .text 3 | .globl fib 4 | .align 16, 0x90 5 | .type fib,@function 6 | fib: # @fib 7 | .cfi_startproc 8 | # BB#0: # %entry 9 | pushq %rbx 10 | .Ltmp2: 11 | .cfi_def_cfa_offset 16 12 | subq $16, %rsp 13 | .Ltmp3: 14 | .cfi_def_cfa_offset 32 15 | .Ltmp4: 16 | .cfi_offset %rbx, -16 17 | movl %edi, 8(%rsp) 18 | cmpl $2, %edi 19 | jg .LBB0_2 20 | # BB#1: # %if.then 21 | movl $1, 12(%rsp) 22 | jmp .LBB0_3 23 | .LBB0_2: # %if.end 24 | movl 8(%rsp), %edi 25 | decl %edi 26 | callq fib 27 | movl %eax, %ebx 28 | movl 8(%rsp), %edi 29 | addl $-2, %edi 30 | callq fib 31 | addl %ebx, %eax 32 | movl %eax, 12(%rsp) 33 | .LBB0_3: # %return 34 | movl 12(%rsp), %eax 35 | addq $16, %rsp 36 | popq %rbx 37 | ret 38 | .Ltmp5: 39 | .size fib, .Ltmp5-fib 40 | .cfi_endproc 41 | 42 | 43 | .section ".note.GNU-stack","",@progbits 44 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.simply.ll: -------------------------------------------------------------------------------- 1 | define i32 @fib(i32 %x) nounwind uwtable { 2 | entry: 3 | ... 4 | br i1 %cmp, label %if.then, label %if.end 5 | 6 | if.then: ; preds = %entry 7 | store i32 1, i32* %retval 8 | br label %return 9 | 10 | if.end: ; preds = %entry 11 | ... 12 | store i32 %add, i32* %retval 13 | br label %return 14 | 15 | return: ; preds = %if.end, %if.then 16 | %3 = load i32* %retval 17 | ret i32 %3 18 | } 19 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/fib.simply.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{k}{define} \PY{k}{i32} \PY{n+nv+vg}{@fib}\PY{p}{(}\PY{k}{i32} \PY{n+nv}{\PYZpc{}x}\PY{p}{)} \PY{k}{nounwind} \PY{k}{uwtable} \PY{p}{\PYZob{}} 3 | \PY{n+nl}{entry:} 4 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 5 | \PY{k}{br} \PY{k}{i1} \PY{n+nv}{\PYZpc{}cmp}\PY{p}{,} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.then}\PY{p}{,} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}if.end} 6 | 7 | \PY{n+nl}{if.then:} \PY{c}{; preds = \PYZpc{}entry} 8 | \PY{k}{store} \PY{k}{i32} \PY{l+m}{1}\PY{p}{,} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}retval} 9 | \PY{k}{br} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}return} 10 | 11 | \PY{n+nl}{if.end:} \PY{c}{; preds = \PYZpc{}entry} 12 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 13 | \PY{k}{store} \PY{k}{i32} \PY{n+nv}{\PYZpc{}add}\PY{p}{,} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}retval} 14 | \PY{k}{br} \PY{k+kt}{label} \PY{n+nv}{\PYZpc{}return} 15 | 16 | \PY{n+nl}{return:} \PY{c}{; preds = \PYZpc{}if.end, \PYZpc{}if.then} 17 | \PY{n+nv+nvAnonymous}{\PYZpc{}3} \PY{p}{=} \PY{k}{load} \PY{k}{i32}\PY{p}{*} \PY{n+nv}{\PYZpc{}retval} 18 | \PY{k}{ret} \PY{k}{i32} \PY{n+nv+nvAnonymous}{\PYZpc{}3} 19 | \PY{p}{\PYZcb{}} 20 | \end{Verbatim} 21 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_0.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | printf{"Hello World!\n); 5 | } 6 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_1.cc: -------------------------------------------------------------------------------- 1 | #include "llvm/LLVMContext.h" 2 | #include "llvm/Module.h" 3 | #include "llvm/IRBuilder.h" 4 | 5 | int main() 6 | { 7 | llvm::LLVMContext& context = llvm::getGlobalContext(); 8 | llvm::Module* module = new llvm::Module("top", context); 9 | llvm::IRBuilder<> builder(context); 10 | 11 | module->dump( ); 12 | } 13 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_1.cc.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c+cp}{\PYZsh{}}\PY{c+cp}{include \PYZdq{}llvm}\PY{c+cp}{/}\PY{c+cp}{LLVMContext.h\PYZdq{}} 3 | \PY{c+cp}{\PYZsh{}}\PY{c+cp}{include \PYZdq{}llvm}\PY{c+cp}{/}\PY{c+cp}{Module.h\PYZdq{}} 4 | \PY{c+cp}{\PYZsh{}}\PY{c+cp}{include \PYZdq{}llvm}\PY{c+cp}{/}\PY{c+cp}{IRBuilder.h\PYZdq{}} 5 | 6 | \PY{k+kt}{int} \PY{n+nf}{main}\PY{p}{(}\PY{p}{)} 7 | \PY{p}{\PYZob{}} 8 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{LLVMContext}\PY{o}{\PYZam{}} \PY{n}{context} \PY{o}{=} \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{getGlobalContext}\PY{p}{(}\PY{p}{)}\PY{p}{;} 9 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Module}\PY{o}{*} \PY{n}{module} \PY{o}{=} \PY{k}{new} \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Module}\PY{p}{(}\PY{l+s}{\PYZdq{}}\PY{l+s}{top}\PY{l+s}{\PYZdq{}}\PY{p}{,} \PY{n}{context}\PY{p}{)}\PY{p}{;} 10 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{IRBuilder}\PY{o}{\PYZlt{}}\PY{o}{\PYZgt{}} \PY{n}{builder}\PY{p}{(}\PY{n}{context}\PY{p}{)}\PY{p}{;} 11 | 12 | \PY{n}{module}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{dump}\PY{p}{(} \PY{p}{)}\PY{p}{;} 13 | \PY{p}{\PYZcb{}} 14 | \end{Verbatim} 15 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_1.cpp.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/Talk/mclinker-2013/code/hello_1.cpp.tex -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_1.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'top' 2 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_1.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c}{; ModuleID = \PYZsq{}top\PYZsq{}} 3 | \end{Verbatim} 4 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_2.cc: -------------------------------------------------------------------------------- 1 | ... 2 | 3 | llvm::FunctionType *funcType = 4 | llvm::FunctionType::get(builder.getInt32Ty(), false); 5 | llvm::Function *mainFunc = 6 | llvm::Function::Create(funcType, 7 | llvm::Function::ExternalLinkage, 8 | "main", module); 9 | 10 | module->dump( ); 11 | } 12 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_2.cc.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 3 | 4 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{FunctionType} \PY{o}{*}\PY{n}{funcType} \PY{o}{=} 5 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{FunctionType}\PY{o}{:}\PY{o}{:}\PY{n}{get}\PY{p}{(}\PY{n}{builder}\PY{p}{.}\PY{n}{getInt32Ty}\PY{p}{(}\PY{p}{)}\PY{p}{,} \PY{n+nb}{false}\PY{p}{)}\PY{p}{;} 6 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Function} \PY{o}{*}\PY{n}{mainFunc} \PY{o}{=} 7 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Function}\PY{o}{:}\PY{o}{:}\PY{n}{Create}\PY{p}{(}\PY{n}{funcType}\PY{p}{,} 8 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Function}\PY{o}{:}\PY{o}{:}\PY{n}{ExternalLinkage}\PY{p}{,} 9 | \PY{l+s}{\PYZdq{}}\PY{l+s}{main}\PY{l+s}{\PYZdq{}}\PY{p}{,} \PY{n}{module}\PY{p}{)}\PY{p}{;} 10 | 11 | \PY{n}{module}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{dump}\PY{p}{(} \PY{p}{)}\PY{p}{;} 12 | \PY{p}{\PYZcb{}} 13 | \end{Verbatim} 14 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_2.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'top' 2 | 3 | declare i32 @main() 4 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_2.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c}{; ModuleID = \PYZsq{}top\PYZsq{}} 3 | 4 | \PY{k}{declare} \PY{k}{i32} \PY{n+nv+vg}{@main}\PY{p}{(}\PY{p}{)} 5 | \end{Verbatim} 6 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_3.cc: -------------------------------------------------------------------------------- 1 | ... 2 | 3 | llvm::BasicBlock *entry = 4 | llvm::BasicBlock::Create(context, "entrypoint", mainFunc); 5 | builder.SetInsertPoint(entry); 6 | 7 | module->dump( ); 8 | } 9 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_3.cc.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 3 | 4 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{BasicBlock} \PY{o}{*}\PY{n}{entry} \PY{o}{=} 5 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{BasicBlock}\PY{o}{:}\PY{o}{:}\PY{n}{Create}\PY{p}{(}\PY{n}{context}\PY{p}{,} \PY{l+s}{\PYZdq{}}\PY{l+s}{entrypoint}\PY{l+s}{\PYZdq{}}\PY{p}{,} \PY{n}{mainFunc}\PY{p}{)}\PY{p}{;} 6 | \PY{n}{builder}\PY{p}{.}\PY{n}{SetInsertPoint}\PY{p}{(}\PY{n}{entry}\PY{p}{)}\PY{p}{;} 7 | 8 | \PY{n}{module}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{dump}\PY{p}{(} \PY{p}{)}\PY{p}{;} 9 | \PY{p}{\PYZcb{}} 10 | \end{Verbatim} 11 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_3.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'top' 2 | 3 | define i32 @main() { 4 | entrypoint: 5 | } 6 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_3.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c}{; ModuleID = \PYZsq{}top\PYZsq{}} 3 | 4 | \PY{k}{define} \PY{k}{i32} \PY{n+nv+vg}{@main}\PY{p}{(}\PY{p}{)} \PY{p}{\PYZob{}} 5 | \PY{n+nl}{entrypoint:} 6 | \PY{p}{\PYZcb{}} 7 | \end{Verbatim} 8 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_4.cc: -------------------------------------------------------------------------------- 1 | ... 2 | 3 | llvm::Value *helloWorld 4 | = builder.CreateGlobalStringPtr("hello world!\n"); 5 | 6 | module->dump( ); 7 | } 8 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_4.cc.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 3 | 4 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Value} \PY{o}{*}\PY{n}{helloWorld} 5 | \PY{o}{=} \PY{n}{builder}\PY{p}{.}\PY{n}{CreateGlobalStringPtr}\PY{p}{(}\PY{l+s}{\PYZdq{}}\PY{l+s}{hello world!}\PY{l+s+se}{\PYZbs{}n}\PY{l+s}{\PYZdq{}}\PY{p}{)}\PY{p}{;} 6 | 7 | \PY{n}{module}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{dump}\PY{p}{(} \PY{p}{)}\PY{p}{;} 8 | \PY{p}{\PYZcb{}} 9 | \end{Verbatim} 10 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_4.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'top' 2 | 3 | @0 = private unnamed_addr constant [14 x i8] c"hello world!\0A\00" 4 | 5 | define void @main() { 6 | entrypoint: 7 | } 8 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_4.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c}{; ModuleID = \PYZsq{}top\PYZsq{}} 3 | 4 | \PY{n+nv+vg}{@0} \PY{p}{=} \PY{k}{private} \PY{k}{unnamed_addr} \PY{k}{constant} \PY{p}{[}\PY{l+m}{14} \PY{k}{x} \PY{k}{i8}\PY{p}{]} \PY{k}{c}\PY{l+s}{\PYZdq{}hello world!\PYZbs{}0A\PYZbs{}00\PYZdq{}} 5 | 6 | \PY{k}{define} \PY{k+kt}{void} \PY{n+nv+vg}{@main}\PY{p}{(}\PY{p}{)} \PY{p}{\PYZob{}} 7 | \PY{n+nl}{entrypoint:} 8 | \PY{p}{\PYZcb{}} 9 | \end{Verbatim} 10 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_5.cc: -------------------------------------------------------------------------------- 1 | ... 2 | 3 | std::vector putsArgs; 4 | putsArgs.push_back(builder.getInt8Ty()->getPointerTo()); 5 | llvm::ArrayRef argsRef(putsArgs); 6 | 7 | llvm::FunctionType *putsType = 8 | llvm::FunctionType::get(builder.getInt32Ty(), argsRef, false); 9 | llvm::Constant *putsFunc 10 | = module->getOrInsertFunction("puts", putsType); 11 | 12 | module->dump(); 13 | } 14 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_5.cc.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 3 | 4 | \PY{n}{std}\PY{o}{:}\PY{o}{:}\PY{n}{vector}\PY{o}{\PYZlt{}}\PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Type} \PY{o}{*}\PY{o}{\PYZgt{}} \PY{n}{putsArgs}\PY{p}{;} 5 | \PY{n}{putsArgs}\PY{p}{.}\PY{n}{push\PYZus{}back}\PY{p}{(}\PY{n}{builder}\PY{p}{.}\PY{n}{getInt8Ty}\PY{p}{(}\PY{p}{)}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{getPointerTo}\PY{p}{(}\PY{p}{)}\PY{p}{)}\PY{p}{;} 6 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{ArrayRef}\PY{o}{\PYZlt{}}\PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Type}\PY{o}{*}\PY{o}{\PYZgt{}} \PY{n}{argsRef}\PY{p}{(}\PY{n}{putsArgs}\PY{p}{)}\PY{p}{;} 7 | 8 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{FunctionType} \PY{o}{*}\PY{n}{putsType} \PY{o}{=} 9 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{FunctionType}\PY{o}{:}\PY{o}{:}\PY{n}{get}\PY{p}{(}\PY{n}{builder}\PY{p}{.}\PY{n}{getInt32Ty}\PY{p}{(}\PY{p}{)}\PY{p}{,} \PY{n}{argsRef}\PY{p}{,} \PY{n+nb}{false}\PY{p}{)}\PY{p}{;} 10 | \PY{n}{llvm}\PY{o}{:}\PY{o}{:}\PY{n}{Constant} \PY{o}{*}\PY{n}{putsFunc} 11 | \PY{o}{=} \PY{n}{module}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{getOrInsertFunction}\PY{p}{(}\PY{l+s}{\PYZdq{}}\PY{l+s}{puts}\PY{l+s}{\PYZdq{}}\PY{p}{,} \PY{n}{putsType}\PY{p}{)}\PY{p}{;} 12 | 13 | \PY{n}{module}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{dump}\PY{p}{(}\PY{p}{)}\PY{p}{;} 14 | \PY{p}{\PYZcb{}} 15 | \end{Verbatim} 16 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_5.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'top' 2 | 3 | @0 = private unnamed_addr constant [14 x i8] c"hello world!\0A\00" 4 | 5 | define void @main() { 6 | entrypoint: 7 | } 8 | 9 | declare i32 @puts(i8*) 10 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_5.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c}{; ModuleID = \PYZsq{}top\PYZsq{}} 3 | 4 | \PY{n+nv+vg}{@0} \PY{p}{=} \PY{k}{private} \PY{err}{u}\PY{err}{n}\PY{err}{n}\PY{err}{a}\PY{err}{m}\PY{err}{e}\PY{err}{d}\PY{err}{\PYZus{}}\PY{err}{a}\PY{err}{d}\PY{err}{d}\PY{err}{r} \PY{k}{constant} \PY{p}{[}\PY{l+m}{14} \PY{k}{x} \PY{k}{i8}\PY{p}{]} \PY{k}{c}\PY{l+s}{\PYZdq{}hello world!\PYZbs{}0A\PYZbs{}00\PYZdq{}} 5 | 6 | \PY{k}{define} \PY{k+kt}{void} \PY{n+nv+vg}{@main}\PY{p}{(}\PY{p}{)} \PY{p}{\PYZob{}} 7 | \PY{n+nl}{entrypoint:} 8 | \PY{p}{\PYZcb{}} 9 | 10 | \PY{k}{declare} \PY{k}{i32} \PY{n+nv+vg}{@puts}\PY{p}{(}\PY{k}{i8}\PY{p}{*}\PY{p}{)} 11 | \end{Verbatim} 12 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_6.cc: -------------------------------------------------------------------------------- 1 | ... 2 | 3 | builder.CreateCall(putsFunc, helloWorld); 4 | builder.CreateRetVoid(); 5 | 6 | module->dump(); 7 | } 8 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_6.cc.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{p}{.}\PY{p}{.}\PY{p}{.} 3 | 4 | \PY{n}{builder}\PY{p}{.}\PY{n}{CreateCall}\PY{p}{(}\PY{n}{putsFunc}\PY{p}{,} \PY{n}{helloWorld}\PY{p}{)}\PY{p}{;} 5 | \PY{n}{builder}\PY{p}{.}\PY{n}{CreateRetVoid}\PY{p}{(}\PY{p}{)}\PY{p}{;} 6 | 7 | \PY{n}{module}\PY{o}{\PYZhy{}}\PY{o}{\PYZgt{}}\PY{n}{dump}\PY{p}{(}\PY{p}{)}\PY{p}{;} 8 | \PY{p}{\PYZcb{}} 9 | \end{Verbatim} 10 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_6.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'top' 2 | 3 | @0 = private unnamed_addr constant [14 x i8] c"hello world!\0A\00" 4 | 5 | define void @main() { 6 | entrypoint: 7 | %0 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @0, i32 0, i32 0)) 8 | ret void 9 | } 10 | 11 | declare i32 @puts(i8*) 12 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/hello_6.ll.tex: -------------------------------------------------------------------------------- 1 | \begin{Verbatim}[commandchars=\\\{\}] 2 | \PY{c}{; ModuleID = \PYZsq{}top\PYZsq{}} 3 | 4 | \PY{n+nv+vg}{@0} \PY{p}{=} \PY{k}{private} \PY{err}{u}\PY{err}{n}\PY{err}{n}\PY{err}{a}\PY{err}{m}\PY{err}{e}\PY{err}{d}\PY{err}{\PYZus{}}\PY{err}{a}\PY{err}{d}\PY{err}{d}\PY{err}{r} \PY{k}{constant} \PY{p}{[}\PY{l+m}{14} \PY{k}{x} \PY{k}{i8}\PY{p}{]} \PY{k}{c}\PY{l+s}{\PYZdq{}hello world!\PYZbs{}0A\PYZbs{}00\PYZdq{}} 5 | 6 | \PY{k}{define} \PY{k+kt}{void} \PY{n+nv+vg}{@main}\PY{p}{(}\PY{p}{)} \PY{p}{\PYZob{}} 7 | \PY{n+nl}{entrypoint:} 8 | \PY{n+nv+nvAnonymous}{\PYZpc{}0} \PY{p}{=} \PY{k}{call} \PY{k}{i32} \PY{n+nv+vg}{@puts}\PY{p}{(}\PY{k}{i8}\PY{p}{*} \PY{k}{getelementptr} \PY{k}{inbounds} \PY{p}{(}\PY{p}{[}\PY{l+m}{14} \PY{k}{x} \PY{k}{i8}\PY{p}{]}\PY{p}{*} \PY{n+nv+vg}{@0}\PY{p}{,} \PY{k}{i32} \PY{l+m}{0}\PY{p}{,} \PY{k}{i32} \PY{l+m}{0}\PY{p}{)}\PY{p}{)} 9 | \PY{k}{ret} \PY{k+kt}{void} 10 | \PY{p}{\PYZcb{}} 11 | 12 | \PY{k}{declare} \PY{k}{i32} \PY{n+nv+vg}{@puts}\PY{p}{(}\PY{k}{i8}\PY{p}{*}\PY{p}{)} 13 | \end{Verbatim} 14 | -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/mi.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/Talk/mclinker-2013/code/mi.txt -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/out.bc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azru0512/slide/d93c9362c363a51c2f8c4a4bd26e953301319b08/Talk/mclinker-2013/code/out.bc -------------------------------------------------------------------------------- /Talk/mclinker-2013/code/out.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'out.bc' 2 | target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" 3 | target triple = "amd64-portbld-freebsd8.3" 4 | 5 | define zeroext i1 @_Z9predicatebb(i1 zeroext %x, i1 zeroext %y) nounwind { 6 | %1 = alloca i8, align 1 7 | %2 = alloca i8, align 1 8 | %z = alloca i8, align 1 9 | %3 = zext i1 %x to i8 10 | store i8 %3, i8* %1, align 1 11 | %4 = zext i1 %y to i8 12 | store i8 %4, i8* %2, align 1 13 | %5 = load i8* %2, align 1 14 | %6 = trunc i8 %5 to i1 15 | br i1 %6, label %10, label %7 16 | 17 | ;