![](/img/trans.png)
[英]Assembly code/AVX instructions for multiplication of complex numbers. (GCC inline assembly)
[英]GCC Inline Assembly Multiplication
我正在尝试在Linux(x86)上学习GCC内联汇编,我的第一个实验是尝试实现乘法的整数溢出检测。 这似乎很容易,但它有副作用,我不明白。
所以,在这里我想要将两个无符号8位整数相乘,并查看结果是否溢出。 基本上我只是将第一个操作数加载到AL寄存器中,将另一个操作数加载到BL寄存器中,然后使用mul
指令。 结果存储为AX寄存器中的16位值。 然后我将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位整数的乘积,所以我是否需要将它包含在被破坏的寄存器列表中?
我看到的问题是,C变量
a
是乘法后设置为0(或1对溢出)。我不明白为什么a
会被任何东西我在这里做的一切都改变了。 它甚至不是输出变量的名单上,所以为什么我的汇编程序影响的值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位的字节存储。
我想我需要告知GCC我使用了AL和BL寄存器,但AX寄存器怎么样? 它隐式用于存储两个8位整数的乘积,所以我是否需要将它包含在被破坏的寄存器列表中?
是的 - 您应该告诉GCC有关您更改其值的任何寄存器(并且,正如nategoose在另一个答案中指出的那样,您应该告诉它您也在更改标志)。 所以这里的clobber列表应该是"%ax", "%bl", "cc"
(AX包括AL,因此您不需要明确提及AL)。
您应该使用-S
选项编译代码并查看* .s文件。 所有的程序集都在用分号分隔的同一行上,我相信分号在gnu汇编程序中开始注释。 您需要在所有汇编指令的末尾添加“\\ n”(或更好的“\\ n \\ t”)。
您可能还想将“cc”添加到clobber列表中。
此外,GCC还有一种方法可以指定输入既可以是您可能感兴趣的汇编的输入也可以是输出。
此外,最好让GCC决定输入的位置而不是强制它们在内存中。 我似乎记得GCC的内联汇编对x86的约束意味着“无论是在寄存器中还是在内存中”,它可以用于无关紧要的情况,但你可能不应该有很多“mov”指令因为GCC最大的工作之一是在实际计算指令之间找出最好的移动指令集,所以你的内联汇编的开始和结束。 例如,在你的代码中,GCC要做的最好的事情就是将常量10和25存储在你用来开始的寄存器中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.