我正在学习Linux(x86)上的GCC内联汇编,我的第一个实验是尝试为乘法实现整数溢出检测。这似乎很容易,但它会产生我不理解的副作用。
所以,我想要将两个无符号8位整数相乘,并查看结果是否溢出。基本上,我只需将第一个操作数加载到AL寄存器中,将另一个操作数加载到BL寄存器中,然后使用mul指令。结果存储为16位值在AX寄存器中。因此,我将AX寄存器中的值复制到我的C变量b中,除非它溢出。如果它溢出,我将c设置为1。
这似乎运行得很好。如果我打印“b”的值,我会得到250,这是正确的。此外,如果我将“b”的起始值更改为26,那么在乘法后,“c”将设置为1,表示溢出,因为当然(10 * 26 > ~uint8_t(0))。我看到的问题是,在乘法后,C变量“a”被设置为0(或在溢出时为1)。我不明白为什么我的任何操作都会改变“a”的值。它甚至不在输出变量列表中,所以为什么我的汇编程序会影响“a”的值?
另外,我对被破坏的寄存器列表不确定。这个列表应该通知GCC有关在汇编程序期间使用的任何寄存器,以便GCC不会尝试不正确地使用它们。我认为我需要告诉GCC我使用了AL和BL寄存器,但AX寄存器呢?它隐式用于存储两个8位整数的乘积,所以我需要将它包含在破坏寄存器列表中吗?
所以,我想要将两个无符号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)
);
这似乎运行得很好。如果我打印“b”的值,我会得到250,这是正确的。此外,如果我将“b”的起始值更改为26,那么在乘法后,“c”将设置为1,表示溢出,因为当然(10 * 26 > ~uint8_t(0))。我看到的问题是,在乘法后,C变量“a”被设置为0(或在溢出时为1)。我不明白为什么我的任何操作都会改变“a”的值。它甚至不在输出变量列表中,所以为什么我的汇编程序会影响“a”的值?
另外,我对被破坏的寄存器列表不确定。这个列表应该通知GCC有关在汇编程序期间使用的任何寄存器,以便GCC不会尝试不正确地使用它们。我认为我需要告诉GCC我使用了AL和BL寄存器,但AX寄存器呢?它隐式用于存储两个8位整数的乘积,所以我需要将它包含在破坏寄存器列表中吗?
%al
和%bl
中并标记它们被破坏,而应该设置约束条件,让gcc一开始就将参数放入正确的寄存器中。 - R.. GitHub STOP HELPING ICE