简体   繁体   English

GCC内联汇编乘法

[英]GCC Inline Assembly Multiplication

I'm trying to learn GCC inline assembly on Linux (x86), and my first experiment was to try and implement integer overflow detection for multiplication. 我正在尝试在Linux(x86)上学习GCC内联汇编,我的第一个实验是尝试实现乘法的整数溢出检测。 It seems easy enough, but it is having side effects which I don't understand. 这似乎很容易,但它有副作用,我不明白。

So, here I want to multiply two unsigned 8-bit integers, and see if the result overflows. 所以,在这里我想要将两个无符号8位整数相乘,并查看结果是否溢出。 Basically I just load the first operand into the AL register and the other operand into the BL register, and then use the mul instruction. 基本上我只是将第一个操作数加载到AL寄存器中,将另一个操作数加载到BL寄存器中,然后使用mul指令。 The result is stored as a 16-bit value in the AX register. 结果存储为AX寄存器中的16位值。 So I then copy the value in the AX register to my C variable b , unless it overflows. 然后我将AX寄存器中的值复制到我的C变量b ,除非它溢出。 If it overflows I set c to 1. 如果它溢出我将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)
 );

This seems to work fine. 这似乎工作正常。 If I printf the value of 'b' I get 250, which is correct. 如果我printf 'b'的值,我得到250,这是正确的。 Also, if I change the starting value of 'b' to 26, then after the multiplication c is set to 1, indicating an overflow because of course (10 * 26 > ~uint8_t(0)). 另外,如果我将'b'的起始值更改为26,那么在乘法c设置为1之后,当然会指示溢出(10 * 26> ~uint8_t(0))。 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. 我看到的问题是,C变量a是乘法后设置为0(或1对溢出)。我不明白为什么a会被任何东西我在这里做的一切都改变了。 It's not even on the list of output variables, so why is my assembly routine affecting the value of a ? 它甚至不是输出变量的名单上,所以为什么我的汇编程序影响的值a

Also, I'm unsure about the clobbered registers list. 此外,我不确定被破坏的寄存器列表。 This list is supposed to inform GCC about any registers which were used during the assembly routine, so that GCC doesn't try to use them incorrectly. 该列表应该告知GCC有关在汇编例程中使用的任何寄存器,以便GCC不会尝试错误地使用它们。 I think I need to inform GCC that I used the AL and BL registers, but what about the AX register? 我想我需要告知GCC我使用了AL和BL寄存器,但AX寄存器怎么样? 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? 它隐式用于存储两个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. 我看到的问题是,C变量a是乘法后设置为0(或1对溢出)。我不明白为什么a会被任何东西我在这里做的一切都改变了。 It's not even on the list of output variables, so why is my assembly routine affecting the value of a ? 它甚至不是输出变量的名单上,所以为什么我的汇编程序影响的值a

mul %%bl multiplies AL (8 bits) by BL (8 bits), placing the result in AX (16 bits). mul %%bl将AL(8位)乘以BL(8位),将结果置于AX(16位)。

Note that AL and AX are not separate registers: AL is just the bottom 8 bits of AX. 注意,AL和AX不是单独的寄存器:AL只是AX的底部8位。

movw %%ax, %0 stores AX (16 bits) to the address of b ... which is a uint8_t . movw %%ax, %0将AX(16位)存储到b ...的地址,即uint8_t So this instruction is also overwriting the next byte in memory with the top 8 bits of the result. 因此,该指令也会用结果的前8位覆盖存储器中的下一个字节。 In this case, that byte happens to be where the value of a is stored (which explains why a is overwritten with 0 when it doesn't overflow, and 1 when it does). 在这种情况下,该字节恰好是存储a的值的位置(这解释了为什么a在没有溢出时被0覆盖,而在它溢出时为1)。

You need to replace that with movb %%al, %0 , to do just a byte store of the bottom 8 bits of the result. 您需要将其替换为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? 我想我需要告知GCC我使用了AL和BL寄存器,但AX寄存器怎么样? 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? 它隐式用于存储两个8位整数的乘积,所以我是否需要将它包含在被破坏的寄存器列表中?

Yes - you should tell GCC about any register that you change the value of (and, as nategoose pointed out in another answer, you should probably tell it that you're changing the flags as well). 是的 - 您应该告诉GCC有关您更改其值的任何寄存器(并且,正如nategoose在另一个答案中指出的那样,您应该告诉它您也在更改标志)。 So the clobber list here should be "%ax", "%bl", "cc" (AX includes AL, so you don't need to mention AL explicitly). 所以这里的clobber列表应该是"%ax", "%bl", "cc" (AX包括AL,因此您不需要明确提及AL)。

You should compile your code with the -S option and have a look at the *.s file. 您应该使用-S选项编译代码并查看* .s文件。 All of your assembly is on the same line separated by semicolons, and I believe that semicolons begin comments in gnu assembler. 所有的程序集都在用分号分隔的同一行上,我相信分号在gnu汇编程序中开始注释。 You'll need to add "\\n" (or better yet "\\n\\t") to the ends of all of your assembly instructions. 您需要在所有汇编指令的末尾添加“\\ n”(或更好的“\\ n \\ t”)。

You might also want to add "cc" to the clobber list. 您可能还想将“cc”添加到clobber列表中。

Additionally, GCC has a way to specify that input is both an input and an output to assembly which you might be interested in. 此外,GCC还有一种方法可以指定输入既可以是您可能感兴趣的汇编的输入也可以是输出。

Also, it is probably best if let GCC decide where the inputs to this are rather than forcing them be in memory. 此外,最好让GCC决定输入的位置而不是强制它们在内存中。 I seem to remember that GCC's inline assembly has a constraint on x86 that means "either in a register or in memory" that can be used for circumstances where it doesn't matter, but you probably shouldn't have many "mov" instructions at the beginning and ends of your inline assembly because one of GCC's biggest jobs is figuring out the best sets of move instructions to put between actual calculating instructions. 我似乎记得GCC的内联汇编对x86的约束意味着“无论是在寄存器中还是在内存中”,它可以用于无关紧要的情况,但你可能不应该有很多“mov”指令因为GCC最大的工作之一是在实际计算指令之间找出最好的移动指令集,所以你的内联汇编的开始和结束。 For instance, in your code the best thing for GCC to do would be to just store the constants 10 and 25 in the registers that you are using to begin with. 例如,在你的代码中,GCC要做的最好的事情就是将常量10和25存储在你用来开始的寄存器中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM