Linux (x86) で GCC インライン アセンブリを学習しようとしています。最初の実験は、乗算の整数オーバーフロー検出を実装することでした。簡単に思えますが、理解できない副作用があります。
したがって、ここでは 2 つの符号なし 8 ビット整数を乗算し、結果がオーバーフローするかどうかを確認します。基本的に、最初のオペランドを AL レジスタにロードし、もう 1 つのオペランドを BL レジスタにロードしてから、mul
命令を使用します。結果は 16 ビット値として AX レジスタに格納されます。b
そのため、オーバーフローしない限り、AX レジスタの値を C 変数にコピーします。オーバーフローした場合はc
1 に設定します。
uint8_t a = 10;
uint8_t b = 25;
uint8_t c = 0; // carry flag
__asm__
(
"clc;" // Clear carry flag
"movb %3, %%al;" // Load b into %al
"movb %2, %%bl;" // Load a into %bl
"mul %%bl;" // Multiply a * b (result is stored in %ax)
"movw %%ax, %0;" // Load result into b
"jnc out;" // Jump to 'out' if the carry flag is not set
"movb $1, %1;" // Set 'c' to 1 to indicate an overflow
"out:"
:"=m"(b), "=m"(c) // Output list
:"ir"(a), "m"(b) // Input list
:"%al", "%bl" // Clobbered registers (not sure about this)
);
これはうまくいくようです。printf
'b' の値が 250 である場合、これは正しいです。また、「b」の開始値を 26 に変更すると、乗算後c
に 1 に設定され、もちろん (10 * 26 > ~uint8_t(0)) によるオーバーフローを示します。私が見ている問題はa
、乗算後に C 変数が 0 (またはオーバーフローで 1) に設定a
されていることです。出力変数のリストにも載っていないのに、アセンブリ ルーチンが の値に影響するのはなぜa
ですか?
また、破壊されたレジスタのリストについてもよくわかりません。このリストは、GCC がそれらを誤って使用しようとしないように、アセンブリ ルーチン中に使用されたレジスタについて GCC に通知することになっています。AL レジスタと BL レジスタを使用したことを GCC に通知する必要があると思いますが、AX レジスタはどうでしょうか。2 つの 8 ビット整数の積を格納するために暗黙的に使用されているため、破壊されたレジスタのリストに含める必要がありますか?