试图将此汇编代码转换为C代码

时间:2016-04-24 00:02:03

标签: gcc assembly x86 gas

# x at %ebp+8, n at %ebp+12
movl 8(%ebp), %esi
movl 12(%ebp), %ebx
movl $-1, %edi
movl $1, %edx
.L2:
movl %edx, %eax
andl %esi, %eax
xorl %eax, %edi
movl %ebx, %ecx
sall %cl, %edx
testl %edx, %edx
jne .L2
movl %edi, %eax

我将上面的代码转换为下面的代码,但我不完全确定它是否正确。

    int loop(int x, int n){

    int result = -1;

    for (mask = 1; mask >= result; mask = x&1) {

    result ^= n;

    }
return result;
}

x和n是存储在%ebp内存中的两个整数,并移动到注册表%esi和%ebx。 Result和Mask的值为-1和1,它来自代码的第一部分 我认为在.L2:之后循环开始,这就是我感到困惑的地方。 最后,结果返回movl %edi, %eax

1 个答案:

答案 0 :(得分:1)

您的代码完全错误。你应该在发布quesetions之前自己做一些测试。

首先,代码中的mask未在您的函数中声明。

然后,在将mask声明为int后,当loop不会通过result变为正数时,函数result ^= n;将陷入无限循环。另一方面,汇编代码不会陷入无限循环,除非n是32的倍数(包括零)。

将代码转换为程序集:

1。我从汇编直接转换为C.

请注意,我使用了无符号类型uint32_t,因为

  • 使用无符号类型,因为当发生溢出或要移位的值为负时,左移操作到有符号整数将导致未定义的行为
  • 使用uint32_t因为unsigned int的大小取决于环境,并且它可能小于32位长,而此处使用的寄存器(%cl除外)是32位长。

引自N1570 6.5.7按位移位运算符:

  

4 E1 << E2的结果是E1左移E2位位置;腾出的位用零填充。如果E1具有无符号类型,则结果的值为E1×2 E2 ,比结果类型中可表示的最大值减少一个模数。如果E1具有签名类型和非负值,并且E1×2 E2 在结果类型中可表示,那么这就是结果值;否则,行为未定义。

另请注意,必须包含stdint.hinttypes.h才能使用uint32_t

在80286或更高版本的x86 CPU中,要移位的宽度被屏蔽为5位长。

uint32_t loop(uint32_t x, uint32_t n) {
    uint32_t esi = x;              /* movl 8(%ebp), %esi         */
    uint32_t ebx = n;              /* movl 12(%ebp), %ebx        */
    uint32_t edi = -1;             /* movl $-1, %edi             */
    uint32_t edx = 1;              /* movl $1, %edx              */
    uint32_t eax, ecx;
    do {                           /* .L2:                       */
        eax = edx;                 /* movl %edx, %eax            */
        eax &= esi;                /* andl %esi, %eax            */
        edi ^= eax;                /* xorl %eax, %edi            */
        ecx = ebx;                 /* movl %ebx, %ecx            */
        edx <<= (ecx & 0xff) & 31; /* sall %cl, %edx             */
    } while (edx != 0);            /* testl %edx, %edx ; jne .L2 */
    eax = edi;                     /* movl %edi, %eax            */
    return eax;
}

2。我引入了变量名来使他们的角色清晰。

uint32_t loop(uint32_t x, uint32_t n) {
    uint32_t result = -1;
    uint32_t mask = 1;
    uint32_t eax, ecx;
    do {
        eax = mask;
        eax &= x;
        result ^= eax;
        ecx = n;
        mask <<= (ecx & 0xff) & 31;
    } while (mask != 0);
    return result;
}

3。我合并了一些表达方式。

uint32_t loop(uint32_t x, uint32_t n) {
    uint32_t result = -1;
    uint32_t mask = 1;
    do {
        result ^= mask & x;
        mask <<= n & 31;
    } while (mask != 0);
    return result;
}

4。我将do循环更改为for循环,因为您尝试使用它。

uint32_t loop(uint32_t x, uint32_t n) {
    uint32_t result = -1;
    uint32_t mask;
    for (mask = 1; mask != 0; mask <<= n & 31) {
        result ^= mask & x;
    }
    return result;
}

完整的测试代码和demo

#include <stdio.h>
#include <inttypes.h>
#include <limits.h>

__asm__ (
/* support both environments that does and doesn't add underscore before function name */
"loop_asm:\n"
"_loop_asm:\n"
"push %ebp\n"
"mov %esp, %ebp\n"
"push %esi\n"
"push %edi\n"
"push %ebx\n"

"# x at %ebp+8, n at %ebp+12\n"
"movl 8(%ebp), %esi\n"
"movl 12(%ebp), %ebx\n"
"movl $-1, %edi\n"
"movl $1, %edx\n"
".L2_test:\n" /* rename .L2 to .L2_test to avoid collision */
"movl %edx, %eax\n"
"andl %esi, %eax\n"
"xorl %eax, %edi\n"
"movl %ebx, %ecx\n"
"sall %cl, %edx\n"
"testl %edx, %edx\n"
"jne .L2_test\n"
"movl %edi, %eax\n"

"pop %ebx\n"
"pop %edi\n"
"pop %esi\n"
"leave\n"
"ret\n"
);

uint32_t loop_asm(uint32_t, uint32_t);

uint32_t loop_convert(uint32_t x, uint32_t n) {
    uint32_t result = -1;
    uint32_t mask;
    for (mask = 1; mask != 0; mask <<= n & 31) {
        result ^= mask & x;
    }
    return result;
}

int mask;
    int loop(int x, int n){

    int result = -1;

    for (mask = 1; mask >= result; mask = x&1) {

    result ^= n;

    }
return result;
}

int main(void) {
    int x, n;
    uint32_t raw, test, conv;
    int miss_count = 0;
    /* search for mismatch in some range */
    for (n = 1; n < 32; n++) {
        uint32_t x_test;
        for (x_test = 0; x_test < UINT32_C(100000); x_test++) {
            if (loop_asm(x, n) != loop_convert(x, n)) {
                printf("mismatch at x=%"PRIu32", n=%d\n", x_test, n);
                if (miss_count < INT_MAX) miss_count++;
            }
        }
    }
    printf("%d mismatch(es) found.\n", miss_count);
    /* print some examples */
    x = 100;
    n = 5;
    raw = loop_asm(x, n);
    conv = loop_convert(x, n);
    printf("loop_asm(%d, %d) = %"PRIu32"\n", x, n, raw);
    printf("loop_convert(%d, %d) = %"PRIu32"\n", x, n, conv);
    fflush(stdout);
    test = loop(x, n);
    printf("loop(%d, %d) = %"PRIu32"\n", x, n, test);
    return 0;
}