如何在机器代码级处理数学平等操作符

时间:2016-11-30 01:38:30

标签: machine-code

所以今天我想问一个相当存在的问题,我觉得好像大多数程序员跳过并接受一些有效的东西,而不是真正问“它是如何工作”的问题。问题很简单:> =运算符如何编译成机器代码,该机器代码是什么样的?在最底部,它必须大于测试,混合“相等”测试。但这实际上是如何实现的呢?考虑它似乎相当矛盾,因为在最底层不可能有一个>或==测试。还需要别的东西。我想知道这是什么。

计算机如何测试相等性和大于基本水平?

3 个答案:

答案 0 :(得分:2)

确实没有>==测试。相反,汇编程序中的最低级别比较由binary subtraction起作用。 在x86上,整数比较的操作码是CMP。它实际上是一条指令来统治它们。如何在80386 Programmer's reference manual

中描述它的工作原理
  

CMP从第一个操作数中减去第二个操作数,但与SUB指令不同,它不存储结果;只有标志被改变。

     

CMP通常与条件跳转和SETcc指令一起使用。 (有关提供的有符号和无符号标志测试列表,请参阅附录D.)如果将大于一个字节的操作数与立即数字进行比较,则首先对字节值进行符号扩展。

基本上,CMP A, B(在Intel operand ordering中)会计算A - B,然后会丢弃结果。但是,在x86 ALU中,算术运算根据操作结果在CPU的标志寄存器内设置条件标志。与算术运算相关的标志是

Bit  Name   Function

 0   CF     Carry Flag -- Set on high-order bit carry or borrow; cleared
            otherwise.
 6   ZF     Zero Flag -- Set if result is zero; cleared otherwise.
 7   SF     Sign Flag -- Set equal to high-order bit of result (0 is
            positive, 1 if negative).
11   OF     Overflow Flag -- Set if result is too large a positive number
            or too small a negative number (excluding sign-bit) to fit in
            destination operand; cleared otherwise.

例如,如果计算结果为零,则设置Zero Flag ZFCMP A, B执行A - B并丢弃结果。减法的结果是0 iff A == B。因此,ZF仅在操作数相等时设置,否则清除。

如果无符号减法会导致borrow,则会设置进位标记CF,如果A - B < 0将是A }和B被视为 unsigned 数字和A < B

只要设置了结果的MSB位,就会设置符号标志。这意味着作为带符号数的结果在2的补码中被认为是负数。但是,如果您考虑8位减法01111111(127) - 10000000( - 128),则结果为11111111,其解释为8位有符号2&#39;补码是-1,即使127 - (-128)应为255。发生有符号整数溢出单独的符号标志并不能单独告知哪些有符号数量更大 - OF溢出标志指示在先前的算术运算中是否发生了有符号溢出。

现在,根据使用它的位置,Byte Set on Condition SETccJump if Condition is Met Jcc指令用于解码标志并对其进行操作。如果布尔值用于设置变量,那么聪明的编译器将使用SETcc; Jcc if更适合else ... >=

现在,int a, b; bool r1, r2; unsigned int c, d; r1 = a >= b; // signed r2 = c >= d; // unsigned 有两种选择:要么是签名比较,要么是无符号比较。

r2

在英特尔汇编中,无符号不等式的条件名称使用上方下方;签名平等的条件使用单词更高更少。因此,对于(CF=0),编译器可以决定使用 Set on Above或Equal ,即SETAE,如果r1,则将目标字节设置为1。对于SETGE,结果将由(SF=OF)解码 - 在大或等上设置字节,这意味着#include <stdbool.h> bool gte_unsigned(unsigned int a, unsigned int b) { return a >= b; } - 即,被解释为2的补码的结果为正溢出,或溢出发生时为负。

最后一个例子:

 cmp     edi, esi
 setae   al
 ret

在x86-64 Linux上生成的优化代码是:

bool gte_signed(int a, int b) {
    return a >= b;
}

同样用于签名比较

 cmp     edi, esi
 setge   al
 ret

生成的程序集是

...
events: <?= $response ?>.filter(function(x) { return x.status === "1";}),
...                    

答案 1 :(得分:1)

这是一个简单的C函数:

bool lt_or_eq(int a, int b)
{
    return (a <= b);
}

在x86-64上,GCC将其编译为:

    .file   "lt_or_eq.c"
    .text
    .globl  lt_or_eq
    .type   lt_or_eq, @function
lt_or_eq:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    cmpl    -8(%rbp), %eax
    setle   %al
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   lt_or_eq, .-lt_or_eq

重要的部分是cmpl -8(%rbp), %eax; setle %al;序列。基本上,它使用http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/webob.html指令以数字方式比较两个参数,并根据该比较设置零标志和进位标志的状态。然后,它使用cmp来决定是否将%al 寄存器设置为01,具体取决于这些标志的状态。调用者从%al 寄存器中获取返回值。

答案 2 :(得分:0)

首先,计算机需要确定数据的类型。在像C这样的语言中,这将是在编译时,python将在运行时调度到不同类型的特定测试。假设我们来自一个编译语言,并且我们知道我们比较的值是整数,那么编译器将确保值在寄存器中,然后发出:

ffmpeg -i input.mp4 -vf fps=1/10 out%03d.jpg

减去寄存器,然后检查零/ undflow。这些指令在CPU上运行。 (我在这里假设是ARM式的,有很多变化)。