① 如何讓編譯器按代碼順序執行(((急急急)))
問題是你do的值就是c.me提供的,想要優化那就要考慮把do(c.me())這整個邏輯進行優化
② 編譯器生成的匯編語句執行順序為什麼與C代碼順序不同
不影響語義的前提下編譯器可以任意重排代碼順序;
在亂序執行(Out-of-Order)的CPU里,機器碼的執行也可以不按照你在「匯編」層面上看到的順序執行,只要不影響語義。
所以說這些中間步驟的順序,作為底層細節平時不需要那麼在意——它們多半跟原始源碼的順序是不一樣的。
現代優化編譯器優化的思路之一是「基於依賴的優化」(dependence-based optimization)。題主引用的CSAPP的例子:
int arith(int x, int y, int z) {
int t1 = x + y;
int t2 = z * 48;
int t3 = t1 & 0xFFFF;
int t4 = t2 * t3;
return t4;
}
所有涉及運算的值都是局部標量變數(local scalar variable),這是最便於編譯器做分析的情況,所有依賴都可以顯式分析。
由於整個函數沒有分支,這里也不需要討論控制依賴(control dependence),只要討論數據依賴(data dependence)就好。
把數據依賴圖畫出來是個DAG(這里正好是棵樹,特例了):
x y z 48
\ / \ /
t1 0xFFFF t2
\ / /
t3 /
\ /
t4
優化必須要滿足的約束是:每個節點求值之前,其子節點(依賴的數據源)必須要先求了值。
顯然,t1和t2之間沒有依賴關系,它們的相對求值順序怎樣重排都沒關系。
有本我很喜歡的書,裡面講的是各種基於依賴的優化:Optimizing Compilers for Modern Architectures - A Dependence-based Approach
以上是理論部分。
================================================================
下面來看例子。
我們可以用一個實際編譯器來看看CSAPP的例子編譯出來的結果:
.text
# -- Begin arith
.p2align 4,,15
.globl arith
.type arith, @function
arith:
.p2align 4,,7
/*.L0:*/ /* Block BB[54:2] preds: none, freq: 1.000 */
movl 8(%esp), %edx /* ia32_Load T[139:10] -:1:22 */
addl 4(%esp), %edx /* ia32_Add Iu[141:12] -:2:14 */
movzwl %dx, %edx /* ia32_Conv_I2I Iu[142:13] -:4:15 */
imull 12(%esp), %edx /* ia32_IMul Iu[143:14] -:5:15 */
leal (%edx,%edx,2), %eax /* ia32_Lea Iu[144:15] -:5:15 */
shll $0x4, %eax /* ia32_Shl Iu[146:17] -:5:15 */
ret /* ia32_Return X[152:23] -:6:3 */
.size arith, .-arith
# -- End arith
這里用的是libFirm。可見它跟CSAPP書里所說的匯編的順序又有所不同。這也是完全合理的。
這個編譯結果的順序是:
edx = y;
edx += x;
edx = zeroextend dx; // edx = edx & 0xFFFF
edx *= z;
eax = edx * 3;
eax <<= 4; // eax = eax * 16
也是完全符合依賴關系的約束的一種順序。
之所以用libFirm舉例是因為它的中間表示(Intermediate Representation)是一種程序依賴圖(Program Dependence Graph),可以很方便的看出控制與數據依賴。把CSAPP那裡例子對應的libFirm IR畫出來,是這個樣子的:
(這張圖跟我前面畫的數據依賴圖正好是左右翻轉的,不過意思一樣。(這張圖跟我前面畫的數據依賴圖正好是左右翻轉的,不過意思一樣。
Arg 0、1、2分別代表x、y、z。白色方塊是普通數據節點,黃色方塊是常量節點,藍色方塊是內存相關節點,紅色方塊是控制流節點,粉紅色方塊是特殊的開始/結束節點。)
某版LLVM生成的代碼:
; MoleID = '/tmp/webcompile/_16355_0.bc'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-ellcc-linux"
; Function Attrs: nounwind readnone
define i32 @arith(i32 %x, i32 %y, i32 %z) #0 {
entry:
%add = add nsw i32 %y, %x
%mul = mul nsw i32 %z, 48
%and = and i32 %add, 65535
%mul1 = mul nsw i32 %mul, %and
ret i32 %mul1
}
attributes #0 = { nounwind readnone "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.ident = !{!0}
!0 = !{!"ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"}
最終生成的x86匯編:
.text
.file "/tmp/webcompile/_15964_0.c"
.globl arith
.align 16, 0x90
.type arith,@function
arith: # @arith
# BB#0: # %entry
movl 8(%esp), %eax
addl 4(%esp), %eax
movzwl %ax, %eax
imull 12(%esp), %eax
shll $4, %eax
leal (%eax,%eax,2), %eax
retl
.Ltmp0:
.size arith, .Ltmp0-arith
.ident "ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"
.section ".note.GNU-stack","",@progbits
GCC 4.9.2 x86-64:
arith(int, int, int):
leal (%rdx,%rdx,2), %eax
addl %edi, %esi
movzwl %si, %esi
sall $4, %eax
imull %esi, %eax
ret
Zing VM Server Compiler x86-64:
# edi: x
# esi: y
# edx: z
movl %edx, %eax
shll $0x4, %eax
leal (%rsi, %rdi, 1), %ecx
shll $0x5, %edx
addl %edx, $eax
movzwl %ecx, %edx
imull %edx, %eax
③ 編譯器編譯代碼時,他的順序是怎樣的
編譯是一個很復雜的東西 不是一兩句能說清楚的
你可以搜一下編譯原理,這個可以說是一門學科了
大多數C編譯器遵循的一個流程
預編譯(宏取代 條件編譯處理等)
詞法分析 根據關鍵字獲取符號
語法分析 分析程序結構 大多數的編譯錯誤都是這一步拋出來的
中間代碼轉換
轉為二級制碼
④ 關於解碼器器的描述正確的是
盡可能通俗講
假設我們有一篇英文說明書。
編譯器負責把每一頁或節或章翻譯成等價的中文。
鏈接器負責把翻譯好的章節整理成完整說明書。
由於語言問題,譯本和原本之間的頁數可能不一樣,導致原本中位於第二頁的某圖在譯本里可能在第五頁,這些引用的修改由鏈接器完成。又譯本中可能還要加上中文的版本歷史,序以及譯者的話等,甚至出於一些考慮還要改變原本的章節順序。
上述所有這些活都是鏈接器的基本工作。
⑤ 如何arm編譯器編譯時變數順序排列
可以 話說,目前業內一般都是用keil編譯器的,它支持的晶元種類還更多
⑥ c++編譯器以何種順序編譯文件的,先cpp文件,還是.h文件
1. 編譯階段 (頭文件 .h)
d工程中在頭文件中對導出內容(function, class, type, object, variable)進行定義.
2. 鏈接階段 (庫文件 .lib)
d工程在link階段會生成.lib
用戶link時需要 這個.lib 解決link時的代碼定位.
3. 運行階段
.exe
⑦ 編譯器生成的匯編語句執行順序為什麼與C代碼順序不同
編譯器不僅是無序的,而且還展開、合並、刪除代碼(如果沒有發現代碼),,,C語言是好的,和C++編譯器編譯,經常連上帝也不知道結果。
暫停現代CPU為了彌補CPU和內存之間的速度差距的接入方案,以所謂的緩存(L1,L2,L3;在CPU晶元的速度比內存快得多,但容量很小),CPU緩存中常見的事情似乎保持鏡子,需要在緩存中直接讀取使用的數據是好的,比內存快幾倍的速度。
注意名字的區別。名稱風險是兩個沒有數據流的指令,數據冒險是兩個指令之間的數據流。
⑧ 編譯器編譯代碼時, 他的順序是怎樣的
先定義全局變數,再按照從左至右,從上至下的順序將源代碼(也就是你寫的代碼)編譯成機器能識別的機器碼,最後再執行編譯好的機器碼.
⑨ c/c++中,編譯器編譯文件的順序和規則
#include <filename>從磁碟安裝根目錄查找文件;#include "filename"從編譯文件的當前目錄查找文件。
⑩ linux編譯器的解析順序與windows編譯器的區別
沒區別,都是編譯成obj lib等目標文件和庫文件,然後鏈接為可執行的二進制代碼,Win平台多了一個動態鏈接庫。