GCC 內嵌匯編允許在 C 代碼中直接編寫匯編指令,並自動處理 C 變量與寄存器/內存操作數的關聯。本文系統梳理語法規則、約束字符、操作數對應關係,並通過實例演示常用模式。
__asm__ [__volatile__] (assem_template
: output_operands /* 輸出部分 */
: input_operands /* 輸入部分 */
: clobbers /* 破壞描述部分 */
);
__volatile__:禁止編譯器優化/重排序,確保指令按序執行;\n\t 分隔占位符 %0~%9 對應冒號後的 C 表達式(按輸出優先、輸入其次順序編號)。每個操作數前有約束字符串:
| 約束 | 含義 | 典型用途 |
|---|---|---|
=r | 輸出,通用寄存器 | 結果變量 |
r | 輸入,通用寄存器 | 參與運算的變量 |
m | 內存操作數 | 內存變量 |
g | 寄存器、內存或立即數 | 通用輸入 |
I | 立即數(特定範圍) | 常數 |
=&r | 早期佔用寄存器(&) | 防止輸入輸出共用寄存器 |
__asm__ __volatile__("hlt");
int result, input = 42;
__asm__ __volatile__("movl %1, %0" : "=r" (result) : "r" (input));
// 等價於 result = input;
static inline void __set_bit(int nr, volatile void *addr)
{
__asm__("btsl %1, %0"
: "=m" (*(volatile long *)addr) // 輸出:內存地址
: "Ir" (nr)); // 輸入:立即數或寄存器
}
btsl 不允許雙操作數均為內存,故 nr 用 Ir 強制為立即數/寄存器。
unsigned long flags;
__asm__ __volatile__("pushfl; popl %0" : "=g" (flags) : : "memory");
// "memory" 告訴編譯器內存可能被修改,防止優化導致錯誤
char low_byte, high_byte;
__asm__("movb %b1, %0" : "=r" (low_byte) : "r" (val)); // 低字節
__asm__("movb %h1, %0" : "=r" (high_byte) : "r" (val)); // 高字節
列出指令隱式修改的寄存器/狀態,常見:
"memory":指令讀寫內存(除輸出操作數外)"cc":修改條件碼標志位(x86 通常隱含)"eax", "ecx" 等movl 要求至少一操作數為寄存器,雙 "m" 會導致非法指令 movl mem, mem& 防止與輸入共用寄存器"=r" 要求變量可寫,無需初始值;"+r"(讀寫)需初始化GCC 內嵌匯編通過模板+約束實現 C 與匯編的安全交互。掌握約束語義、操作數編號規則、clobber 聲明是編寫正確、高效內嵌匯編的關鍵。優先使用內核提供的內聯函數(如 atomic_*, barrier()),僅在必要時手寫匯編。