Reputation: 7610
I would like to ask some help form You! I have a project with a lot of C source. Most of them compiled with gcc, but some compiled with Intel compiler. This later codes have a lot of inline asm codes in Microsoft's MASM
format. I would like to compile the whole project with gcc and modify as few code as I can. So I wrote a perl script which converts the intel format inline asm to GAS format. (BTW: I compile to 32 bit on a 64 bit Linux machine).
My problem that I have to specify for gcc that in the inline asm("...")
which C variables are passed to the code adding the :: [var1] "m" var1, [var2] "m" var2, ...
line at the end.
Is it a way to avoid this explicit specification?
My tryings:
The dummy test C code is simply replaces 4 characters the destination char array with the elements of the source char array (I know this is not the best way to do it. It is just a stupid example).
In the original function there is no explicit specification, but it can be compiled with Intel compiler (shame on me, but I did not test this, but it should work with Intel compiler as I made it based on the real code). LOOP
label is used a lot of times, even in the same C source file.
#include <stdio.h>
void cp(char *pSrc, char *pDst) {
__asm
{
mov esi, pSrc
mov edi, pDst
mov edx, 4
LOOP:
mov al, [esi]
mov [edi], al
inc esi
inc edi
dec edx
jnz LOOP
};
}
int main() {
char src[] = "abcd";
char dst[] = "ABCD";
cp(src, dst);
printf("SRC: '%s', DST: '%s'\n", src, dst);
return 0;
}
Result is: SRC: 'abcd', DST: 'abcd'
The working converted cp
code is (compiled with gcc).
GAS (AT&T) format (compiled: gcc -ggdb3 -std=gnu99 -m32 -o asm asm.c
)
void cp(char *pSrc, char *pDst) {
asm(
"mov %[pSrc], %%esi\n\t"
"mov %[pDst], %%edi\n\t"
"mov $4, %%edx\n\t"
"LOOP%=:\n\t"
"mov (%%esi), %%al\n\t"
"mov %%al, (%%edi)\n\t"
"inc %%esi\n\t"
"inc %%edi\n\t"
"dec %%edx\n\t"
"jnz LOOP%=\n\t"
: [pDst] "=m" (pDst)
: [pSrc] "m" (pSrc)
: "esi", "edi", "edx", "al"
);
}
Intel format (compiled: gcc -ggdb3 -std=gnu99 -m32 -masm=intel -o asm asm.c
)
void cp(char *pSrc, char *pDst) {
asm(".intel_syntax noprefix\n\t");
asm(
"mov esi, %[pSrc]\n\t"
"mov edi, %[pDst]\n\t"
"mov edx, 4\n\t"
"LOOP%=:\n\t"
"mov al, [esi]\n\t"
"mov [edi], al\n\t"
"inc esi\n\t"
"inc edi\n\t"
"dec edx\n\t"
"jnz LOOP%=\n\t"
: [pDst] "=m" (pDst)
: [pSrc] "m" (pSrc)
: "esi", "edi", "edx", "al"
);
asm(".intel_syntax prefix");
}
Both codes are working, but it needs some code modification (inserting the '%' characters, collecting the variables, modifying the jump labels and jump
functions).
I also tried this version:
void cp(char *pSrc, char *pDst) {
asm(".intel_syntax noprefix\n\t");
asm(
"mov esi, pSrc\n\t"
"mov edi, pDst\n\t"
"mov edx, 4\n\t"
"LOOP:\n\t"
"mov al, [esi]\n\t"
"mov [edi], al\n\t"
"inc esi\n\t"
"inc edi\n\t"
"dec edx\n\t"
"jnz LOOP\n\t"
);
asm(".intel_syntax prefix");
}
But it drops
gcc -ggdb3 -std=gnu99 -masm=intel -m32 -o ./asm.exe ./asm.c
/tmp/cc2F9i0u.o: In function `cp':
/home/TAG_VR_20130311/vr/vr/slicecodec/src/./asm.c:41: undefined reference to `pSrc'
/home/TAG_VR_20130311/vr/vr/slicecodec/src/./asm.c:41: undefined reference to `pDst'
collect2: ld returned 1 exit status
Is there a way to avoid the definition of the input arguments and avoid the modifications of local labels?
ADDITION
I tried to use global variable. For this g
constrain has to be used instead of m
.
char pGlob[] = "qwer";
void cp(char *pDst) {
asm(".intel_syntax noprefix\n\t"
"mov esi, %[pGlob]\n\t"
"mov edi, %[pDst]\n\t"
"mov edx, 4\n\t"
"LOOP%=:\n\t"
"mov al, [esi]\n\t"
"mov [edi], al\n\t"
"inc esi\n\t"
"inc edi\n\t"
"dec edx\n\t"
"jnz LOOP%=\n\t"
".intel_syntax prefix" : [pDst] "=m" (pDst) : [pGlob] "g" (pGlob)
: "esi", "edi", "edx", "al");
}
ADDITION#2
I tried
"lea esi, pGlob\n\t" // OK
"lea esi, %[_pGlob]\n\t" // BAD
//"lea esi, pGlob_not_defined\n\t" // BAD
//gcc failed with: undefined reference to `pGlob_not_defined'
compiled to
lea esi, pGlob
lea esi, OFFSET FLAT:pGlob // BAD
//compilation fails with: Error: suffix or operands invalid for `lea'
It seems that only function local variables has to be defined. Global variable may be added to the trailer, but not really necessary. Both are working:
"mov esi, pGlob\n\t" // OK
"mov esi, %[_pGlob]\n\t" // OK
compiled to
mov esi, OFFSET FLAT:pGlob
mov esi, OFFSET FLAT:pGlob
I defined a function local variable. It has to be defined in the constraint part:
void cp(char *pDst) {
char pLoc[] = "yxcv";
asm(".intel_syntax noprefix\n\t"
...
//"mov esi, pLoc\n\t" // BAD
"mov esi, %[_pLoc]\n\t" // OK, 'm' BAD
...
".intel_syntax prefix" : [_pDst] "=m" (pDst) : [_pLoc] "g" (pLoc)
: "esi", "edi", "edx", "al");
Unfortunately it should be determined what are the global and what are the local variables. It is not easy as the asm code can be defined in C
macros and even the surrounding function is not definite. I think only the precompiler has enough information to this. Maybe the code has to be precompiled with gcc -E ...
.
I realized that not defining the output in the constraint part, some code can be eliminated by the optimizer.
TIA!
Upvotes: 4
Views: 7314
Reputation: 25559
Yes, you do need to specify the registers explicitly. GCC will not do that for you. And you can't (generally) put C variable names inside the ASM string.
Your final code block looks perfectly good, to me, but in GCC you don't need to choose which registers to use yourself. You should also use the volatile
keyword to prevent the compiler thinking the code doesn't do anything given that it has no outputs.
Try this:
char pGlob[] = "qwer";
void cp(char *pDst) {
asm volatile (".intel_syntax noprefix\n\t"
"mov edx, 4\n\t"
"LOOP%=:\n\t"
"mov al, [%[pGlob]]\n\t"
"mov [%[pDst]], al\n\t"
"inc %[pGlob]\n\t"
"inc %[pDst]\n\t"
"dec edx\n\t"
"jnz LOOP%=\n\t"
".intel_syntax prefix" :: [pGlob] "g" (pGlob), [pDst] "g" (pDst) : "edx");
}
This way the compiler deals with loading the variables and chooses the registers for you (thereby eliminating a pointless copy from one register to another). Ideally you'd also eliminate the explicit use of edx
, but it's not really necessary here.
Of course, in this silly example I would just recode the whole thing in C and let the compiler do its job.
Upvotes: 5