Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assembly - inline asm - copy from one array to another?

Context :

Linux 64. AT&T.

GCC 4.8.2 (with -O3 -march=native)

The x86_64 abi under my left hand, opened at page 21.

The intended C code :

So that the intent is made clear, here is the idea :

int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};

for (int32_t x = 0; x < 4; ++x) {

    res[x] = primo[x];
}

printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);

The faulty C code :

Error detected :

Error: `(%rsp,%esi,4)' is not a valid base/index expression

The code :

int32_t res[] = {0,0,0,0};
int32_t primo[] = {5,8,50,150};
int32_t counter = 0;

    __asm__ volatile(
        "start_loop:\n\t"
        "movl (%1,%2,4), (%0,%2,4)\n\t"
        "addl $1, %2\n\t"
        "cmp $4, %2\n\t"
        "jne start_loop"
        : "=&r"(res)
        : "r"(primo),"r"(counter)
        :"cc"
        );

printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);

The assembly code (lines underlying the problem):

...
start_loop:
movl (%rsp,%edx,4), (%si,%edx,4)
addl $1, %edx
cmp $4, %edx
jne start_loop
...

The question :

How can I express the right code ? Where did I got it wrong ?

Thanks

Update:

when changing the assembly line to

movl (%rsp,%rdx,4), (%rsi,%rdx,4)

I get

Error: too many memory references for 'mov'

What ??

Update 2

For the readers, it seems that my system is quite special in that it will not put the right instruction size.

I have to manually type my variables with int64_t as an example to force the r*x thing to happen. If c11/c++11 use uintptr_t type.

Otherwise, gcc sticks with the 32 bits version which causes invalid base/index error.

It has bitten me multiple times. I hope it won't for you now.

like image 973
Larry Avatar asked Feb 21 '15 11:02

Larry


1 Answers

Now close the ABI doc, and open the intel manual, basic architecture and the instruction set reference of course :->

First of all, mov does not accept two memory operands, you have to go through a register or use the specialized string move movs. Second, in the effective address you can not mix 16, 32 and 64 bit registers. Given the types in your code fragment, it is highly suspicious that the compiler substituted 16 bit registers for you. Also, it shouldn't even compile due to impossible constraints, res being an array you can't use it as output. Furthermore you are changing the counter variable, but you don't tell the compiler that.

gcc inline assembly is a very complicated thing. Better avoid it if possible, especially if you are a beginner or else you will be fighting the compiler instead of learning assembly.

A fixed version may look like:

#include <stdint.h>
#include <stdio.h>

int main()
{
    int32_t res[] = {0,0,0,0};
    int32_t primo[] = {5,8,50,150};
    int32_t counter = 0;
    int32_t tmp;

    __asm__ volatile(
        "start_loop:\n\t"
        "movl (%3, %q1, 4), %0\n\t"
        "movl %0, (%2, %q1, 4)\n\t"
        "addl $1, %1\n\t"
        "cmp $4, %1\n\t"
        "jne start_loop"
        : "=&r" (tmp), "+r" (counter)
        : "r" (res), "r"(primo)
        : "cc", "memory"
        );

    printf("%d %d %d %d\n", res[0], res[1], res[2], res[3]);
    return 0;
}
like image 99
Jester Avatar answered Sep 21 '22 13:09

Jester