Arm匯編學習筆記(九)——高效的分支代碼及非對齊數據的訪問

ARM 848瀏覽

 

分支代碼switch(x)在我們平常的代碼中是非常常見的,而且也是比較耗時的操作,如果優化以后可以對代碼的效率有很大提升。

1. 對于0 <= x < N類型的分支代碼

此種情況N不能太大,對于下面C代碼:
int ref_switch(int x)
{
     switch (x) {
                 case 0: return method_0();
                 case 1: return method_1();
                 case 2: return method_2();
                 case 3: return method_3();
                 case 4: return method_4();
                 case 5: return method_5();
                 case 6: return method_6();
                 case 7: return method_7();
                 default: return method_d();
     } 
}

我們可以以pc寄存器的值為基準,以x的值作為索引來實現,優化的匯編代碼如下:

?          ; int switch_relative(int x)
switch_relative
         MP     x, #8
         ADDLT   pc, pc, x, LSL#2
         B       method_d
         B       method_0
         B       method_1
         B       method_2
         B       method_3
         B       method_4
         B       method_5
         B       method_6
         B       method_7

2. x是一個普通的值

如果遇到x不遵循0 <= x < N這種形式,或者N非常大,上面那種方式顯然不適用了。這種情況下我們可以用hashing function來映射一下,即y = f(x),可以將其轉換成0 <= y < N的形式,以y = f(x)而不是x作為分支判斷的條件,這樣我們就可以采用上面的方法了。
舉例假設,當x = 2^k時,調用method_k函數,即x的取值有1,2,4,8,16,32,64,128,其它值調用默認函數method_d。我們需要找到一個hash函數由2的若干次方減一相乘組成(這種方式在ARM上效率比較高,直接位移就可以實現)。經過實驗發現,對于上面8個值x * 15 * 31得到的數的第9-11位是不同的,我們可以利用這個特點通過位運算來實現分支跳轉。
下面是優化后的匯編代碼:
x RN0
hash RN 1
                     ; int switch_hash(int x)
switch_hash
                     RSB     hash, x, x, LSL#4             ; hash=x*15
                     RSB     hash, hash, hash, LSL#5   ; hash=x*15*31
                     AND hash, hash, #7 << 9           ; mask out the hash value
                     ADD pc, pc, hash, LSR#6 
                     NOP
                     TEQ x, #0x01
                     BEQ     method_0
                     TEQ     x, #0x02
                     BEQ     method_1
                     TEQ     x, #0x40
                     BEQ     method_6
                     TEQ     x, #0x04
                     BEQ     method_2
                     TEQ     x, #0x80
                     BEQ     method_7
                     TEQ     x, #0x20
                     BEQ     method_5
                     TEQ     x, #0x10
                     BEQ     method_4
                     TEQ     x, #0x08
                     BEQ     method_3
                     B       method_d

上面的方法只是我們舉出的一個特例,在x為非2的冪的情況下,我們依然可以使用相似的方法來實現。這里僅僅提供一種思路。

 

3. 非對齊數據訪問

對于非地址對齊的數據訪問應該盡量避免,否則對可移植性和效率都是不利的。
  • 最簡單的訪問方法是以一個字節或半字為單位進行讀寫,這種方法是比較推薦的,但是效率相對比較低。

下面代碼讀取一個非地址對齊的32位數據,我們使用t0,t1,t2三個寄存器去讀取以防止流水線互鎖。在ARM9TDMI上每個非地址對齊的數據讀取需要消耗7個時鐘周期。下面的例子中列出了little_endian和big_endian對應的版本。

p RN0 x RN1
t0 RN 2
t1 RN 3
t2 RN 12
                     ; int load_32_little(char *p)
load_32_little
                     LDRB    x,  [p]
                     LDRB    t0, [p, #1]
                     LDRB    t1, [p, #2]
                     LDRB    t2, [p, #3]
                     ORR     x, x, t0, LSL#8
                     ORR     x, x, t1, LSL#16
                     ORR     r0, x, t2, LSL#24
                     MOV     pc, lr
                     ; int load_32_big(char *p)
load_32_big
                     LDRB    x,  [p]
                     LDRB    t0, [p, #1]
                     LDRB    t1, [p, #2]
                     LDRB    t2, [p, #3]
                     ORR     x, t0, x, LSL#8
                     ORR     x, t1, x, LSL#8
                     ORR     r0, t2, x, LSL#8
                     MOV     pc, lr
                   ; void store_32_little(char *p, int x)
store_32_little
                   STRB    x,  [p]
                   MOV     t0, x, LSR#8
                   STRB    t0, [p, #1]
                   MOV     t0, x, LSR#16
                   STRB    t0, [p, #2]
                   MOV     t0, x, LSR#24
                   STRB    t0, [p, #3]
                   MOV     pc, lr
                   ; void store_32_big(char *p, int x)
store_32_big
                   MOV t0, x, LSR#24
                   STRB t0, [p]
                   MOV t0, x, LSR#16
                   STRB t0, [p, #1]
                   MOV t0, x, LSR#8
                   STRB t0, [p, #2]
                   STRB x, [p, #3]
                   MOV pc,lr

 

七星彩走势图2元网官网