我正在尝试在 Linux (x86) 上学习 GCC 内联汇编,我的第一个实验是尝试为乘法实现整数溢出检测。这似乎很容易,但它有我不明白的副作用。
所以,这里我想将两个无符号的 8 位整数相乘,看看结果是否溢出。基本上我只是将第一个操作数加载到 AL 寄存器中,将另一个操作数加载到 BL 寄存器中,然后使用该mul
指令。结果作为 16 位值存储在 AX 寄存器中。所以我然后将 AX 寄存器中的值复制到我的 C 变量b
中,除非它溢出。如果溢出,我设置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))。我看到的问题是 C 变量a
在乘法之后设置为 0(或在溢出时设置为 1。)我不明白为什么a
我在这里所做的任何事情都会改变。它甚至不在输出变量列表中,那么为什么我的汇编程序会影响 的值a
?
另外,我不确定被破坏的寄存器列表。这个列表应该通知 GCC 任何在汇编例程中使用的寄存器,这样 GCC 就不会试图错误地使用它们。我想我需要通知 GCC 我使用了 AL 和 BL 寄存器,但是 AX 寄存器呢?它被隐式用于存储两个 8 位整数的乘积,所以我需要将它包含在被破坏的寄存器列表中吗?