内联汇编
外观
内联汇编(英语:Inline assembly)是部分编译器支持的一种功能。其将非常低端的汇编语言内嵌在高级语言源始码中。实施内联汇编通常是为了以下理由:
- 执行效率优化:将算法中最攸关性能的部分使用手写组语取代高阶代码,优点是不会受到编译器的限制。
- 使用处理器特有指令:某些处理器提供特定的指令,比如Compare-and-swap和Test-and-set指令可以直接用以实现信号机制。因为多工系统都需要信号机制,几乎所有现代的处理器都支持前述的两个指令。其它的一些指令集如SPARC架构的VIS指令集,Intel处理器的MMX指令集以及SSE指令集等。
- 系统调用(System Call):高级语言鲜少提供直接调用system calls的机制,故使用组语来进行这项工作。
使用处理器持有指令优化示例
[编辑]下面是一段在D语言进行内联汇编的代码。该代码使用x86架构的浮点运算器指令来计算。此实现快于编译器产生的一系列浮点数运算,内联汇编也使编程人员得以使用fldpi
指令来加载在x86架构下可得到的最佳之pi估计值。
// 計算tan(x)
real tan(real x)
{
asm
{
fld x[EBP] ; // 將x值載入浮點運算器的堆疊上
fxam ; // 測試堆疊頂端的值是否是合法、可計算浮點數
fstsw AX ;
sahf ;
jc trigerr ; // 若x是NAN,正負無限,或空值
// 387可以處理denormals數值
SC18: fptan ; // 為與8087運算器相容fptan會使堆疊頂端為1.0,再來才是tan值,
fstp ST(0) ; // 丟棄堆疊頂端的值
fstsw AX ;
sahf ;
jnp Lret ; // C2為FPU狀態變數,C2 == 1表示x超出允許的範圍
; // tan之週期為pi,下面的程式碼就是將x縮至允許的範圍
fldpi ;
fxch ;
SC17: fprem1 ;
fstsw AX ;
sahf ;
jp SC17 ;
fstp ST(1) ; // 將pi值移出堆疊
jmp SC18 ;
}
trigerr:
return real.nan;
Lret:
;
}
系统调用(system calls)示例
[编辑]在保护模式运行的应用程序无法直接调用专属于OS的功能。因为OS的行程空间包含核心空间(kernel mode)与用户空间(user mode);运行在用户空间的程序只能透过中断来引用专属于操作系统的功能。通常高级语言都不提供这项功能,所以要运用内联汇编将调用system calls的过程包装为高级语言可辨认、调用的函数。
下面的C语言片段即含有一个system call的包装函数。其组语语法为GNU汇编器使用的AT&T组语语法。一般这类程序都会使用宏实现,不过为了清楚展示观念,在此列出了完整的代码。
GNU汇编器的内联汇编语法相当直觉,基本型式如下:
asm("assembly code");
例如:
asm("movl %ecx, %eax"); /* 複製ecx暫存器的內容至eax暫存器*/
或
__asm_("movb %bh, (%eax)"); /* 從bh暫存器複製1位元組的資料到eax暫存器所指的記憶體區塊*/
必须注意的是,AT&T语法中的操作数顺序与Windows平台下常用的MASM刚好相反。
asm
跟__asm__
都是合法型式;当asm
与代码中某些变量命名起冲突时可使用后者。
extern int errno;
int funcname(int arg1, int *arg2, int arg3)
{
int res;
__asm__ volatile(
"int $0x80" /* 向OS拋出請求*/
: "=a" (res) /* 請編譯器將結果將儲存於eax ("a")*/
"+b"(arg1), /* 請編譯器將arg1存於ebx ("b")*/
"+c"(arg2), /* 請編譯器將arg2存於ecx ("c")*/
"+d"(arg3) /* 請編譯器將arg3存於edx ("d")*/
: "a"(128) /* 該system call的編號放於eax ("a")*/
: "memory", "cc"); /* 通知編譯器,記憶體與condition code register(決定分支的暫存器)己被更改*/
/* 若OS回傳負值表示錯誤;則包裝函式要設定errno global variable並回傳 -1 */
if (-125 <= res && res < 0) {
errno = -res;
res = -1;
}
return res;
}