GNU Cインラインasmを使用する場合は、asmテンプレート内の命令を使用して「手動で」実行するのではなく、制約を使用してコンパイラに必要な場所を指示します。
writechar
およびについては、x86-64 Linuxシステムコール規約(非常に厳密に)に従って、レジスタ内のすべての入力(およびシステムコール用のメモリ内のポイント)を設定するための制約を伴う、テンプレートとしてのreadchar
aのみが必要です。System V ABIの関数呼び出し規約に一致します)。 i386およびx86-64でのUNIXおよびLinuxシステムコールの呼び出し規約は何ですか。"syscall"
char
write(2)
これにより、コンパイラが値を保持している可能性のあるレッドゾーン(RSPより128バイト下)が壊れるのを簡単に回避できます。インラインasmからそれを壊してはいけません(したがって、最初に使用しない限りpush
/pop
は使用できませんsub rsp, 128
:C ++インラインasmでのベースポインタレジスタの使用と、GNU Cインラインasmに関する多くの有用なリンクを参照してください)。コンパイラに通知する方法はありません。それを覆い隠す。を使用してビルドすることもできます-mno-redzone
が、この場合、入出力オペランドの方がはるかに優れています。
私はこれらputchar
と呼ぶのをためらっていますgetchar
。まだバッファリングをサポートしていない独自のstdioを実装している場合はこれを行うことができますが、一部の関数は正しく実装するために入力バッファリングを必要とします。たとえば、scanf
文字を調べてフォーマット文字列と一致するかどうかを確認し、一致しない場合は「未読」のままにする必要があります。ただし、出力バッファリングはオプションです。プライベートバッファとそれを作成する関数、または直接それらの入力ポインタを使用してstdioを完全に実装すると思います。write()
write()
writechar()
:
int writechar(char my_char)
{
int retval; // sys_write uses ssize_t, but we only pass len=1
// so the return value is either 1 on success or -1..-4095 for error
// and thus fits in int
asm volatile("syscall #dummy arg picked %[dummy]\n"
: "=a" (retval) /* output in EAX */
/* inputs: ssize_t read(int fd, const void *buf, size_t count); */
: "D"(1), // RDI = fd=stdout
"S"(&my_char), // RSI = buf
"d"(1) // RDX = length
, [dummy]"m" (my_char) // dummy memory input, otherwise compiler doesn't store the arg
/* clobbered regs */
: "rcx", "r11" // clobbered by syscall
);
// It doesn't matter what addressing mode "m"(my_char) picks,
// as long as it refers to the same memory as &my_char so the compiler actually does a store
return retval;
}
これは、Godboltコンパイラエクスプローラでgcc-O3を使用して非常に効率的にコンパイルされます。
writechar:
movb %dil, -4(%rsp) # store my_char into the red-zone
movl $1, %edi
leaq -4(%rsp), %rsi
movl %edi, %edx # optimize because fd = len
syscall # dummy arg picked -4(%rsp)
ret
@nrzのテストは、ほとんどのレジスタを変更せずに1回だけ設定できるという事実を利用して、その回答の安全でない(レッドゾーンクロバリング)バージョンよりもはるかに効率的main
にインライン化します。syscall
main:
movl $97, %r8d # my_char = 'a'
leaq -1(%rsp), %rsi # rsi = &my_char
movl $1, %edx # len
.L6: # do {
movb %r8b, -1(%rsp) # store the char into the buffer
movl %edx, %edi # silly compiler doesn't hoist this out of the loop
syscall #dummy arg picked -1(%rsp)
addl $1, %r8d
cmpb $123, %r8b
jne .L6 # } while(++my_char < 'z'+1)
movb $10, -1(%rsp)
syscall #dummy arg picked -1(%rsp)
xorl %eax, %eax # return 0
ret
readchar()、同じ方法で行います:
int readchar(void)
{
int retval;
unsigned char my_char;
asm volatile("syscall #dummy arg picked %[dummy]\n"
/* outputs */
: "=a" (retval)
,[dummy]"=m" (my_char) // tell the compiler the asm dereferences &my_char
/* inputs: ssize_t read(int fd, void *buf, size_t count); */
: "D"(0), // RDI = fd=stdin
"S" (&my_char), // RDI = buf
"d"(1) // RDX = length
: "rcx", "r11" // clobbered by syscall
);
if (retval < 0) // -1 .. -4095 are -errno values
return retval;
return my_char; // else a 0..255 char / byte
}
発信者は、をチェックすることでエラーをチェックできますc < 0
。