Search code examples
gccassemblyx86gnu-assembler

Trying to convert this Assembly code to C code


# 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

I converted the above code to the below code, but i am not completely sure if it is correct.

    int loop(int x, int n){

    int result = -1;

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

    result ^= n;

    }
return result;
}

x and n are two integers stored in %ebp memory and are moved to registry %esi and %ebx. Result and Mask have a value of -1 and 1 and that is from the first part of the code I think after .L2: the loop starts and that is where i get confused. At the end result is returned movl %edi, %eax


Solution

  • Your code is completely wrong. You should have done some test by yourself before posting quesetions.

    First of all, mask in your code is not declared in your function.

    Then, after declareing mask as int, the function loop will fall into an infinite loop when result won't become positive via result ^= n;. On the other hand, the assembly code won't fall into an infinite loop unless n is multiple of 32 (including zero).

    To convert the code to assembly:

    1. I did direct conversion from assembly to C.

    Note that I used unsigned type uint32_t because

    • Use unsigned type because left shift operation to signed integer will cause undefined behavior when overflow occures or the value to be shifted is negative.
    • Use uint32_t because size of unsigned int is dependent to environments and it may be less than 32-bit long while registers used here (except for %cl) are 32-bit long.

    Quote from N1570 6.5.7 Bitwise shift operators:

    4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 × 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

    Also note that stdint.h or inttypes.h has to be included to use uint32_t.

    The width to shift is masked to 5-bit long in x86 CPUs that is 80286 or later.

    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. I introduced variable names to make their roles clear.

    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. I merged some expressions.

    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. I changed do loop to for loop because your attempt uses it.

    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;
    }
    

    Full code for testing and 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;
    }