道道指令皆辛苦...
從前讀ARM instruction set的時候, 覺得CPU提供一大堆addressing mode很煩
那時候想說, 反正就是多一道兩道指令, 何必這麼辛苦?
最近有機會提CPU enhancement proposal, 需要仔細review C code和assembly code
舉例來講, C code可能長這樣...
WRITE(ADDR, 0xaaaa)
首先是寫constant的部分, 需要三道指令才做得完
load #ADDR, r0
load #0xaaaa, r1
store r1, [r0]
接著我們就會想, 如果可以直接把constant塞到instruction的話
就可以少一道指令, 也可以少用一個register
load #ADDR, r0
store 0xaaaa, [r0]
----
接著繼續思考不同的C code...
WRITE(ADDR, 0xaaaa)
把上面的enhancement拿來用, 會變成這樣...
load #ADDR, r0
store 0xaaaa, [r0]
add r0, #1
store 0xbbbb, [r0]
如果遇到連續的address寫入, 這樣就會多一道中間的addi, 看起來有點討厭
如果讓store做完的時候, 同時把address pointer +1, 這個問題就可以被完美的解決
load #ADDR, r0
store 0xaaaa, [r0], #1
store 0xbbbb, [r0], #1
這樣一來, 除了第一道指令載入address, 剩下都可以1cycle寫一筆出去
----
然後接著又會想, store constant的指令, 寫完就+1是不是我們要的行為?
有些硬體用indirect的方法存取, 行為像是這樣...
WRITE(PERI_ADDR, 0x100)
WRITE(PERI_ADDR+1, 0xaaaa) // peri[0x100] = 0xaaaa
WRITE(PERI_ADDR+1, 0xbbbb) // peri[0x101] = 0xbbbb
WRITE(PERI_ADDR+1, 0xcccc) // peri[0x102] = 0xcccc
如果store constant的指令固定會+1, 那上面的指令就會變成
load #ADDR, r0
store 0x100, [r0], #1
store 0xaaaa, [r0], #1
add r0, #-1
store 0xbbbb, [r0], #1
add r0, #-1
store 0xcccc, [r0], #1
因為會有硬體用indirect的方式存取, store constant也不能讓他總是動pointer
所以說呢, 我們還要提供一個模式, 決定是不是要post-increment
如果可以在指令裡決定post-increment, 這樣問題就解決了...
load #ADDR, r0
store 0x100, [r0], #1
store 0xaaaa, [r0], #0
store 0xbbbb, [r0], #0
store 0xcccc, [r0], #0
----
但是呢, 指令的bits數可能不夠, 以上面的例子來說, 這些資訊要塞進指令裡
- op-code
- immediate value
- register index
- post-increment or not
post-increment切換的功能需要1bit, 但是我已經用完所有空間
思考要不要拿register index來交換? 比如我只定址一半的register交換post-increment
再下一個問題, 好吧我真的拿register來換了, 是拿前半段還是後半段?
那就算換了, compiler是不是能自動產生出指令?
----
表面上看起來, 啊不就是要做到這些嗎? 其實背後分析的工作量遠超乎想像
做過才知道, 為了讓code跑得飛快, 真的是不容易
那時候想說, 反正就是多一道兩道指令, 何必這麼辛苦?
最近有機會提CPU enhancement proposal, 需要仔細review C code和assembly code
舉例來講, C code可能長這樣...
WRITE(ADDR, 0xaaaa)
首先是寫constant的部分, 需要三道指令才做得完
load #ADDR, r0
load #0xaaaa, r1
store r1, [r0]
接著我們就會想, 如果可以直接把constant塞到instruction的話
就可以少一道指令, 也可以少用一個register
load #ADDR, r0
store 0xaaaa, [r0]
----
接著繼續思考不同的C code...
WRITE(ADDR, 0xaaaa)
WRITE(ADDR+1, 0xbbbb)
把上面的enhancement拿來用, 會變成這樣...
load #ADDR, r0
store 0xaaaa, [r0]
add r0, #1
store 0xbbbb, [r0]
如果遇到連續的address寫入, 這樣就會多一道中間的addi, 看起來有點討厭
如果讓store做完的時候, 同時把address pointer +1, 這個問題就可以被完美的解決
load #ADDR, r0
store 0xaaaa, [r0], #1
store 0xbbbb, [r0], #1
這樣一來, 除了第一道指令載入address, 剩下都可以1cycle寫一筆出去
----
然後接著又會想, store constant的指令, 寫完就+1是不是我們要的行為?
有些硬體用indirect的方法存取, 行為像是這樣...
WRITE(PERI_ADDR, 0x100)
WRITE(PERI_ADDR+1, 0xaaaa) // peri[0x100] = 0xaaaa
WRITE(PERI_ADDR+1, 0xbbbb) // peri[0x101] = 0xbbbb
WRITE(PERI_ADDR+1, 0xcccc) // peri[0x102] = 0xcccc
如果store constant的指令固定會+1, 那上面的指令就會變成
load #ADDR, r0
store 0x100, [r0], #1
store 0xaaaa, [r0], #1
add r0, #-1
store 0xbbbb, [r0], #1
add r0, #-1
store 0xcccc, [r0], #1
因為會有硬體用indirect的方式存取, store constant也不能讓他總是動pointer
所以說呢, 我們還要提供一個模式, 決定是不是要post-increment
如果可以在指令裡決定post-increment, 這樣問題就解決了...
load #ADDR, r0
store 0x100, [r0], #1
store 0xaaaa, [r0], #0
store 0xbbbb, [r0], #0
store 0xcccc, [r0], #0
----
但是呢, 指令的bits數可能不夠, 以上面的例子來說, 這些資訊要塞進指令裡
- op-code
- immediate value
- register index
- post-increment or not
post-increment切換的功能需要1bit, 但是我已經用完所有空間
思考要不要拿register index來交換? 比如我只定址一半的register交換post-increment
再下一個問題, 好吧我真的拿register來換了, 是拿前半段還是後半段?
那就算換了, compiler是不是能自動產生出指令?
----
表面上看起來, 啊不就是要做到這些嗎? 其實背後分析的工作量遠超乎想像
做過才知道, 為了讓code跑得飛快, 真的是不容易
留言