關於ARM上程式設計的

2021-06-15 22:09:28 字數 4768 閱讀 1764

arm彙編優化

要做程式的優化,最徹底的方法當然是彙編!還有除了彙編以外(除了二進位制)能讓你對你的處理器有更全面的控制嗎?!對於arm彙編,作為乙個初學者,也就只好先補補基礎了@_@。

首先,程式段的定義從area 開始,它命名乙個**區域,注意,用非阿拉伯數字作為名字時,應該用|把名字包起來,code關鍵字宣告程式(猜測),readonly宣告訪問許可權(猜測)。export 來表示某個可以用作外部連線的符號(簡單點,應該就是函式名?)。end用來結尾。

#eg:

area    |.text|, code, readonly

export   square

; int square(int i)

square      ;armcc把不縮排的正文作為乙個標號定義

mul    r1,r0,r0

mov   r0,r1           ;arm乘法指令有乙個限制,就是目標暫存器不能和第乙個引數暫存器相同

mov   pc,lr            ;對thumb指令,應該改為bx lr

end使用import,可以宣告其他檔案中定義的標號,要用arm c庫的話,就import |lib$request$armlib|, weak表示本行的標號如果找不到,不會報告連線錯誤。如果程式包含主程式main,那麼要引入標號__main,代表c庫初始化的開始。rn可以讓使用者給暫存器命名。

#eg:

area            |.text|, code,readonly

export     main

import     |lib$$request$$armlib|, weak

import     __main             ;c library entry

import     printf                ;prints to stdout

i    rn   4

;int main(void)

main

stmfd      sp!,

mov         i,#0

loop

adr          r0,print_string

mov         r1,i

mul          r2,i,i

bl            printf

add         i,i,#1

cmp          i,#10

blt          loop

ldmfd      sp!,

print_string

dcb         " square of %d is %d/n", 0

endmap(別名^)和field(別名#),可以在堆疊中為變數和陣列定義和分配空間。

map     0                             ;map symbols to offsets starting at offset 0?

a            field    4                             ;a is 4 bytes integer(at offset 0)

b            field    2                            ;b is bytes integer(at offset 4)

c            field    64                           ;c is an array of 64 characters (at offset 6)

length     field   0                             ;length records the current offset reached?

macro用來宣告巨集:

macro

checksum $ alignment

checksum_$ alignment

ldr         w,[data],#4

l0         ;loop

if $ alignment<>0

add     sum,sum,w,lsr #8, $alignement

ldr      w,[data],#4

subs    n,n,#1

add     sum,sum,w,lsl#32-8* $ alignment

else

add     sum,sum,w

ldr      w,[data],#4

subs    n,n,#1

endif

bgt         %bt l0

mov         pc,lr

mend

針對彙編的優化主要是面向硬體的。首先是流水線,有些load指令要需要多個週期來完成,可以通過調整指令的順序(當然要保證邏輯)來改善效能。另外,盡量讓程式只是用暫存器,方法是搞清楚資料占用暫存器的時間關係,實現暫存器有效的分時復用。另外,可以將長度較小的變數合併到乙個32位暫存器中儲存,以節省暫存器。由於pc可以通過程式操作,對於條件指令,可以直接用pc與形成分支的引數作運算來尋找對應的分支:

;int switch_relative(int x)

switch_relative

cmp          x,#8

addlt      pc,pc,x,lsl,#2

b              method_d      ;利用流水線,如果pc還是按順序那麼default分支的預取址就不

;會被沖掉

b              method_0

b              method_1

arm上的c程式設計

1.arm c編譯器預設char型別是8位無符號的,與其它編譯器有點不同

2.區域性變數最好用int型,因為暫存器是32位的,如果變數不是32位的就需要額外的指令限制範圍.

例如: 變數i,操作i++ ,如果int i, 則只需add r1,r1,#1  如果char i,則變成add r1,r1,#1 

and r1,r1, 0xff  .多了一條指令

3.迴圈最好用do{}while()型的,相比for(;;)型迴圈每次迴圈可以節省3條指令

4.函式引數也最好用int 型的,例如 short add(short x,short y)

編譯器為了保證輸入引數的是short型的會新增額外的指令,比如確保x是short型的,需要

mov r0,r0,lsr #16    mov r0,r0,asr #16

5.函式引數最好不要超過4個,因為前4個引數是通過暫存器r0-r3傳遞的,超過4個後的引數使用堆疊傳遞,速度慢多了.

6.適當的展開迴圈.迴圈有一定的開銷,在乙個迴圈中多做幾遍操作,減少迴圈的次數可以減少迴圈的開銷.

例如:  i = 0;         int i;

do                                        dowhile(i<64)   改為             i++;}while(i<64)

當然,這樣做也增加了**長度.

7.使用減計數到0的迴圈結構,這樣就不用用暫存器儲存終止值.

8.使用無符號的迴圈計數值,迴圈條件是i!=0,而不是i>0, 這樣迴圈的開銷只有2條指令 

arm上的彙編優化小方法

1. 加減法,邏輯操作佔乙個週期,目的位址是pc暫存器時增加乙個週期。分支指令佔3個週期。在cache命中的情況下,16位和8位的裝載指令(ldrh、ldrh等)佔乙個週期,但緊跟的2個週期不能使用裝入的資料。32位裝載指令佔乙個週期,緊跟的乙個週期不能使用裝載資料。如果裝載入pc,同樣要增加2個週期。

ldr    r1,[r2]        add  r1,r1,r3       add  r4,r4,r5    佔4個週期

改變次序後

ldr    r1,[r2]        add  r4,r4,r5        add  r1,r1,r3    佔3個週期

2. load指令佔時間比較長,在迴圈中可以使用預載的方法將load與跳轉指令放在一起,減少流水線的斷流。

例如:loop

ldrb   r2,[r1]

...............           //do

b            loop

更改為ldrb   r2,[r1]

loop

..............            //do

ldrb   r2,[r1]

b          loop

3. 迴圈展開時,可以在計算i步時就載入i+1步的資料,在i步的結果還沒準備好時執行i+1步計算。

4. arm只有16個可見暫存器,其中14個通用暫存器,1個堆疊指標r13,1個程式計數器r15。在影象處理的應用中很多是8位的運算元,可以利用32為暫存器一次進行兩組運算。

例如:加操作      100 + 50    和       2 + 3

位                     24                16               8                 0

運算元1           0                100              0                 2

運算元2           0                 50                0                3

結果                 0                 150              0                5

5. 暫存器數量不夠時,可用32為暫存器儲存兩個16位變數和4個8位變數。

ARM上的C程式設計

1 arm c編譯器預設char型別是8位無符號的,與其它編譯器有點不同 2 區域性變數最好用int型,因為暫存器是32位的,如果變數不是32位的就需要額外的指令限制範圍.例如 變數i,操作i 如果int i,則只需add r1,r1,1 如果char i,則變成add r1,r1,1 and r1...

ARM上的C程式設計

1 arm c編譯器預設char型別是8位無符號的,與其它編譯器有點不同 2 區域性變數最好用int型,因為暫存器是32位的,如果變數不是32位的就需要額外的指令限制範圍.例如 變數i,操作i 如果int i,則只需add r1,r1,1 如果char i,則變成add r1,r1,1 and r1...

ARM程式設計 關於ARM暫存器的巨集定義

arm程式設計 關於arm暫存器的巨集定義 gf86530430 136次 2008 8 27 請問各位大蝦 在arm的標頭檔案裡,對與暫存器的定義為什麼不跟51的一樣?如下 define rutxh0 volatile unsigned char 0x1d00020 define rutxh1 v...