汇编代码 - 循环中的异或如何工作? [复制]

Posted

技术标签:

【中文标题】汇编代码 - 循环中的异或如何工作? [复制]【英文标题】:Assembly Code - How does xor in a loop works? [duplicate] 【发布时间】:2016-09-12 01:30:56 【问题描述】:

我有这个汇编代码,其中有一个 for 循环,我将把它改回 C 代码。但是,我注意到循环中有一个异或。

.L3:
        movq    -8(%rbp), %rax      
        andl    $1, %eax    
        xorl    %eax, -12(%rbp)     
        sarq    -8(%rbp)        
.L2:
        cmpq    $0, -8(%rbp)        
        jg      .L3         

所以我知道一个 for 循环会不断循环,只要它大于 0 并且每个循环都除以 2。但是我遇到的问题是andl和xorl。我知道它用 and 检查 1 和 eax 并根据它们的值返回 1 或 0,但是循环如何更改 xor?

【问题讨论】:

logic gate AND/OR/XOR/NOT/... ... 等等 x86 mov al,0b1010 xor al,0b0011 将导致 al = 0b1001。 ... and al,0b0011 而不是 xor 会产生 al = 0b0010。而or al,0b0011 将产生al = 0b1011not al 将以 al = 0b11110101 结尾(在其他示例中我没有写那些较高的 4 位零,但在 not 之后它们也被反转了)(将值写在彼此上方以便更好地查看和比较它与“门”文章) Eww,为什么所有的内存操作数?我没有看到 RBP 的指针增量发生,所以 IDK 为什么该代码不只是对所有内容使用寄存器。我假设来自gcc -O0 :( 【参考方案1】:

假设局部变量b 位于-8(%rbp),局部变量c 位于-12(%rbp)

.L3:    movq    -8(%rbp), %rax
        andl    $1, %eax

eax 的值设置为b 的最低有效位的值。

xorl    %eax, -12(%rbp) 

cb 的最低有效位执行异或,将结果存储在c 中。

sarq    -8(%rbp)

b 除以2

cmpq    $0, -8(%rbp)
jg      .L3

如果b 大于0,则返回循环开始,否则继续。

所以对应的C代码是:

do 
    c ^= (b & 1);
    b /= 2;           //  Or: b >>= 1;
 while ( b > 0 );

虽然.L2 标签的存在表明之前可能有一个jmp .L2,但您没有向我们展示,在这种情况下它将是一个while 循环:

while ( b > 0 ) 
    c ^= (b & 1);
    b /= 2;           //  Or: b >>= 1;

一个工作演示(在 OS X 上使用 gas 汇编器):

asm_func.S:

.globl  _asm_func

.text

_asm_func:
    push    %rbp
    mov     %rsp, %rbp
    sub     $16, %rsp

    movq    %rdi, -8(%rbp)
    movl    %esi, -12(%rbp)

    jmp     .L2

.L3:
    movq    -8(%rbp), %rax
    andl    $1, %eax
    xorl    %eax, -12(%rbp)
    sarq    -8(%rbp)

.L2:
    cmpq    $0, -8(%rbp)
    jg      .L3

    movl    -12(%rbp), %eax

    leave
    ret 

main.c:

#include <stdio.h>

int asm_func(int b, int c);

int c_func(int b, int c)

    while ( b > 0 ) 
        c ^= (b & 1);
        b >>= 1;
    
    return c;


int main(void)

    for ( int i = 112; i < 127; i += 7 ) 
        for ( int j = 203; j > 182; j -= 9 ) 
            printf("C function  (%d, %d): %d\n", i, j, c_func(i, j));
            printf("Asm function(%d, %d): %d\n", i, j, asm_func(i, j));
        
    
    return 0;

Makefile:

prog: main.o asm_func.o
    cc -o prog main.o asm_func.o

main.o: main.c
    cc -o main.o main.c -c -std=c99 -pedantic -Wall -Wextra

asm_func.o: asm_func.S
    as -o asm_func.o asm_func.S

带输出:

paul@horus:~/src/sandbox/so_asm$ ./prog
C function  (112, 203): 202
Asm function(112, 203): 202
C function  (112, 194): 195
Asm function(112, 194): 195
C function  (112, 185): 184
Asm function(112, 185): 184
C function  (119, 203): 203
Asm function(119, 203): 203
C function  (119, 194): 194
Asm function(119, 194): 194
C function  (119, 185): 185
Asm function(119, 185): 185
C function  (126, 203): 203
Asm function(126, 203): 203
C function  (126, 194): 194
Asm function(126, 194): 194
C function  (126, 185): 185
Asm function(126, 185): 185
paul@horus:~/src/sandbox/so_asm$ 

【讨论】:

我被告知不要包含不必要的代码,所以我没有包含开头。但是是的,在 main 中有一个跳转到 L2 的调用。 b%1 是如何工作的?只比较最小的位? b &amp; 1 只是一个常规的按位与。它评估为1b 是奇数,如果b 是偶数则评估为0 感谢您的示例! @JMei:顺便说一句,这看起来像奇偶校验计算:所有位的水平异或。请参阅this Q&A,了解如何通过将上半部分和下半部分异或到一个字节来更有效地计算奇偶校验。重复到一位,或者只读取 PF,因为 x86 已经计算了奇偶校验。 哇,我刚刚注意到这个函数使用有符号整数比较。这太奇怪了,它会返回除设置了高位(符号位)的整数的奇偶校验之外的东西。我想知道在原始 C 源代码中是否有意使逆向工程变得更加棘手?

以上是关于汇编代码 - 循环中的异或如何工作? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SIMD 加速两个内存块的异或?

java引用之间的异或操作

找出第 K 大的异或坐标值--力扣

LeetCode刷题1863-简单-找出所有子集的异或和

LeetCode刷题1863-简单-找出所有子集的异或和

如何不利用一个额外的变量来达到交换两个变量值的目的-------位上的异或运算