關于我們
書單推薦
新書推薦
|
自制編譯器
本書將帶領讀者從頭開始制作一門語言的編譯器。筆者特意為本書設計了C?語言,C?可以說是C語言的子集,實現(xiàn)了包括指針運算等在內(nèi)的C語言的主要部分。本書所實現(xiàn)的編譯器就是C?語言的編譯器, 是實實在在的編譯器,而非有諸多限制的玩具。另外,除編譯器之外,本書對以編譯器為中心的編程語言的運行環(huán)境,即編譯器、匯編器、鏈接器、硬件、運行時環(huán)境等都有所提及,介紹了程序運行的所有環(huán)節(jié)。
貫穿編譯、匯編、鏈接、加載的全過程!
比“龍書”更具實踐性! 1.實戰(zhàn) 通過實際動手制作一個精簡版C語言編譯器,讓讀者深入了解C語言程序編譯、運行背后的細節(jié)。 2.全面 不僅限于編譯器,對以編譯器為中心的編程語言的運行環(huán)境,即編譯器、匯編器、鏈接器、硬件以及運行時環(huán)境等,均有所涉及。 3.杰出 日本知名技術書作家青木峰郎耗時3年精心打造,通過具體的例子講解概念,通俗易懂,更適合入門。
青木峰郎(作者)
程序員,著有《Ruby程序設計268技(第2版)》《Ruby源代碼完全解說》《Linux程序設計》等多部編程相關著作,并積極參與標準庫維護、文檔維護等各種各樣的活動。 嚴圣逸(譯者) 畢業(yè)于上海交通大學。8年軟件開發(fā)經(jīng)驗,期間赴日本工作,F(xiàn)就職于想能信息科技(上海)有限公司,從事基于云平臺的客戶關系管理及各類營銷自動化系統(tǒng)的開發(fā)工作。譯有《高效團隊開發(fā):工具與方法》。 絕云(譯者) 畢業(yè)于清華大學軟件學院。曾在日本創(chuàng)意公司KAYAC從事即時通信軟件及社交游戲的開發(fā)工作,現(xiàn)任螞蟻金服前端架構專家。譯有《寫給大家看的算法書》等圖書,曾參與《像外行一樣思考,像專家一樣實踐(修訂版)》的審校。
第1章
開始制作編譯器 1 1.1 本書的概要 2 本書的主題 2 本書制作的編譯器 2 編譯示例 2 可執(zhí)行文件 3 編譯 4 程序運行環(huán)境 6 1.2 編譯過程 8 編譯的4個階段 8 語法分析 8 語義分析 9 生成中間代碼 9 代碼生成 10 優(yōu)化 10 總結 10 1.3 使用C?編譯器進行編譯 11 C?編譯器的必要環(huán)境 11 安裝C?編譯器 11 C?的Hello, World! 12 第2章 C?和cbc 13 2.1 C?語言的概要 14 C?的Hello, World! 14 C?中刪減的功能 14 import關鍵字 15 導入文件的規(guī)范 16 2.2 C?編譯器cbc的構成 17 cbc的代碼樹 17 cbc的包 18 compiler包中的類群 18 main函數(shù)的實現(xiàn) 19 commandMain函數(shù)的實現(xiàn) 19 Java5泛型 20 build函數(shù)的實現(xiàn) 20 Java 5的foreach語句 21 compile函數(shù)的實現(xiàn) 21 第1部分 代碼分析 第3章 語法分析的概要 24 3.1 語法分析的方法 25 代碼分析中的問題點 25 代碼分析的一般規(guī)律 25 詞法分析、語法分析、語義分析 25 掃描器的動作 26 單詞的種類和語義值 27 token 28 抽象語法樹和節(jié)點 29 3.2 解析器生成器 30 什么是解析器生成器 30 解析器生成器的種類 30 解析器生成器的選擇 31 3.3 JavaCC的概要 33 什么是JavaCC 33 語法描述文件 33 語法描述文件的例子 34 運行JavaCC 35 啟動JavaCC所生成的解析器 36 中文的處理 37 第4章 詞法分析 39 4.1 基于JavaCC的掃描器的描述 40 本章的目的 40 JavaCC的正則表達式 40 固定字符串 41 連接 41 字符組 41 排除型字符組 41 重復1次或多次 42 重復0次或多次 42 重復n次到m次 42 正好重復n次 43 可以省略 43 選擇 43 4.2 掃描沒有結構的單詞 44 TOKEN命令 44 掃描標識符和保留字 44 選擇匹配規(guī)則 45 掃描數(shù)值 46 4.3 掃描不生成token的單詞 48 SKIP命令和SPECIAL_TOKEN命令 48 跳過空白符 48 跳過行注釋 49 4.4 掃描具有結構的單詞 50 最長匹配原則和它的問題 50 基于狀態(tài)遷移的掃描 50 MORE命令 51 跳過塊注釋 52 掃描字符串字面量 53 掃描字符字面量 53 第5章 基于JavaCC的解析器的描述 55 5.1 基于EBNF語法的描述 56 本章的目的 56 基于JavaCC的語法描述 56 終端符和非終端符 57 JavaCC的EBNF表示法 58 連接 58 重復0次或多次 59 重復1次或多次 59 選擇 60 可以省略 60 5.2 語法的二義性和token的超前掃描 61 語法的二義性 61 JavaCC的局限性 62 提取左側(cè)共通部分 63 token的超前掃描 63 可以省略的規(guī)則和沖突 64 重復和沖突 65 更靈活的超前掃描 66 超前掃描的相關注意事項 66 第6章 語法分析 68 6.1 定義的分析 69 表示程序整體的符號 69 語法的單位 69 import聲明的語法 70 各類定義的語法 71 變量定義的語法 72 函數(shù)定義的語法 73 結構體定義和聯(lián)合體定義的語法 74 結構體成員和聯(lián)合體成員的語法 75 typedef語句的語法 76 類型的語法 76 C語言和C?在變量定義上的區(qū)別 77 基本類型的語法 77 6.2 語句的分析 79 語句的語法 79 if語句的語法 80 省略if語句和大括號 80 while語句的語法 81 for語句的語法 81 各類跳轉(zhuǎn)語句的語法 82 6.3 表達式的分析 83 表達式的整體結構 83 expr的規(guī)則 83 條件表達式 84 二元運算符 85 6.4 項的分析 88 項的規(guī)則 88 前置運算符的規(guī)則 88 后置運算符的規(guī)則 89 字面量的規(guī)則 89 第2部分 抽象語法樹和中間代碼 第7章 JavaCC的action和抽象語法樹 92 7.1 JavaCC的action 93 本章的目的 93 簡單的action 93 執(zhí)行action的時間點 93 返回語義值的action 95 獲取終端符號的語義值 95 Token類的屬性 96 獲取非終端符號的語義值 98 語法樹的結構 99 選擇和action 99 重復和action 100 本節(jié)總結 102 7.2 抽象語法樹和節(jié)點 103 Node類群 103 Node類的定義 105 抽象語法樹的表示 105 基于節(jié)點表示表達式的例子 107 第8章 抽象語法樹的生成 110 8.1 表達式的抽象語法樹 111 字面量的抽象語法樹 111 類型的表示 112 為什么需要TypeRef類 113 一元運算的抽象語法樹 114 二元運算的抽象語法樹 116 條件表達式的抽象語法樹 117 賦值表達式的抽象語法樹 118 8.2 語句的抽象語法樹 121 if語句的抽象語法樹 121 while語句的抽象語法樹 122 程序塊的抽象語法樹 123 8.3 聲明的抽象語法樹 125 變量聲明列表的抽象語法樹 125 函數(shù)定義的抽象語法樹 126 表示聲明列表的抽象語法樹 127 表示程序整體的抽象語法樹 128 外部符號的import 128 總結 129 8.4 cbc 的解析器的啟動 132 Parser對象的生成 132 文件的解析 133 解析器的啟動 134 第9章 語義分析(1)引用的消解 135 9.1 語義分析的概要 136 本章目的 136 抽象語法樹的遍歷 137 不使用Visitor模式的抽象語法樹的處理 137 基于Visitor模式的抽象語法樹的處理 138 Vistor模式的一般化 140 cbc中Visitor模式的實現(xiàn) 141 語義分析相關的cbc的類 142 9.2 變量引用的消解 144 問題概要 144 實現(xiàn)的概要 144 Scope樹的結構 145 LocalResolver類的屬性 146 LocalResolver類的啟動 146 變量定義的添加 147 函數(shù)定義的處理 148 pushScope方法 149 currentScope方法 149 popScope方法 150 添加臨時作用域 150 建立VariableNode和變量定義的關聯(lián) 151 從作用域樹取得變量定義 151 9.3 類型名稱的消解 153 問題概要 153 實現(xiàn)的概要 153 TypeResolver類的屬性 153 TypeResolver類的啟動 154 類型的聲明 154 類型和抽象語法樹的遍歷 155 變量定義的類型消解 156 函數(shù)定義的類型消解 157 第10章 語義分析(2)靜態(tài)類型檢查 159 10.1 類型定義的檢查 160 問題概要 160 實現(xiàn)的概要 161 檢測有向圖中的閉環(huán)的算法 162 結構體、聯(lián)合體的循環(huán)定義檢查 163 10.2 表達式的有效性檢查 165 問題概要 165 實現(xiàn)的概要 165 DereferenceChecker類的啟動 166 SemanticError異常的捕獲 167 非指針類型取值操作的檢查 167 獲取非左值表達式地址的檢查 168 隱式的指針生成 169 10.3 靜態(tài)類型檢查 170 問題概要 170 實現(xiàn)的概要 170 C?中操作數(shù)的類型 171 隱式類型轉(zhuǎn)換 172 TyperChecker類的啟動 173 二元運算符的類型檢查 174 隱式類型轉(zhuǎn)換的實現(xiàn) 175 第11章 中間代碼的轉(zhuǎn)換 178 11.1 cbc的中間代碼 179 組成中間代碼的類 180 中間代碼節(jié)點類的屬性 181 中間代碼的運算符和類型 182 各類中間代碼 183 中間代碼的意義 184 11.2 IRGenerator類的概要 185 抽象語法樹的遍歷和返回值 185 IRGenerator類的啟動 185 函數(shù)本體的轉(zhuǎn)換 186 作為語句的表達式的判別 187 11.3 流程控制語句的轉(zhuǎn)換 189 if語句的轉(zhuǎn)換(1)概要 189 if語句的轉(zhuǎn)換(2)沒有else部分的情況 190 if語句的轉(zhuǎn)換(3)存在else部分的情況 191 while語句的轉(zhuǎn)換 191 break語句的轉(zhuǎn)換(1)問題的定義 192 break語句的轉(zhuǎn)換(2)實現(xiàn)的方針 193 break語句的轉(zhuǎn)換(3)實現(xiàn) 194 11.4 沒有副作用的表達式的轉(zhuǎn)換 196 UnaryOpNode對象的轉(zhuǎn)換 196 BinaryOpNode對象的轉(zhuǎn)換 197 指針加減運算的轉(zhuǎn)換 198 11.5 左值的轉(zhuǎn)換 200 左邊和右邊 200 左值和右值 200 cbc中左值的表現(xiàn) 201 結構體成員的偏移 202 成員引用(expr.memb)的轉(zhuǎn)換 203 左值轉(zhuǎn)換的例外:數(shù)組和函數(shù) 204 成員引用的表達式(ptr->memb)的轉(zhuǎn)換 205 11.6 存在副作用的表達式的轉(zhuǎn)換 206 表達式的副作用 206 有副作用的表達式的轉(zhuǎn)換方針 206 簡單賦值表達式的轉(zhuǎn)換(1)語句 207 臨時變量的引入 208 簡單賦值表達式的轉(zhuǎn)換(2)表達式 209 后置自增的轉(zhuǎn)換 210 第3部分 匯編代碼 第12章 x86架構的概要 214 12.1 計算機的系統(tǒng)結構 215 CPU和存儲器 215 寄存器 215 地址 216 物理地址和虛擬地址 216 各類設備 217 緩存 218 12.2 x86系列CPU的歷史 220 x86系列CPU 220 32位CPU 220 指令集 221 IA-32的變遷 222 IA-32的64位擴展——AMD64 222 12.3 IA-32的概要 224 IA-32的寄存器 224 通用寄存器 225 機器!226 機器棧的操作 227 機器棧的用途 227 棧幀 228 指令指針 229 標志寄存器 229 12.4 數(shù)據(jù)的表現(xiàn)形式和格式 231 無符號整數(shù)的表現(xiàn)形式 231 有符號整數(shù)的表現(xiàn)形式 231 負整數(shù)的表現(xiàn)形式和二進制補碼 232 字節(jié)序 233 對齊 233 結構體的表現(xiàn)形式 234 第13章 x86匯編器編程 236 13.1 基于GNU匯編器的編程 237 GNU匯編器 237 匯編語言的Hello, World! 237 基于GNU匯編器的匯編代碼 238 13.2 GNU匯編器的語法 240 匯編版的Hello, World! 240 指令 241 匯編偽操作 241 標簽 241 注釋 242 助記符后綴 242 各種各樣的操作數(shù) 243 間接內(nèi)存引用 244 x86指令集的概要 245 13.3 傳輸指令 246 mov指令 246 push指令和pop指令 247 lea指令 248 movsx指令和movzx指令 249 符號擴展和零擴展 250 13.4 算術運算指令 251 add指令 251 進位標志 252 sub指令 252 imul指令 252 idiv指令和div指令 253 inc指令 254 dec指令 255 neg指令 255 13.5 位運算指令 256 and指令 256 or指令 257 xor指令 257 not指令 257 sal指令 258 sar指令 258 shr指令 259 13.6 流程的控制 260 jmp指令 260 條件跳轉(zhuǎn)指令(jz、jnz、je、jne、……) 261 cmp指令 262 test指令 263 標志位獲取指令(SETcc) 263 call指令 264 ret指令 265 第14章 函數(shù)和變量 266 14.1 程序調(diào)用約定 267 什么是程序調(diào)用約定 267 Linux/x86下的程序調(diào)用約定 267 14.2 Linux/x86下的函數(shù)調(diào)用 269 到函數(shù)調(diào)用完成為止 269 到函數(shù)開始執(zhí)行為止 270 到返回原處理流程為止 271 到清理操作完成為止 271 函數(shù)調(diào)用總結 272 14.3 Linux/x86下函數(shù)調(diào)用的細節(jié) 274 寄存器的保存和復原 274 caller-save寄存器和callee-save寄存器 274 caller-save寄存器和callee-save寄存器的靈活應用 275 大數(shù)值和浮點數(shù)的返回方法 276 其他平臺的程序調(diào)用約定 277 第15章 編譯表達式和語句 278 15.1 確認編譯結果 279 利用cbc進行確認的方法 279 利用gcc進行確認的方法 280 15.2 x86匯編的對象與DSL 282 表示匯編的類 282 表示匯編對象 283 15.3 cbc的x86匯編DSL 285 利用DSL生成匯編對象 285 表示寄存器 286 表示立即數(shù)和內(nèi)存引用 287 表示指令 287 表示匯編偽操作、標簽和注釋 288 15.4 CodeGenerator類的概要 290 CodeGenerator類的字段 290 CodeGenerator類的處理概述 290 實現(xiàn)compileStmts方法 291 cbc的編譯策略 292 15.5 編譯單純的表達式 294 編譯Int節(jié)點 294 編譯Str節(jié)點 294 編譯Uni節(jié)點(1)按位取反 295 編譯Uni節(jié)點(2)邏輯非 297 15.6 編譯二元運算 298 編譯Bin節(jié)點 298 實現(xiàn)compileBinaryOp方法 299 實現(xiàn)除法和余數(shù) 300 實現(xiàn)比較運算 300 15.7 引用變量和賦值 301 編譯Var節(jié)點 301 編譯Addr節(jié)點 302 編譯Mem節(jié)點 303 編譯Assign節(jié)點 303 15.8 編譯jump語句 305 編譯LabelStmt節(jié)點 305 編譯Jump節(jié)點 305 編譯CJump節(jié)點 305 編譯Call節(jié)點 306 編譯Return節(jié)點 307 第16章 分配棧幀 308 16.1 操作!309 cbc中的棧幀 309 棧指針操作原則 310 函數(shù)體編譯順序 310 16.2 參數(shù)和局部變量的內(nèi)存分配 312 本節(jié)概述 312 參數(shù)的內(nèi)存分配 312 局部變量的內(nèi)存分配:原則 313 局部變量的內(nèi)存分配 314 處理作用域內(nèi)的局部變量 315 對齊的計算 316 子作用域變量的內(nèi)存分配 316 16.3 利用虛擬棧分配臨時變量 318 虛擬棧的作用 318 虛擬棧的接口 319 虛擬棧的結構 319 virtualPush方法的實現(xiàn) 320 VirtualStack#extend方法的實現(xiàn) 320 VirtualStack#top方法的實現(xiàn) 321 virtualPop方法的實現(xiàn) 321 VirtualStack#rewind方法的實現(xiàn) 321 虛擬棧的運作 322 16.4 調(diào)整棧訪問的偏移量 323 本節(jié)概要 323 StackFrameInfo類 323 計算正在使用的callee-save寄存器 324 計算臨時變量區(qū)域的大小 325 調(diào)整局部變量的偏移量 325 調(diào)整臨時變量的偏移量 326 16.5 生成函數(shù)序言和尾聲 327 本節(jié)概要 327 生成函數(shù)序言 327 生成函數(shù)尾聲 328 16.6 alloca函數(shù)的實現(xiàn) 330 什么是alloca函數(shù) 330 實現(xiàn)原則 330 alloca函數(shù)的影響 331 alloca函數(shù)的實現(xiàn) 331 第17章 優(yōu)化的方法 333 17.1 什么是優(yōu)化 334 各種各樣的優(yōu)化 334 優(yōu)化的案例 334 常量折疊 334 代數(shù)簡化 335 降低運算強度 335 削除共同子表達式 335 消除無效語句 336 函數(shù)內(nèi)聯(lián) 336 17.2 優(yōu)化的分類 337 基于方法的優(yōu)化分類 337 基于作用范圍的優(yōu)化分類 337 基于作用階段的優(yōu)化分類 338 17.3 cbc中的優(yōu)化 339 cbc中的優(yōu)化原則 339 cbc中實現(xiàn)的優(yōu)化 339 cbc中優(yōu)化的實現(xiàn) 339 17.4 更深層的優(yōu)化 341 基于模式匹配選擇指令 341 分配寄存器 342 控制流分析 342 大規(guī)模的數(shù)據(jù)流分析和SSA形式 342 總結 343 第4部分 鏈接和加載 第18章 生成目標文件 346 18.1 ELF文件的結構 347 ELF的目的 347 ELF的節(jié)和段 348 目標文件的主要ELF節(jié) 348 使用readelf命令輸出節(jié)頭 349 使用readelf命令輸出程序頭 350 使用readelf命令輸出符號表 351 readelf命令的選項 351 什么是DWARF格式 352 18.2 全局變量及其在ELF文件中的表示 354 分配給任意ELF節(jié) 354 分配給通用ELF節(jié) 354 分配.bss節(jié) 355 通用符號 355 記錄全局變量對應的符號 357 記錄符號的附加信息 357 記錄通用符號的附加信息 358 總結 358 18.3 編譯全局變量 360 generate方法的實現(xiàn) 360 generateAssemblyCode方法的實現(xiàn) 360 編譯全局變量 361 編譯立即數(shù) 362 編譯通用符號 363 編譯字符串字面量 364 生成函數(shù)頭 365 計算函數(shù)的代碼大小 366 總結 366 18.4 生成目標文件 367 as命令調(diào)用的概要 367 引用GNUAssembler類 367 調(diào)用as命令 367 第19章 鏈接和庫 369 19.1 鏈接的概要 370 鏈接的執(zhí)行示例 370 gcc和GNU ld 371 鏈接器處理的文件 372 常用庫 374 鏈接器的輸入和輸出 374 19.2 什么是鏈接 375 鏈接時進行的處理 375 合并節(jié) 375 重定位 376 符號消解 377 19.3 動態(tài)鏈接和靜態(tài)鏈接 379 兩種鏈接方法 379 動態(tài)鏈接的優(yōu)點 379 動態(tài)鏈接的缺點 380 動態(tài)鏈接示例 380 靜態(tài)鏈接示例 381 庫的檢索規(guī)則 381 19.4 生成庫 383 生成靜態(tài)庫 383 Linux中共享庫的管理 383 生成共享庫 384 鏈接生成的共享庫 385 第20章 加載程序 387 20.1 加載ELF段 388 利用mmap系統(tǒng)調(diào)用進行文件映射 388 進程的內(nèi)存鏡像 389 內(nèi)存空間的屬性 390 ELF段對應的內(nèi)存空間 390 和ELF文件不對應的內(nèi)存空間 392 ELF文件加載的實現(xiàn) 393 20.2 動態(tài)鏈接過程 395 動態(tài)鏈接加載器 395 程序從啟動到終止的過程 395 啟動ld.so 396 系統(tǒng)內(nèi)核傳遞的信息 397 AUX矢量 397 讀入共享庫 398 符號消解和重定位 399 運行初始化代碼 400 執(zhí)行主程序 401 執(zhí)行終止處理 402 ld.so解析的環(huán)境變量 402 20.3 動態(tài)加載 404 所謂動態(tài)加載 404 Linux下的動態(tài)加載 404 動態(tài)加載的架構 405 20.4 GNU ld的鏈接 406 用于cbc的ld選項的結構 406 C運行時 407 生成可執(zhí)行文件 408 生成共享庫 408 第21章 生成地址無關代碼 410 21.1 地址無關代碼 411 什么是地址無關代碼 411 全局偏移表(GOT) 412 獲取GOT地址 412 使用GOT地址訪問全局變量 413 訪問使用GOT地址的文件內(nèi)部的全局變量 414 過程鏈接表(PLT) 414 調(diào)用PLT入口 416 地址無關的可執(zhí)行文件:PIE 416 21.2 全局變量引用的實現(xiàn) 418 獲取GOT地址 418 PICThunk函數(shù)的實現(xiàn) 418 刪除重復函數(shù)并設置不可見屬性 419 加載GOT地址 420 locateSymbols函數(shù)的實現(xiàn) 421 全局變量的引用 421 訪問全局變量:地址無關代碼的情況下 422 函數(shù)的符號 423 字符串常量的引用 424 21.3 鏈接器調(diào)用的實現(xiàn) 425 生成可執(zhí)行文件 425 generateSharedLibrary方法 426 21.4 從程序解析到執(zhí)行 428 build和加載的過程 428 詞法分析 429 語法分析 429 生成中間代碼 430 生成代碼 431 匯編 432 生成共享庫 432 生成可執(zhí)行文件 433 加載 433 第22章 擴展閱讀 434 22.1 參考書推薦 435 編譯器相關 435 語法分析相關 435 匯編語言相關 436 22.2 鏈接、加載相關 437 22.3 各種編程語言的功能 438 異常封裝相關的圖書 438 垃圾回收 438 垃圾回收相關的圖書 439 面向?qū)ο缶幊陶Z言的實現(xiàn) 439 函數(shù)式語言 440 附 錄 441 A.1 參考文獻 442 A.2 在線資料 444 A.3 源代碼 445
你還可能感興趣
我要評論
|