




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、轉(zhuǎn)自:ARM GCC 內(nèi)嵌(inline)匯編手冊關(guān)于這篇文檔這篇文章是本人為方便各位業(yè)界同仁而翻譯,方便大家開發(fā)底層代碼使用,轉(zhuǎn)載請注明出處,謝謝。要是你E文功底好,本人還是建議閱讀E文版的。http:/www.ethernut.de/en/documents/arm-inline-asm.html對于基于ARM的RISC處理器,GNU C編譯器提供了在C代碼中內(nèi)嵌匯編的功能。這種非常酷的特性提供了C代碼沒有的功能,比如手動優(yōu)化軟件關(guān)鍵部分的代碼、使用相關(guān)的處理器指令。這里設(shè)想了讀者是熟練編寫ARM匯編程序讀者,因為該片文檔不是ARM匯編手冊。同樣也不是C語言手冊。這篇文檔假設(shè)使用的是GCC
2、 4 的版本,但是對于早期的版本也有效。GCC asm 聲明讓我們以一個簡單的例子開始。就像C中的聲明一樣,下面的聲明代碼可能出現(xiàn)在你的代碼中。/* NOP 例子 */asm("mov r0,r0"該語句的作用是將r0移動到r0中。換句話講他并不干任何事。典型的就是NOP指令,作用就是短時的延時。請接著閱讀和學習這篇文檔,因為該聲明并不像你想象的和其他的C語句一樣。內(nèi)嵌匯編使用匯編指令就像在純匯編程序中使用的方法一樣??梢栽谝粋€asm聲明中寫多個匯編指令。但是為了增加程序的可讀性,最好將每一個匯編指令單獨放一行。asm("mov r0, r0nt"&qu
3、ot;mov r0, r0nt""mov r0, r0nt""mov r0, r0"換行符和制表符的使用可以使得指令列表看起來變得美觀。你第一次看起來可能有點怪異,但是當C編譯器編譯C語句的是候,它就是按照上面(換行和制表)生成匯編的。到目前為止,匯編指令和你寫的純匯編程序中的代碼沒什么區(qū)別。但是對比其它的C聲明,asm的常量和寄存器的處理是不一樣的。通用的內(nèi)嵌匯編模版是這樣的。asm(code : output operand list : input operand list : clobber list;匯編和C語句這間的聯(lián)系是通過上面a
4、sm聲明中可選的output operand list和input operand list。Clobber list后面再講。下面是將C語言的一個整型變量傳遞給匯編,邏輯左移一位后在傳遞給C語言的另外一個整型變量。/* Rotating bits example */asm("mov %result, %value, ror #1" : result "=r" (y : value "r" (x;每一個asm語句被冒號(:)分成了四個部分。 匯編指令放在第一部分中的“”中間。"mov %result, %value, ro
5、r #1" 接下來是冒號后的可選擇的output operand list,每一個條目是由一對(方括號)和被他包括的符號名組成,它后面跟著限制性字符串,再后面是圓括號和它括著的C變量。這個例子中只有一個條目。result "=r" (y 接著冒號后面是輸入操作符列表,它的語法和輸入操作列表一樣value "r" (x 破壞符列表,在本例中沒有使用就像上面的NOP例子,asm聲明的4個部分中,只要最尾部沒有使用的部分都可以省略。但是有有一點要注意的是,上面的4個部分中只要后面的還要使用,前面的部分沒有使用也不能省略,必須空但是保留冒號。下面的一個
6、例子就是設(shè)置ARM Soc的CPSR寄存器,它有input但是沒有output operand。 asm("msr cpsr,%ps" : : ps"r"(status即使匯編代碼沒有使用,代碼部分也要保留空字符串。下面的例子使用了一個特別的破壞符,目的就是告訴編譯器內(nèi)存被修改過了。這里的破壞符在下面的優(yōu)化部分在講解。 asm("":"memory"為了增加代碼的可讀性,你可以使用換行,空格,還有C風格的注釋。asm("mov %result, %value, ror #1": result&q
7、uot;=r" (y /* Rotation result. */: value"r" (x /* Rotated value. */: /* No clobbers */;在代碼部分%后面跟著的是后面兩個部分方括號中的符號,它指的是相同符號操作列表中的一個條目。%result表示第二部分的C變量y,%value表示三部分的C變量x;符號操作符的名字使用了獨立的命名空間。這就意味著它使用的是其他的符號表。簡單一點就是說你不必關(guān)心使用的符號名在C代碼中已經(jīng)使用了。在早期的C代碼中,循環(huán)移位的例子必須要這么寫:asm("mov %0, %1, ror #1&
8、quot; : "=r" (result : "r" (value在匯編代碼中操作數(shù)的引用使用的是%后面跟一個數(shù)字,%1代表第一個操作數(shù),%2代碼第二個操作數(shù),往后的類推。這個方法目前最新的編譯器還是支持的。但是它不便于維護代碼。試想一下,你寫了大量的匯編指令的代碼,要是你想插入一個操作數(shù),那么你就不得不從新修改操作數(shù)編號。優(yōu)化C代碼有兩種情況決定了你必須使用匯編。1st,C限制了你更加貼近底層操作硬件,比如,C中沒有直接修改程序狀態(tài)寄存器(PSR)的聲明。2nd就是要寫出更加優(yōu)化的代碼。毫無疑問GNU C代碼優(yōu)化器做的很好,但是他的結(jié)果和我們手工寫的匯
9、編代碼相差很遠。這一部分有一點很重要,也是被別人忽視最多的就是:我們在C代碼中通過內(nèi)嵌匯編指令添加的匯編代碼,也是要被C編譯器的優(yōu)化器處理的。讓我們下面做個試驗來看看吧。下面是代碼實例。bigtreejust:/embedded/basic-C$ arm-linux-gcc -c test.cbigtreejust:/embedded/basic-C$ arm-linux-objdump -D test.o編譯器選擇r3作為循環(huán)移位使用。它也完全可以選擇為每一個C變量分配寄存器。Load或者store一個值并不顯式的進行。下面是其它編譯器的編譯結(jié)果。E420A0E1 mov r2, r4, r
10、or #1 y, x編譯器為每一個操作數(shù)選擇一個相應(yīng)的寄存器,將操作過的值cache到r4中,然后傳遞該值到r2中。這個過程你能理解不?有的時候這個過程變得更加糟糕。有時候編譯器甚至完全拋棄你嵌入的匯編代碼。C編譯器的這種行為,取決于代碼優(yōu)化器的策略和嵌入?yún)R編所處的上下文。如果在內(nèi)嵌匯編語句中不使用任何輸出部分,那么C代碼優(yōu)化器很有可能將該內(nèi)嵌語句完全刪除。比如NOP例子,我們可以使用它作為延時操作,但是對于編譯器認為這影響了程序的執(zhí)行速速,認為它是沒有任何意義的。上面的解決方法還是有的。那就是使用volatile關(guān)鍵字。它的作用就是禁止優(yōu)化器優(yōu)化。將NOP例子修改過后如下:/* NOP ex
11、ample, revised */asm volatile("mov r0, r0"下面還有更多的煩惱等著我們。一個設(shè)計精細的優(yōu)化器可能重新排列代碼??聪旅娴拇a:i+;if (j = 1x += 3;i+;優(yōu)化器肯定是要從新組織代碼的,兩個i+并沒有對if的條件產(chǎn)生影響。更進一步的來講,i的值增加2,僅僅使用一條ARM匯編指令。因而代碼要重新組織如下:if (j = 1x += 3;i += 2;這樣節(jié)省了一條ARM指令。結(jié)果是:這些操作并沒有得到許可。這些將對你的代碼產(chǎn)生很到的影響,這將在下面介紹。下面的代碼是c乘b,其中c和b中的一個或者兩個可能會被中斷處理程序修改。
12、進入該代碼前先禁止中斷,執(zhí)行完該代碼后再開啟中斷。asm volatile("mrs r12, cpsrnt""orr r12, r12, #0xC0nt""msr cpsr_c, r12nt" : "r12", "cc"c *= b; /* This may fail. */asm volatile("mrs r12, cpsrn""bic r12, r12, #0xC0n""msr cpsr_c, r12" : "r12&
13、quot;, "cc"但是不幸的是針對上面的代碼,優(yōu)化器決定先執(zhí)行乘法然后執(zhí)行兩個內(nèi)嵌匯編,或相反。這樣將會使得我們的代碼變得毫無意義。我們可以使用clobber list幫忙。上面例子中的clobber list如下:"r12", "cc"上面的clobber list將會將向編譯器傳達如下信息,修改了r12和程序狀態(tài)寄存器的標志位。Btw,直接指明使用的寄存器,將有可能阻止了最好的優(yōu)化結(jié)果。通常你只要傳遞一個變量,然后讓編譯器自己選擇適合的寄存器。另外寄存器名,cc(condition registor 狀態(tài)寄存器標志位),mem
14、ory都是在clobber list上有效的關(guān)鍵字。它用來向編譯器指明,內(nèi)嵌匯編指令改變了內(nèi)存中的值。這將強迫編譯器在執(zhí)行匯編代碼前存儲所有緩存的值,然后在執(zhí)行完匯編代碼后重新加載該值。這將保留程序的執(zhí)行順序,因為在使用了帶有memory clobber的asm聲明后,所有變量的內(nèi)容都是不可預測的。asm volatile("mrs r12, cpsrnt""orr r12, r12, #0xC0nt""msr cpsr_c, r12nt" : : "r12", "cc", "memo
15、ry"c *= b; /* This is safe. */asm volatile("mrs r12, cpsrn""bic r12, r12, #0xC0n""msr cpsr_c, r12" : "r12", "cc", "memory"使所有的緩存的值都無效,只是局部最優(yōu)(suboptimal)。你可以有選擇性的添加dummy operand 來人工添加依賴。asm volatile("mrs r12, cpsrnt""orr
16、r12, r12, #0xC0nt""msr cpsr_c, r12nt" : "=X" (b : "r12", "cc"c *= b; /* This is safe. */asm volatile("mrs r12上面的第一個asm試圖修改變量先b,第二個asm試圖修改c。這將保留三個語句的執(zhí)行順序,而不要使緩存的變量無效。理解優(yōu)化器對內(nèi)嵌匯編的影響很重要。如果你讀到這里還是云里霧里,最好是在看下個主題之前再把這段文章讀幾遍_。Input and output operands前面我們學到,
17、每一個input和output operand,由被方括號中的符號名,限制字符串,圓括號中的C表達式構(gòu)成。這些限制性字符串有哪些,為什么我們需要他們?你應(yīng)該知道每一條匯編指令只接受特定類型的操作數(shù)。例如:跳轉(zhuǎn)指令期望的跳轉(zhuǎn)目標地址。不是所有的內(nèi)存地址都是有效的。因為最后的opcode只接受24位偏移。但矛盾的是跳轉(zhuǎn)指令和數(shù)據(jù)交換指令都希望寄存器中存儲的是32位的目標地址。在所有的例子中,C傳給operand的可能是函數(shù)指針。所以面對傳給內(nèi)嵌匯編的常量、指針、變量,編譯器必須要知道怎樣組織到匯編代碼中。對于ARM核的處理器,GCC 4 提供了一下的限制。ConstraintUsage in AR
18、M stateUsage in Thumb statefFloating point registers f0 . f7Not availablehNot availableRegisters r8.r15GImmediate floating point constantNot availableHSame a G, but negatedNot availableIImmediate value in data processing instructionse.g. ORR R0, R0, #operandConstant in the range 0 . 255e.g. SWI oper
19、andJIndexing constants -4095 . 4095e.g. LDR R1, PC, #operandConstant in the range -255 . -1e.g. SUB R0, R0, #operandKSame as I, but invertedSame as I, but shiftedLSame as I, but negatedConstant in the range -7 . 7e.g. SUB R0, R1, #operandlSame as rRegisters r0.r7e.g. PUSH operandMConstant in the ran
20、ge of 0 . 32 or a power of 2e.g. MOV R2, R1, ROR #operandConstant that is a multiple of 4 in the range of 0 . 1020e.g. ADD R0, SP, #operandmAny valid memory addressNNot availableConstant in the range of 0 . 31e.g. LSL R0, R1, #operandONot availableConstant that is a multiple of 4 in the range of -50
21、8 . 508e.g. ADD SP, #operandrGeneral register r0 . r15e.g. SUB operand1, operand2, operand3Not availablewVector floating point registers s0 . s31Not availableXAny operand限制字符可能要單個modifier指示。要是沒有modifier指示的默認為read-only operand。ModifierSpecifies=Write-only operand, usually used for all output operands
22、+Read-write operand, must be listed as an output operand&A register that should be used for output onlyOutput operands必須為write-only,相應(yīng)C表達式的值必須是左值。Input operands必須為read-only。C編譯器是沒有能力做這個檢查。比較嚴格的規(guī)則是:不要試圖向input operand寫。但是如果你想要使用相同的operand作為input和output。限制性modifier(+)可以達到效果。例子如下:asm("mov %valu
23、e, %value, ror #1" : value "+r" (y和上面例子不一樣的是,最后的結(jié)果存儲在input variable中??赡躮odifier + 不支持早期的編譯器版本。慶幸的是這里提供了其他解決辦法,該方法在最新的編譯器中依然有效。對于input operators有可能使用單一的數(shù)字n在限制字符串中。使用數(shù)字n可以告訴編譯器使用的第n個operand,operand都是以0開始計數(shù)。下面是例子:asm("mov %0, %0, ror #1" : "=r" (value : "0"
24、(value限制性字符串“0”告訴編譯器,使用和第一個output operand使用同樣input register。請注意,在相反的情況下不會自動實現(xiàn)。如果我沒告訴編譯器那樣做,編譯器也有可能為input和output選擇相同的寄存器。第一個例子中就為input和output選擇了r3。在多數(shù)情況下這沒有什么,但是如果在input使用前output已經(jīng)被修改過了,這將是致命的。在input和output使用不同寄存器的情況下,你必須使用&modifier來限制output operand。下面是代碼示例:asm volatile("ldr %0, %1" &qu
25、ot;nt""str %2, %1, #4" "nt" : "=&r" (rdv : "r" (&table, "r" (wdv: "memory"在以張表中讀取一個值然后在寫到該表的另一個位置。其他內(nèi)嵌匯編作為預處理宏要是經(jīng)常使用使用部分匯編,最好的方法是將它以宏的形式定義在頭文件中。使用該頭文件在嚴格的ANSI模式下會出現(xiàn)警告。為了避免該類問題,可以使用_asm_代替asm,_volatile_代替volatile。這可以等同于別名。下面就是個例
26、程:#define BYTESWAP(val _asm_ _volatile_ ( "eor r3, %1, %1, ror #16nt" "bic r3, r3, #0x00FF0000nt" "mov %0, %1, ror #8nt" "eor %0, %0, r3, lsr #8" : "=r" (val : "0"(val : "r3", "cc" ;C 樁函數(shù)宏定義包含的是相同的代碼。這在大型routine中是不可以接受的。
27、這種情況下最好定義個樁函數(shù)。unsigned long ByteSwap(unsigned long valasm volatile ("eor r3, %1, %1, ror #16nt""bic r3, r3, #0x00FF0000nt""mov %0, %1, ror #8nt""eor %0, %0, r3, lsr #8": "=r" (val: "0"(val: "r3" ;return val;替換C變量的符號名默認的情況下,GCC使用同函
28、數(shù)或者變量相同的符號名。你可以使用asm聲明,為匯編代碼指定一個不同的符號名unsigned long value asm("clock" = 3686400這個聲明告訴編譯器使用了符號名clock代替了具體的值。替換C函數(shù)的符號名為了改變函數(shù)名,你需要一個原型聲明,因為編譯器不接受在函數(shù)定義中出現(xiàn)asm關(guān)鍵字。extern long Calc(void asm ("CALCULATE"調(diào)用函數(shù)calc(將會創(chuàng)建調(diào)用函數(shù)CALCULATE的匯編指令。強制使用特定的寄存器局部變量可能存儲在一個寄存器中。你可以利用內(nèi)嵌匯編為該變量指定一個特定的寄存器。void Count(void register unsigned char counter asm("r3". some code.asm volatile("eor r3, r3, r3". more code.匯編指令“eor r3, r3, r3”,會將r3清零。Waring:該例子在到多數(shù)情況下是有問題的,因為這將和優(yōu)化器相沖突。因為GCC不會預留其它寄存器。要是優(yōu)化器認為該變量在以后一段時間沒有使用,那么該寄存器將會被再次使用。但是編譯器并沒有能力去檢查是否和編譯器預先定義的寄存器有沖突。如果你用這種方式指定了太多的寄存器,編譯器
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度投資理財代理服務(wù)合同
- 二零二五年度吊車安全操作規(guī)程制定及執(zhí)行合同
- 二零二五年度冬季勞務(wù)掃雪環(huán)境保護協(xié)議
- 2025年度正規(guī)貨車駕駛員勞動合同及貨運業(yè)務(wù)操作規(guī)范合同
- 二零二五年度扶貧項目風險防范與應(yīng)急處理合作協(xié)議
- 二零二五年度合同糾紛賠償調(diào)解服務(wù)協(xié)議
- 二零二五年度名人房產(chǎn)銷售代理合同范本
- 2025年度智能制造股權(quán)抵押貸款合同
- 2025年度電子商務(wù)平臺合作解除終止范本
- 二零二五年度企業(yè)勞動合同解除與離職員工就業(yè)援助服務(wù)協(xié)議
- 《概率論與數(shù)理統(tǒng)計》課件第八章 假設(shè)檢驗
- 山東工商學院馬克思主義基本原理期末復習題及參考答案
- 2023年濟南工程職業(yè)技術(shù)學院單招職業(yè)技能考試題庫及答案解析word版
- 文獻檢索與論文寫作-文獻檢索與科技論文寫作138課件
- 10KV開關(guān)柜教學講解課件
- 廢橡膠處理協(xié)議書范本
- 增額終身壽險銷售邏輯
- GB/T 8813-2020硬質(zhì)泡沫塑料壓縮性能的測定
- GB/T 15057.2-1994化工用石灰石中氧化鈣和氧化鎂含量的測定
- 潔凈廠房監(jiān)理實施細則
- 哈工大研究生課程-高等結(jié)構(gòu)動力學-第四章課件
評論
0/150
提交評論