c - GCC 内联汇编乘法

标签 c assembly x86 inline-assembly integer-overflow

我正在尝试学习 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 位整数的乘积,所以我需要将它包含在被破坏的寄存器列表中吗?

最佳答案

The problem I'm seeing is that the C variable a is set to 0 after the multiplication (or 1 on an overflow.) I don't understand why a would be changed at all by anything I'm doing here. It's not even on the list of output variables, so why is my assembly routine affecting the value of a?

mul %%bl 将 AL(8 位)乘以 BL(8 位),将结果放入 AX(16 位)。

请注意,AL 和 AX 不是单独的寄存器:AL 只是 AX 的低 8 位。

movw %%ax, %0 将 AX(16 位)存储到 b 的地址...这是一个 uint8_t。所以这条指令也用结果的前 8 位覆盖内存中的下一个字节。在这种情况下,该字节恰好是存储 a 值的位置(这解释了为什么 a 在未溢出时被覆盖为 0,而在溢出时被覆盖为 1确实)。

您需要将其替换为 movb %%al, %0,以便仅对结果的低 8 位进行字节存储。

I think I need to inform GCC that I used the AL and BL registers, but what about the AX register? It's used implicitly to store the product of two 8-bit integers, so do I need to include it in the list of clobbered registers?

是的——您应该告诉 GCC 关于您更改其值的任何寄存器(而且,正如 nategoose 在另一个答案中指出的那样,您可能应该告诉它您也在更改标志)。所以这里的 clobber 列表应该是 "%ax", "%bl", "cc"(AX 包括 AL,所以你不需要明确提到 AL)。

关于c - GCC 内联汇编乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3884024/

相关文章:

c - 将windows中的linux静态库与cygwin链接起来

assembly - 使用 3Dh 导致中断只返回 "Acces Denied"

caching - 软件预取是否分配行填充缓冲区(LFB)?

assembly - 有没有办法以编程方式获取当前系统上存在的所有 BIOS 中断列表?

c - 使用变量将元素输入到 char 数组中

java - 带有附加参数的 JNA 回调函数

assembly - 有没有办法在 x86 上使用 MMX/SSE 减去压缩的无符号双字,饱和?

assembly - 在 x86 NASM 汇编语言中离开函数时,堆栈是否会自动弹出?

assembly - 为什么每次迭代的微指令数会随着流负载的增长而增加?

c - 如何编写我自己的 setenv()?