編譯器2023詳細攻略!(持續更新)

Posted by Tommy on January 3, 2019

編譯器

這是兩種新的獨立於語言的語法樹形式,隨GCC 4.0引入編譯器前端。 GENERIC更複雜,是一種基於 GCC 3.x Java 前端的中介表示。 GIMPLE 是一個簡化的 GENERIC,其中各種結構被簡化為多個 GIMPLE 指令。 C、C++ 和 Java 前端直接在前端生成 GENERIC。 相反,其他前端在解析後會有不同的中介表示,這些中介表示將轉換為 GENERIC。 前端生成GENERIC之後再使用「gimplifier」技術簡化GENERIC的複雜結構,成為一較簡單的以SSA為基礎的GIMPLE形式,一種強大的,獨立於語言和體系結構的全域(函數範圍)最佳化的通用語言。

从20世纪70年代起,实现能编译自己源程序的编译器变得越来越可行,不过还是用Pascal和C语言来实现编译器更加流行。 制作某种语言的第一个能编译器,要么需要用其它语言来编写,要么就像Hart和Levin制作Lisp编译器那样,用解释器来运行编译器。 早期的计算机软件都是用汇编语言直接编写的,这种状况持续了数年。 当人们发现为不同类型的中央处理器(CPU)编写可重用软件的开销要明显高于编写编译器时,人们发明了高级编程语言。 由于早期的计算机的内存很少,当大家实现编译器时,遇到了许多技术难题。

編譯器: 支援的處理器架構

GCC不僅是GNU作業系統的官方編譯器,還是許多類UNIX系統和Linux發行版的標準編譯器。 BSD家族中的大部分作業系統也在GCC釋出之後轉用GCC;不過FreeBSD、OpenBSD和Apple macOS已經轉向了Clang編譯器[5],主要是因為授權問題。 [6][7][8]GCC也可以編譯Windows、Android、iOS、Solaris、HP-UX、IBM AIX和DOS系統的代碼。 GCC原本用C開發,後來因為LLVM、Clang的崛起,它更快地將開發語言轉換為C++。 許多C的愛好者在對C++一知半解的情況下主觀認定C++的效能一定會輸給C,但是Ian Lance Taylor給出了不同的意見,並表明C++不但效能不輸給C,而且能設計出更好,更容易維護的程式[9][10]。

有一些編譯器輸出的代碼,將執行於與編譯器所在相同類型的電腦和作業系統之上,這種編譯器叫做本地編譯器。 由於嵌入式系統通常沒有軟體開發環境,因此,為這類系統開發軟體時,通常需要使用交叉編譯器。 編譯器2023 早期的電腦軟體都是用組合語言直接編寫的,這種狀況持續了數年。 當人們發現為不同類型的中央處理器(CPU)編寫可重用軟體的開銷要明顯高於編寫編譯器時,人們發明了高階程式語言。

編譯器: 簡單直覺

這裡需要註意的是所謂平臺,實際上包含兩個概念:體繫結構和操作系統。 同一個體繫結構可以運行不同的操作系統;同樣,同一個操作系統也可以在不同的體繫結構上運行。 舉例來說,常說的X86Linux平臺實際上是IntelX86體繫結構和LinuxforX86操作系統的統稱;而X86WinNT平臺實際上是IntelX86體繫結構和WindowsNTforX86操作系統的簡稱。 它主要的目的是將便於人編寫,閱讀,維護的高級電腦語言所寫作的源代碼程式,翻譯為電腦能解讀、運行的低階機器語言的程式,也就是可執行文件。 編譯器將原始程式(Source program)作為輸入,翻譯產生使用目標語言(Target language)的等價程式。

它主要的目的是將便於人編寫、閱讀、維護的進階電腦語言所寫作的原始碼程式,翻譯為電腦能解讀、執行的低階機器語言的程式,也就是執行檔。 編譯器將原始程式(source program)作為輸入,翻譯產生使用目標語言(target language)的等價程式。 原始碼一般為高階語言(High-level language),如Pascal、C、C++、C# 、Java等,而目標語言則是組合語言或目標機器的目的碼(Object code),有時也稱作機器碼(Machine code)。

編譯器: 如何從 AVR 當中探索計算機的本質系列 第

在轉譯的過程中,這組高階語言所寫成的程式仍然維持在原始碼的格式(或某種中繼語言的格式),而程式本身所指涉的動作或行為則由直譯器來表現。 其他特殊用途的除錯工具是Valgrind,用以發現記憶體流失(memory leak)。 而GNU測量器(gprof)可以得知程式中某些函數花費多少時間,以及其呼叫頻率;此功能需要使用者在編譯時選定測量(profiling)選項。

編譯器

以 ijump 與 編譯器2023 icall 都是直接以記憶體位址改動 PC 值,也可以透過 offset 的方式來改變。 由于新的编程语言支持的功能越来越多,计算机的架构越来越复杂,这使得编译器也越来越复杂。 AVR 的指令集雖然不多,但算下來也有 100 多條,文章裡無法一一介紹,不過我會在接下來介紹到特定功能時,一併介紹對應的指令。

編譯器: 電腦不認識字、只認得數字0、1,所以電腦讀的語言叫做機器語言,也叫低階語言。下面這張圖一組一組的就是指令:

在 AVR 當中暫存器可以透過特定的位址存取,舉例來說 PORTB 這個暫存器就存在於 0x18 這個記憶體位址當中。 不過直接撰寫記憶體位址撰寫時比較麻煩,因此 assembler 通常會事先定義好這些暫存器的位址與名稱,在撰寫時就不需要查表寫記憶體位址了。 由於編譯器會產生一些 bootstrap 程式碼,所以光看編譯器編譯後的程式碼,可能會比想像中的數量還多。 為了深入底層,你可能還需要知道編譯器對於特定片段的程式碼是如何編譯的。 直譯式程式相較於編譯式程式有較佳的可攜性,可以容易的在不同軟硬體平台上執行。 MacOS和Windows也都有內建終端機模式,只是Windows XP是最後一代會讓使用者(從「附屬應用程式」內)快速開啟終端機模式的作業系統,之後的Windows就沒那麼簡單。

編譯器(compiler)是一種電腦程式,它會將某種程式語言寫成的原始碼(原始語言)轉換成另一種程式語言(目標語言)。 為了改善編譯語言的效率而發展出的即時編譯技術,已經縮小了這兩種語言間的差距。 這種技術混合了編譯語言與直譯語言的優點,它像編譯語言一樣,先把程式原始碼編譯成位元組碼。 PC 可透過 ijump、icall 修改,讓程式跳到指定的位置後執行,進而實現像是迴圈、條件式判斷、函數呼叫等功能。 AVR 採用的是哈佛架構(Harvard Architecture),在哈佛架構當中,程式指令和資料儲存會分別存放在不同的記憶體空間。 目前使用哈佛架構的微控制器與中央處理器的晶片有 AVR、ARM9、ARM10、ARM11。

編譯器: 簡單的來說編譯就是”翻譯一下程式碼”,為什麼程式碼要翻譯呢?

一組目標文件,不必是同一編譯器產生,但使用的編譯器必須採用同樣的輸出格式,可以鏈接在一起並生成可以由用戶直接執行的可執行程式。 首個能編譯自己源程式的編譯器是在1962年由麻省理工學院的Hart和Levin製作的。 從20世紀70年代起,實現能編譯自己源程式的編譯器變得越來越可行,不過還是用Pascal和C語言來實現編譯器更加流行。 製作某種語言的第一個能編譯器,要麼需要用其它語言來編寫,要麼就像Hart和Levin製作Lisp編譯器那樣,用直譯器來執行編譯器。 編譯語言(英語:Compiled language)是一種程式語言類型,通過編譯器來實作。

編譯器

從這個指令多少就能感受出指標在組合語言的表達是什麼,其實就是 indirect 的對應。 即時編譯(Just-in-time compilation)是指一種在執行時期把位元組碼編譯成原生機器碼的技術;這項技術是被用來改善虛擬機器的效能的。 該技術在近幾年來才開始獲得重視,而它後來模糊了直譯、位元組碼直譯及編譯的差異性。 大約在1980年代Smalltalk語言出現的時候JIT的技術就存在了。 在編譯結束時,有效RTL會被簡化為嚴格的形式,其中每條指令都指向真實的機器暫存器、和目標機器描述檔案中的一種模式。 嚴格化RTL是個相當複雜的工作:首先是暫存器分配,選擇真實的硬體暫存器來取代最初分配的偽暫存器;還有多載,未分配實際硬體暫存器的偽暫存器都會被溢位到堆疊中,並生成執行此溢位的 RTL。

編譯器: 直譯器

這個"編譯過的"碼之後會被位元組碼直譯器(使用C寫成的)轉譯。 在這種情況下,這個"編譯過的"碼可以被說成是虛擬機器(不是真的硬體,而是一種位元組碼直譯器)的機器碼。 這個方式被用在Open Firmware系統所使用的Forth程式碼中:原始程式將會被編譯成"F code"(一種位元組碼),然後被一個特定平台的虛擬機器直譯和執行。

  • GENERIC 是一種中間表示語言,在將原始碼編譯成可執行二進位檔案時用作「中介端」。
  • 常態來說,一萬個人類裡頭,有九千九百九十九個人無法用電腦的0/1、指令碼、記憶體位址等方式去做事情,--至少沒辦法做得太複雜,處理「1+1+1」大概就是大多數人的極限了。
  • 在這邊要特別注意的是,並不是每個指令都能夠像這樣,後面直接接一個常數,而是要先將數字載入到暫存器之後,再繼續接下來的操作。
  • 由于早期的计算机的内存很少,当大家实现编译器时,遇到了许多技术难题。
  • 隨著 GENERIC 和 GIMPLE 的引入,這種情況得以避免。
  • 交叉編譯這個概念的出現和流行是和嵌入式系統的廣泛發展同步的。

我們在前面有講到,AVR 具有 32 個通用暫存器,分別從 r0 ~ r31,其中 r26 ~ r31 在特定指令中會具有定址功能,而當這些暫存器當作定址功能使用時就稱為 X、Y、Z 暫存器。 因此我們將資料儲存的地方稱為 data space,儲存指令的地方稱為 program memory space。 將程式指令與資料儲存空間分開存放最大的好處在於,當我們在執行指令時,就可以預先讀取下一條指令,進而提高效能。 但編譯器本身很單純,它只負責讀取跟分析程式碼,最終要產生什麼樣的程式,必須要去相對應的「圖書館library」裡查詢。

編譯器: 編譯器最佳化

但下一款語言通常不會更好,因為「一點虛無、兩點悲觀」,設計一款軟體有時候跟寫篇教學文或心得書,最後的命運很雷同。 多謝這年頭氾濫又充滿錯誤與偏見的科普、或敷衍了事毫無啟迪人心功能的義務教育,「我們(還沒學習寫程式)」都知道電腦是用0/1二進位做運算。 编译器所输出于虚拟机上运行之代码,编译器和编译器输出的运行平台有可能相同,也有可能不同。 因此,对于这类编译器,不去区分它是本地编译器还是交叉编译器。 首个能编译自己源程序的编译器是在1962年由麻省理工学院的Hart和Levin制作的。

常態來說,一萬個人類裡頭,有九千九百九十九個人無法用電腦的0/1、指令碼、記憶體位址等方式去做事情,--至少沒辦法做得太複雜,處理「1+1+1」大概就是大多數人的極限了。 一個八位元數字最大可以表示到256,表示這可以有256組記憶體、可以儲存256組數字,但每個數字都需要8位元,所以每組記憶體其實是8位元,也就是「256*8」。 在這邊可以發現 編譯器 PINB 以及 PORTC 之類看起來很像變數的東西,這是由 assembler 事先定義的變數。 在 AVR 當中除了通用暫存器(general purpose register)這類可以讓我們直接操作的暫存器之外,還有控制各種功能、參數的暫存器可以使用。 在這邊要特別注意的是,並不是每個指令都能夠像這樣,後面直接接一個常數,而是要先將數字載入到暫存器之後,再繼續接下來的操作。 對於組合語言,最大的迷思在於:「有了編譯器,為什麼我還要學組合語言?」,關於學習組合語言的好處我認為可以分成幾點討論。

編譯器: 編譯語言列表

在交叉編譯技術中有兩種比較典型的實現,一個稱之為Java模式,即Java的位元組碼編譯技術;另一個稱之為GNUGCC模式,即通常所講的CrossGCC技術。 編譯器2023 Java模式(如圖所示)的最大特點是引入了一個自定義的虛擬機,即Java虛擬機JVM(Java Virtual Machine)。 所有Java源程式都會首先被編譯成只在這個虛擬機上才能執行的“目標代碼”:位元組碼(Bytecode)。 由於第一次是非實時編譯,Java編譯器生成的是基於JVM的“目標代碼”,可以將它的編譯技術也稱為交叉編譯。 考量程式執行之前所需要分析的時間,存在了一個介於直譯與編譯之間的可能性。 例如,用Emacs Lisp所撰寫的原始碼會被編譯成一種高度壓縮且最佳化的另一種Lisp原始碼格式,這就是一種位元組碼(bytecode),而它並不是機器碼(因此不會被綁死在特定的硬體上)。

編譯器

GCC 專案在CPLv3的授權下實現了C++標準庫(libstdc++)。 電腦沒有辦法「閱讀」任何人類閱讀的文字,不管是二進位或N進位,當你用「+」或「加」這樣的概念描述自己希望電腦做的事情時,電腦只會呆在那邊為溫室效應做出貢獻而已。

編譯器: 代碼分析

它不像直譯語言一樣,由直譯器將程式碼一句一句執行,而是以編譯器,先將程式碼編譯為機器碼,再加以執行。 GCC編譯器已經被移植到比其他編譯器更多的平台和指令集架構上,並被廣泛部署在開發自由和專有軟體的工具中。 GCC還可用於許多嵌入式系統,包括基於ARM和Power ISA(英語:Power ISA)的晶片。 編譯器是指從高級語言到低級語言的翻譯器,同樣的技術可用於不同種類語言之間的翻譯。 編譯器是一種電腦程式,它會將用某種編程語言寫成的源代碼(原始語言),轉換成另一種編程語言(目標語言)。 由於新的程式語言支援的功能越來越多,電腦的架構越來越複雜,這使得編譯器也越來越複雜。

編譯器

由於早期的電腦的記憶體很少,當大家實現編譯器時,遇到了許多技術難題。 編譯器 它主要的目的是將便於人編寫、閱讀、維護的高級計算機語言所寫作的原始碼程式,翻譯為計算機能解讀、運行的低階機器語言的程序,也就是執行檔。 編譯器將原始程序(source program)作為輸入,翻譯產生使用目標語言(target language)的等價程序。

編譯器: 編譯器的分類

最後運行的時候通過通用語言運行庫的轉換,編程最終可以被CPU直接計算的機器碼(NativeCode)。 編譯器所輸出於虛擬機上運行之代碼,編譯器和編譯器輸出的運行平台有可能相同,也有可能不同。 首個能編譯自己源程序的編譯器是在1962年由麻省理工學院的Hart和Levin製作的。 從20世紀70年代起,實現能編譯自己源程序的編譯器變得越來越可行,不過還是用Pascal和C語言來實現編譯器更加流行。 製作某種語言的第一個能編譯器,要麼需要用其它語言來編寫,要麼就像Hart和Levin製作Lisp編譯器那樣,用解釋器來運行編譯器。

編譯器

它主要的目的是將便于人编写、阅读、维护的高级计算机语言所寫作的原始碼程式,翻译为计算机能解读、运行的低阶机器语言的程序,也就是執行檔。 编译器将原始程序(source program)作为输入,翻译产生使用目标语言(target language)的等价程序。 ②交叉編譯器:編譯器也可以生成用來在其他平臺上運行的目標代碼,交叉編譯器在生成新的硬體平臺時非常有用。

使用直譯器來執行程式會比直接執行編譯過的機器碼來得慢,但是相對的這個直譯的行為會比編譯再執行來得快。 這在程式開發的雛型化階段和只是撰寫試驗性的程式碼時尤其來得重要,因為這個「編輯-直譯-除錯」的循環通常比「編輯-編譯-執行-除錯」的循環來得省時許多。 GCC的後端部分是由預處理器宏和目標架構特有的函數指定的,例如定義其位元組序、字大小和呼叫約定。 後端的前半部分使用這些來決定RTL的生成形式,因此雖然GCC的RTL理論上不受處理器影響,但在此階段其抽象指令已被轉換成目標架構的格式。 在任何時候,構成程式的實際RTL指令都必須符合目標架構的機器描述標準。

編譯器: 編譯器比較快還是直譯器比較快?

由於嵌入式系統通常沒有軟件開發環境,因此,為這類系統開發軟件時,通常需要使用交叉編譯器。 GCC模式(如圖所示)通過CrossGCC直接生成目標平臺的目標代碼,從而能夠直接在目標平臺上運行。 需要根據目標平臺的不同,選擇針對這個平臺的CrossGCC。

在 GCC 4.0 之前,程式的語法樹結構不完全獨立於輸出的目標處理器架構。 對於不同語言的前端來說,語法樹的含義可能不同;而且前端可以提供它們特別的語法樹規則。 隨著 GENERIC 和 GIMPLE 的引入,這種情況得以避免。



Related Posts