ç无符号长长和imulq [英] C unsigned long long and imulq

查看:215
本文介绍了ç无符号长长和imulq的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

随着新的人组装,我用gcc逆向工程。但现在我跑了不知何故有趣的问题:我尝试将两个64位整数的x86-64的。这架C - code如下所示:

As somebody new to assembly, I use gcc for reverse engineering. But now I ran in a somehow funny problem: I try to multiply two 64bit-integer for x86-64. The C - code looks as follows:

unsigned long long 
val(unsigned long long a, unsigned long long b){
    return a*b;
}

和使用GCC编译:

val:
    movq    %rdi, %rax
    imulq   %rsi, %rax
    ret

这可能是违反直觉的使用签署乘法无符号整数,但它为C.

It might be counterintuitive to use signed multiplication for unsigned integers, but it works for C.

不过,我想检查乘法溢出。现在,如果结果比更大的溢出标志设置2 ^ 63-1 (我猜是因为它是有符号的乘法毕竟)。但是,对于64位无符号的结果不大于这将是只要还行 2 ^ 64-1

However, I would like to check the multiplication for overflows. Now, the overflow flag is set if the result is greater than 2^63-1 (I guess because it is signed multiplication after all). But for unsigned 64bit this would be still OK as long as the result is not greater than 2^64-1.

什么是在这种情况下做乘法(组装)的正确方法?

What is the right way to do the multiplication (in assembly) in this case?

推荐答案

在乘以两个值,结果的最低位显著是完全一样的,不管你做无符号数的乘法。所以,如果你乘两个32位值,你会得到一个64位的结果,它的低32位是相同的,乘法运算是带有符号。对于一个64位的乘法,其产生一个128位的结果同样的事情,它的低64位是在这两种情况下是相同的。

When multiplying two values, the least significant bits of the result are exactly the same, whether you do unsigned or signed multiplication. So, if you multiply two 32-bit values, you get a 64-bit result, the low 32-bits of which are the same, whether the multiplication is signed or unsigned. Same thing for a 64-bit multiplication, which produces a 128-bit result, the lower 64-bits of which are identical in both cases.

因此​​,编译器通常使用两种类型的乘法 IMUL 指令(其助记符建议符号乘法),因为它是更灵活的比 MUL ,并普遍较快。而 MUL 来只在一个形式(允许的任意通用寄存器或存储器位置由隐含目的地相乘寄存器AL / AX / EAX / RAX), IMUL 有很多形式,包括一个操作数形式(同 MUL ),两个操作数形式(寄存器或内存&倍;注册或存储区或立即)和三操作数形式(注册或内存与次;立即,并将结果存储在第三个目标寄存器)。更多细节Intel的文档中提供(请参见 86 链接标签的wiki),或 MUL 快速参考和的 IMUL

As such, compilers often use the IMUL instruction (whose mnemonic suggests signed multiplication) for both types of multiplication because it is more flexible than MUL, and generally faster. Whereas MUL comes in only one form (allowing an arbitrary general-purpose register or memory location to be multiplied by the implied destination register AL/AX/EAX/RAX), IMUL has many forms, including a one-operand form (same as MUL), a two-operand form (register or memory × register or memory or immediate), and a three-operand form (register or memory × immediate, storing the result in a third destination register). More details are available in Intel's documentation (see the x86 tag wiki for links), or quick reference for MUL and IMUL.

究其原因,编译器可以使用 IMUL 所有的时间,是因为你扔掉结果的高位脱身。当你做一个32位的&倍; 32位乘法和结果存储在一个32位的变量,整个64位结果的高32位被丢弃。再次,相同的64比特倍; 64位的乘法,该丢弃128位结果的高64位,只留下低64位,这是相同的是否是一个符号或无符号乘法

The reason the compiler can get away with using IMUL all the time is because you throw away the high-order bits of the result. When you do a 32-bit × 32-bit multiplication and store the result in a 32-bit variable, the upper 32-bits of the entire 64-bit result were discarded. Again, same for a 64-bit × 64-bit multiplication, which discards the upper 64-bits of the 128-bit result, leaving only the lower 64-bits, which are the same whether it is a signed or unsigned multiply.

从英特尔说明书引用:

在二,三操作数形式[中IMUL]也可能与无符号运算使用,因为该产品的下半部分是一样的,不管如果操作数是带符号。在CF和标志的,但是,不能被用于确定如果结果的上半部分是非零

The two- and three-operand forms [of IMUL] may also be used with unsigned operands because the lower half of the product is the same regardless if the operands are signed or unsigned. The CF and OF flags, however, cannot be used to determine if the upper half of the result is non-zero.

彼得·科德斯也在他的更大的答案的部分解释了这个很好的一个非常普遍的问题上二进制补码算术运算

Peter Cordes has also explained this very well in a section of his larger answer to a very general question on two's-complement arithmetic operations.

总之,写装配code自己时,您必须决定是否想要做的编译器做同样的事情,并扔掉了产品的高位,或者是否要留住他们。如果你不关心的高位,并假设该操作不会溢出,写在同一code作为编译器。

Anyway, when writing the assembly code yourself, you have to decide whether you want to do the same thing the compiler does and throw away the upper bits of the products, or whether you want to keep them. If you don't care about the upper bits and assume that the operation will not overflow, write the same code as the compiler does.

如果你这样做对高位护理,只需使用 MUL 指令,它设定了CF和标志,如果乘法的产品是不是能适应较大类型的操作数。

If you do care about the upper bits, just use the MUL instruction, which sets the CF and OF flags if the product of the multiplication is larger than can fit into the type of its operands.

mov  rax, QWORD PTR [a]   ; put 64-bit operand 'a' into RAX
mov  rbx, QWORD PTR [b]   ; put 64-bit operand 'b' into RBX
mul  rbx                  ; multiply 'a' * 'b'
; 128-bit result is returned in RDX:RAX (upper-bits:lower-bits)

jo  ProductOverflowed

使用 MUL 在这里几乎可以肯定不是试图找到一种方法,使用 IMUL 更高效和测试高64位之后,以查看它们是否非零(这将指示溢出)。只要具有非predictable分支会把你后面的路在性能上,相比1或2μops你会站在与 IMUL 保存。

Using MUL here is almost certainly more efficient than trying to find a way to use IMUL and testing the high 64-bits afterwards to see if they are non-zero (which would indicate an overflow). Simply having a non-predictable branch would put you way behind in performance, compared to the 1 or 2 μops you would stand to save with IMUL.

这篇关于ç无符号长长和imulq的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆