在 Zig 中使用匯編輸出 Hello- World-

Zig 中的內聯彙編

儘管現代高級語言的特性已經非常豐富,但我們仍需要彙編語言的幫助,在某些特殊的場景下,彙編語言可以發揮出比高級語言更好的性能,這是因爲彙編語言更加接近硬件,它允許我們對硬件直接進行操作。

一般是在以下場景下,纔會涉及到使用匯編語言:

對於 x86 和 x86_64 ,當前彙編語法爲 AT&T 語法,而不是更流行的 Intel 語法。這是由於技術限制,彙編解析由 LLVM 提供,其對 Intel 語法的支持存在 bug 且測試 結果並不理想。

原文地址:https://zigcc.github.io/zig-course/advanced/assembly.html

以下簡單介紹一下兩種在 Zig 中使用匯編的方式:

全局彙編

Zig 支持在容器中使用 comptime + asm 包裹表達式,以此聲明一段全局彙編,可以通過此方法直接引入彙編代碼。

以下是一段通過全局彙編功能實現在 x86_64-linux 下輸出 "Hello, World!\n" 的代碼:

comptime {
    asm (
        \\  msg:
        \\      .ascii "Hello, World!\n"
        \\  len = . - msg
        \\
        \\.global hello;
        \\.type hello, @function;
        \\hello:
        \\  # write syscall
        \\  mov $1, %rax
        \\  # file descriptor (stdout)
        \\  mov $1, %rdi
        \\  # address of string to output
        \\  lea msg(%rip), %rsi
        \\  # length of string to output
        \\  mov $len, %rdx
        \\  syscall
        \\ retq
    );
}
extern fn hello() usize;
pub fn main() !void {
    _ = hello();
}

在以上代碼中,我們通過使用 .global 聲明一個全局符號,使用 .type 聲明一個 hello 函數,隨後便是爲 syscall 提供參數,rax 寄存器保存系統調用號, rdi, rsi, rdx, r10, r8, r9 則存儲參數。

完整的系統調用號信息可以在此處查詢:Linux System Call Table[1] 。

我們使用 1 號系統調用,並按照以下順序傳遞參數:標準輸出 1、字符串地址(使用 lea 指令獲取到有效地址)、字符串長度。

內聯彙編

內聯彙編給予了我們可以將 low-level 的彙編代碼和高級語言相組合,實現更加高效或者更直白的操作。

Zig 的內聯彙編語法所採用的是 GNU 語法,並且約束語義採取的是類似 LLVM 和 GCC 的方案。

以下是實現代碼:

const str = "Hello, World!\n";
pub fn main() !void {
    _ = asm volatile ("syscall"
        : [ret] "={rax}" (-> usize),
        : [number] "{rax}" (1),
          [arg1] "{rdi}" (1),
          [arg2] "{rsi}" (str),
          [arg3] "{rdx}" (str.len),
        : "rcx", "r11"
    );
}

很簡單地通過 asm 定義了一個內聯彙編,使用 ret 作爲返回值的標識符(numberarg1arg2arg3同理),將執行完 syscall 指令後 rax 寄存器的值作爲返回值,隨後將參數分別賦值給對應的寄存器,在最後的約束中rcxr11 表示會被使用的寄存器(syscall 會將下一條指令地址存入 rcx,將 rflags 存入 r11)。

更多的內聯彙編約束語義,可以參考:LLVM documentation[2]、GCC documentation[3]。

參考資料

[1]

Linux System Call Table: https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86_64-64_bit

[2]

LLVM documentation: http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string

[3]

GCC documentation: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/benRXj7CMSyDDyXOdEimag