簡體   English   中英

包含內聯C代碼的程序集如何工作?

[英]How does including assembly inline with C code work?

我已經看到了Arduino和其他硬件的代碼,這些硬件具有與C內聯的匯編,類似於以下內容:

asm("movl %ecx %eax"); /* moves the contents of ecx to eax */
__asm__("movb %bh (%eax)"); /*moves the byte from bh to the memory pointed by eax */

這實際上是如何工作的 我意識到每個編譯器都不同,但是這樣做的常見原因是什么,以及如何利用這個?

內聯匯編程序代碼直接進入完整的匯編代碼,並且是一體的。 當您真正需要完全控制指令序列時,或者當您無法讓優化程序使用您的代碼時,就可以執行此操作。 也許你需要每一個時鍾滴答。 也許您需要代碼的每個分支都采用完全相同的時鍾周期數,並使用NOP來實現這一點。

在任何情況下,有很多理由可能有人想要這樣做,但你真的需要知道你在做什么。 這些代碼塊對於編譯器來說是非常不透明的,如果你做的不好,你可能不會得到任何警告。

通常編譯器只會將匯編程序指令插入其生成的匯編程序輸出中。 它會這樣做而不考慮后果。

例如,在此代碼中,優化器正在執行復制傳播,從而看到y = x,然后z = y。 因此它用z = x替換z = y,希望這將允許它執行進一步的優化。 但是,它沒有發現我在平均時間內弄亂了x的值。

char x=6;
char y,z;

y=x;                 // y becomes 6

_asm                    
    rrncf x, 1       // x becomes 3. Optimiser doesn't see this happen!
_endasm  

z=y;                 // z should become 6, but actually gets
                     // the value of x, which is 3

為了解決這個問題,您實際上可以告訴優化器不要對此變量執行此優化。

volatile char x=6;   // Tell the compiler that this variable could change
                     // all by itself, and any time, and therefore don't
                     // optimise with it.
char y,z;

y=x;                 // y becomes 6

_asm                    
    rrncf x, 1       // x becomes 3. Optimiser doesn't see this happen!
_endasm  

z=y;                 // z correctly gets the value of y, which is 6

從歷史上看,C編譯器生成匯編代碼,然后由匯編程序將其轉換為機器代碼。 內聯匯編作為一個簡單的特性出現 - 在中間匯編代碼中,在那時,注入一些用戶選擇的代碼。 一些編譯器直接生成機器代碼,在這種情況下,它們包含匯編程序或調用外部匯編程序來生成內聯匯編代碼段的機器代碼。

匯編代碼最常見的用途是使用編譯器無法生成的專用處理器指令。 例如,禁止中斷的一個關鍵部分,控制處理器的功能(高速緩存,MMU,MPU,電源管理,查詢CPU的能力,...),訪問協處理器和硬件外圍設備(如inb / outb的x86指令),等你很少找到asm("movl %ecx %eax") ,因為它會影響它周圍的C代碼也在使用的通用寄存器,但是類似於asm("mcr p15, 0, 0, c7, c10, 5")有它的用途(ARM上的數據存儲器障礙)。 OSDev wiki有幾個帶代碼片段的例子。

匯編代碼對於實現破壞C的流控制模型的功能也很有用。 一個常見的例子是線程之間的上下文切換(無論是協作還是搶占,無論是否在相同的地址空間中),需要匯編代碼來保存和恢復寄存器值。

匯編代碼對於手動優化代碼的小內存或速度也很有用。 隨着編譯器越來越智能化,這在現在的應用程序層面很少有用,但它在大多數嵌入式領域仍然具有相關性。

將裝配與C組合有兩種方式:使用內聯裝配,或通過將裝配模塊與C模塊鏈接。 鏈接可以說更清晰但並不總是適用:有時你需要在函數中間使用一條指令(例如,在上下文切換中保存寄存器,函數調用會破壞寄存器),或者你不想支付成本一個函數調用。

大多數C編譯器都支持內聯匯編,但語法各不相同。 它通常由關鍵字asm_asm__asm asm__asm__引入。 除了匯編代碼本身之外,內聯匯編構造還可以包含允許您在匯編和C之間傳遞值的其他代碼(例如,請求將局部變量的值復制到條目上的寄存器),或者聲明匯編代碼破壞或保留某些寄存器。

asm(“”)__asm__都是有效用法。 基本上,如果關鍵字asm與程序中的某些內容沖突,則可以使用__asm__ 如果您有多個指令,則可以用雙引號在每行寫一個,並在指令后面添加'\\ n''\\ t' 這是因為gcc將每條指令作為字符串發送到(GAS),並使用換行符/選項卡,您可以將正確格式化的行發送到匯編程序。 您問題中的代碼段是基本內聯

基本的內聯匯編中 ,只有說明 擴展裝配中 ,您還可以指定操作數 它允許您指定輸入寄存器,輸出寄存器和破壞寄存器列表。 指定要使用的寄存器不是強制性的,您可以將其保留給GCC,這可能更適合GCC的優化方案。 擴展asm的一個例子是:

__asm__ ("movl %eax, %ebx\n\t"
           "movl $56, %esi\n\t"
           "movl %ecx, $label(%edx,%ebx,$4)\n\t"
           "movb %ah, (%ebx)");

請注意除了最后一行之外的每行末尾的'\\ n \\ t' ,每行用引號括起來。 這是因為gcc將每個作為指令發送到我之前提到的字符串。 需要換行/標簽組合,以便按照正確的格式輸入行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM