keil優化等級設置


Compiler optimization levels and the debug view

4.3 Compiler optimization levels and the debug view

The precise optimizations performed by the compiler depend both on the level of optimization chosen, and whether you are optimizing for performance or code size.

The compiler supports the following optimization levels:
 
0
Minimum optimization. Turns off most optimizations. When debugging is enabled, this option gives the best possible debug view because the structure of the generated code directly corresponds to the source code. All optimization that interferes with the debug view is disabled. In particular:
  • Breakpoints can be set on any reachable point, including dead code.
  • The value of a variable is available everywhere within its scope, except where it is uninitialized.
  • Backtrace gives the stack of open function activations that is expected from reading the source.

Note

Although the debug view produced by  -O0 corresponds most closely to the source code, users might prefer the debug view produced by  -O1 because this improves the quality of the code without changing the fundamental structure.

Note

Dead code includes reachable code that has no effect on the result of the program, for example an assignment to a local variable that is never used. Unreachable code is specifically code that cannot be reached via any control flow path, for example code that immediately follows a return statement.
1
Restricted optimization. The compiler only performs optimizations that can be described by debug information. Removes unused inline functions and unused static functions. Turns off optimizations that seriously degrade the debug view. If used with  --debug, this option gives a generally satisfactory debug view with good code density.
The differences in the debug view from  –O0 are:
  • Breakpoints cannot be set on dead code.
  • Values of variables might not be available within their scope after they have been initialized. For example if their assigned location has been reused.
  • Functions with no side-effects might be called out of sequence, or might be omitted if the result is not needed.
  • Backtrace might not give the stack of open function activations that is expected from reading the source because of the presence of tailcalls.
The optimization level  –O1 produces good correspondence between source code and object code, especially when the source code contains no dead code. The generated code can be significantly smaller than the code at  –O0, which can simplify analysis of the object code.
2
High optimization. If used with  --debug, the debug view might be less satisfactory because the mapping of object code to source code is not always clear. The compiler might perform optimizations that cannot be described by debug information.
This is the default optimization level.
The differences in the debug view from  –O1 are:
  • The source code to object code mapping might be many to one, because of the possibility of multiple source code locations mapping to one point of the file, and more aggressive instruction scheduling.
  • Instruction scheduling is allowed to cross sequence points. This can lead to mismatches between the reported value of a variable at a particular point, and the value you might expect from reading the source code.
  • The compiler automatically inlines functions.
3
Maximum optimization. When debugging is enabled, this option typically gives a poor debug view. ARM recommends debugging at lower optimization levels.
If you use  -O3 and  -Otime together, the compiler performs extra optimizations that are more aggressive, such as:
  • High-level scalar optimizations, including loop unrolling. This can give significant performance benefits at a small code size cost, but at the risk of a longer build time.
  • More aggressive inlining and automatic inlining.
These optimizations effectively rewrite the input source code, resulting in object code with the lowest correspondence to source code and the worst debug view. The  --loop_optimization_level=option controls the amount of loop optimization performed at  –O3 –Otime. The higher the amount of loop optimization the worse the correspondence between source and object code.
For extra information about the high level transformations performed on the source code at  –O3 –Otime use the  --remarks command-line option.
Because optimization affects the mapping of object code to source code, the choice of optimization level with  -Ospace and  -Otime generally impacts the debug view.
The option  -O0 is the best option to use if a simple debug view is required. Selecting  -O0 typically increases the size of the ELF image by 7 to 15%. To reduce the size of your debug tables, use the  --remove_unneeded_entities option.
 



優化級別說明(僅供參考):
則其中的 Code Optimization    欄就是用來設置C51的優化級別。共有9個優化級別(書上這么寫的),高優化級別中包含了前面所有的優化級別。現將各個級別說明如下:

0級優化:
1、 常數折疊:只要有可能,編譯器就執行將表達式化為常數數字的計算,其中包括運行地址的計算。
2、 簡單訪問優化:對8051系統的內部數據和位地址進行訪問優化。
3、 跳轉優化:編譯器總是將跳轉延至最終目標上,因此跳轉到跳轉之間的命令被刪除。
1級優化:
1、 死碼消除:無用的代碼段被消除。
2、 跳轉否決:根據一個測試回溯,條件跳轉被仔細檢查,以決定是否能夠簡化或刪除。
2級優化:
1、 數據覆蓋:適於靜態覆蓋的數據和位段被鑒別並標記出來。連接定位器BL51通過對全局數據流的分析,選擇可靜態覆蓋的段。
3級優化:
1、“窺孔”優化:將冗余的MOV命令去掉,包括不必要的從存儲器裝入對象及裝入常數的操作。另外如果能節省存儲空間或者程序執行時間,復雜操作將由簡單操作所代替。
4級優化:
1、 寄存器變量:使自動變量和函數參數盡可能位於工作寄存器中,只要有可能,將不為這些變量保留數據存儲器空間。
2、擴展訪問優化:來自IDATA、XDATA、PDATA和CODE區域的變量直接包含在操作之中,因此大多數時候沒有必要將其裝入中間寄存器。
3、局部公共子式消除:如果表達式中有一個重復執行的計算,第一次計算的結果被保存,只要有可能,將被用作后續的計算,因此可從代碼中消除繁雜的計算。
4、 CASE/SWITCH語句優化:將CASE/SWITCH語句作為跳轉表或跳轉串優化。
5級優化:
1、 全局公共子式消除:只要有可能,函數內部相同的子表達式只計算一次。中間結果存入一個寄存器以代替新的計算。
2、 簡單循環優化:以常量占據一段內存的循環再運行時被優化。
6級優化:
1、 回路循環:如果程序代碼能更快更有效地執行,程序回路將進行循環。
7級優化:
1、 擴展入口優化:在適合時對寄存器變量使用DPTR數據指針,指針和數組訪問被優化以減小程序代碼和提高執行速度。
8級優化:
1、 公共尾部合並:對同一個函數有多處調用時,一些設置代碼可被重復使用,從而減小程序代碼長度。
9級優化:
1、 公共子程序塊:檢測重復使用的指令序列,並將它們轉換為子程序。C51甚至會重新安排代碼以獲得更多的重復使用指令序列。
當然,優化級別並非越高越好,應該根據具體要求適當選擇。 




Keil C51總線外設操作問題的深入分析
    閱讀了《單片機與嵌入式系統應用》2005年第10期雜志《經驗交流》欄目的一篇文章《Keil C51對同一端口的連續讀取方法》(原文)后,筆者認為該文並未就此問題進行深入准確的分析 文章中提到的兩種解決方法並不直接和簡單。筆者認為這並非是Keil C51中不能處理對一個端口進行連續讀寫的問題,而是對Kei1 C51的使用不夠熟悉和設計不夠細致的問題,因此特撰寫本文。
    本文中對原文提到的問題,提出了三種不同於原文的解決方法。每種方法都比原文中提到的方法更直接和簡單,設計也更規范。(無意批評,請原文作者見諒)

1 問題回顧和分析
    原文中提到:在實際工作中遇到對同一端口反復連續讀取,Keil C51編譯並未達到預期的結果。原文作者對C編譯出來的匯編程序進行分析發現,對同一端口的第二次讀取語句並未被編譯。但可惜原文作者並未分析沒有被編譯的原因,而是匆忙地采用一些不太規范的方法試驗出了兩種解決辦法。
    對此問題,翻閱Keil C51的手冊很容易發現:KeilC51的編譯器有一個優化設置,不同的優化設置,會產生不同的編譯結果。一般情況缺省編譯優化設置被設定為8級優化,實際最高可設定為9級優化:

1. Dead code elimination。
2.Data overlaying。
3.Peephole optimization。
4.Register variables。
5.Common subexpression elimination。
6.Loop rotation。
7.Extended Index Access Optimizing。
8.Reuse Common Entry Code。
9.Common Block Subroutines。
    而以上的問題,正是由於Keil C51編譯優化產生的。因為在原文程序中將外設地址直接按如下定義:
unsigned char xdata MAX197 _at_ 0x8000
    采用_at_將變量MAX197定義到外部擴展RAM 指定地址0x8000。因此,Keil C51優化編譯理所當然認為重復讀第二次是沒有用的,直接用第一次讀取的結果就可以了,因此編譯器跳過了第二條讀取語句。至此,問題就一目了然了。

2 解決方法
由以上分析很容易就能提出很好的解決辦法。
2.1 最簡單最直接的辦法
    程序一點都不用修改,將Keil C51的編譯優化選擇設置為0(不優化)就可以了。選擇project窗口的Target,然后打開“Options for Target”設置對話框,選擇“C51”選項卡,將“Code Optimiztaion”中的“Level”選擇為“0:Costant folding”。再次編譯后,大家會發現編譯結果為:
CLR MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
MOV R7,A
MOV down8,R7
SETB MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
MOV R7,A
MOV up4,R7
兩次讀取操作都被編譯出來了。

2.2 最好的方法
    告訴Keil C51,這個地址不是一般的擴展RAM,而是連接的設備,具有“揮發”特性,每次讀取都是有意義的。可以修改變量定義,增加“volatile”關鍵字說明其特征:
unsigned char volatile xdata MAX197 _at_ 0x8000;
    也可以在程序中包含系統頭文件;“#include<absacc.h>”,然后在程序中修改變量,定義為直接地址:
#define MAX197 XBYTE
    這樣,Keil C51的設置仍然可以保留高級優化,且編譯結果中,同樣兩次讀取並不會被優化跳過。

2 3 硬件解決方法
    原文中將MAX197的數據直接連接到數據總線,而對地址總線並未使用,采用一根端口線選擇操作高低字節。很簡單的修改方法就是使用一根地址線選擇操作高低字節即可。比如:將P2.0(A8)連接到原來P1.0連接的HBEN腳(MAX197的5腳).在程序中分別定義高低字節的操作地址:
unsigned char volatile xdata MAX197_L _at_ 0x8000;
unsigned char volatile xdata MAX197_H _at_ 0x8100;
將原來的程序:
MAXHBEN =0;
down8=MAX197;//讀取低8位
MAXHBEN =1;
up4=MAX197;//讀取高4位
改為以下兩句即可
down8= MAX197_L;//讀取低8位
up4=MAX197_H;//讀取高4位

3 小結
    Keil C51經過長期考驗和改進以及大量開發人員的實際使用,已經克服了絕大多數的問題,並且其編譯效率也非常高。對於一般的使用.很難再發現什么問題。筆者曾經粗略研究過一下Keil C51優化編洋的結果.非常佩服Keil C51設計者的智慧,一些C程序編譯產生的匯編代碼.甚至比一般程序員直接用匯編編寫的代碼還要優秀和簡練 通過研讀Kell C51編譯產生的匯編代碼.對提高匯編語言編寫程序的水平都是很有幫助的。
    由本文中的問題可以看出:在設計中遇到問題時.一定不要被表面現象蒙蔽,不要急於解決,應該認真分析,找出問題的原因.這樣才能從根本上徹底解決問題。

附表:Keil C51中的優化級別及優化作用 級別         說明
0         常數合並:編譯器預先計算結果,盡可能用常數代替表達式。包括運行地址計算。
優化簡單訪問:編譯器優化訪問8051系統的內部數據和位地址。
跳轉優化:編譯器總是擴展跳轉到最終目標,多級跳轉指令被刪除。
1         死代碼刪除:沒用的代碼段被刪除。
拒絕跳轉:嚴密的檢查條件跳轉,以確定是否可以倒置測試邏輯來改進或刪除。
2         數據覆蓋:適合靜態覆蓋的數據和位段被確定,並內部標識。BL51連接/定位器可以通過全局數據流分析,選擇可被覆蓋的段。
3         窺孔優化:清除多余的MOV指令。這包括不必要的從存儲區加載和常數加載操作。當存儲空間或執行時間可節省時,用簡單操作代替復雜操作。
4         寄存器變量:如有可能,自動變量和函數參數分配到寄存器上。為這些變量保留的存儲區就省略了。
優化擴展訪問:IDATA、XDATA、PDATA和CODE的變量直接包含在操作中。在多數時間沒必要使用中間寄存器。
局部公共子表達式刪除:如果用一個表達式重復進行相同的計算,則保存第一次計算結果,后面有可能就用這結果。多余的計算就被刪除。
Case/Switch優化:包含SWITCH和CASE的代碼優化為跳轉表或跳轉隊列。
5         全局公共子表達式刪除:一個函數內相同的子表達式有可能就只計算一次。中間結果保存在寄存器中,在一個新的計算中使用。
簡單循環優化:用一個常數填充存儲區的循環程序被修改和優化。
6         循環優化:如果結果程序代碼更快和有效則程序對循環進行優化。
7         擴展索引訪問優化:適當時對寄存器變量用DPTR。對指針和數組訪問進行執行速度和代碼大小優化。
8         公共尾部合並:當一個函數有多個調用,一些設置代碼可以復用,因此減少程序大小。
9         公共塊子程序:檢測循環指令序列,並轉換成子程序。Cx51甚至重排代碼以得到更大的循環序列。




優化論

談到優化,其實很多人都哭笑不得,因為在一個C51軟件工程師的生涯中,總要被KEIL的優化耍那么一次到幾次。我被耍過,想必看着文章的你也被耍過,如果你回答說不,那只能說你寫的C51程序不多!
看看KEILC的優化級別選項吧:


0-9共10個級別的優化,0是最低,9最高,一個普通的程序,設置最高級別和最低級別,編譯后代碼量有時會相差很遠,以DX板DEMO程序為例,0級優化后是14K的CODE,9級優化后是10K的CODE,前后相差了4K。可見這個差別是多么的大。
事實上我們不需要知道對應的各個級別KEIL會如何優化你的程序或優化了些什么,我們只需要以一種嚴謹的態度去編寫和對待你的程序就可以了。在我個人的觀念中,程序在9級優化后依然能保持完美無誤的運行,你才算了解KEIL的脾氣。
好了,還是說點正點的:
有些人習慣整體程序都選擇同一個優化級,事實上每個C文件都可以有獨立的優化級別的:

在工作區右鍵選擇你的模塊(.C)然后選取Options for File xxx就會出現如下界面:


在C51選項中就可以選擇優化級別和警告級別等東西了,被獨立設置過的C文件會有特殊的標記的:


用以提醒你這個文件的編譯處理並非默認設置!
如果你覺得模塊優化都不夠細的話,你可以考慮局部優化,也就是說對某個函數實行某個級別的優化。當你發現9級優化的時候某個函數總是變的不正常,但你又希望其它函數和程序段保持最高的簡潔度,那么局部優化可以說是相當有用的了。在KEIL手冊中有介紹這個功能:
#pragma OPTIMIZE(x) x就是你希望的優化級別,一般應用如下:

#pragma OPTIMIZE(6)
void FunA()
{
}
......
......

#pragma OPTIMIZE(9)
void FunB()
{
}

上面的意思就是說,在void FunA()到void FunB()之前的所有函數,包括FunA在內,都采用6級的優化,而從FunB開始直到之后,只要沒碰上#pragma OPTIMIZE,都采用9級優化了。
OPTIMIZE還可以多一個參數,就是speed和size,
用法: #pragma OPTIMIZE(9,speed)或#pragma OPTIMIZE(5,size)
對應的就是9級優化,以速度為主,或5級優化,以空間最小為主。

4.StartUp.a51
在之前第一節的建立工程中就曾經提到過StartUp.a51這個東西了,就是在工程初建的時候有個對話框用於選擇是否為工程添加這個a51文件。


其實這個文件給大家最最深刻的感覺就是:開機清空RAM。事實上它還有其他特別的用途的,例如初始化堆棧(很多人不知道KEILC一開始把堆棧設定為多少,事實上可以通過軟件仿真的時候從這個文件找到答案),然后是再入函數的虛擬堆棧的設置,還有更高級一點的,BANK的初始化。
舊版本KEIL自動為每個工程默認添加相同的StartUp文件,后期的KEIL就有了上圖的選擇,如果選擇添加,則會為每個工程添加一個獨立的StartUp。用戶可以通過手工改寫StartUp.a51實現某些必要的上電初始化。例如最通常的:取消單片機開機清RAM功能!!
關於STARTUP的介紹,我建議大家看看以下的文章,它的解釋非常詳盡。

##################################

在實際使用時發現仿真時有寫程序是白色的無法進行斷點設置

搜索到的答案是優化等級過高,一些普通的程序被優化。

只得把優化程序等級降低。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM