如何将变量传递给使用gcc编译的英特尔内联asm代码

Tru*_*ueY 4 c assembly gcc

我想问一下你的帮助!我有一个包含大量C源代码的项目.他们中的大多数使用gcc编译,但有些是使用英特尔编译器编译的.后来的代码在Microsoft的MASM格式中有很多内联asm代码.我想用gcc编译整个项目,并尽可能少地修改代码.所以我写了一个perl脚本,它将intel格式内联asm转换为GAS格式.(顺便说一句:我在64位Linux机器上编译为32位).

我的问题是我必须为gcc指定在内联中将asm("...")哪些C变量传递给:: [var1] "m" var1, [var2] "m" var2, ...在末尾添加行的代码.

这是一种避免这种明确规范的方法吗?

我的尝试:

虚拟测试C代码简单地用源char数组的元素替换目标char数组的4个字符(我知道这不是最好的方法.它只是一个愚蠢的例子).

在原始函数中没有明确的规范,但它可以使用英特尔编译器进行编译(对我来说很遗憾,但我没有对此进行测试,但它应该与英特尔编译器一起使用,因为我根据实际代码进行了编译).LOOP即使在同一个C源文件中,标签也会被使用很多次.

#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;
}
Run Code Online (Sandbox Code Playgroud)

结果是: SRC: 'abcd', DST: 'abcd'

工作转换cp代码是(使用gcc编译).

GAS(AT&T)格式(编译: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"
    );
}
Run Code Online (Sandbox Code Playgroud)

英特尔格式(编译: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");
}
Run Code Online (Sandbox Code Playgroud)

这两个代码都有效,但需要进行一些代码修改(插入'%'字符,收集变量,修改跳转标签和jump函数).

我也试过这个版本:

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");
}
Run Code Online (Sandbox Code Playgroud)

但它下降了

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
Run Code Online (Sandbox Code Playgroud)

有没有办法避免输入参数的定义并避免修改本地标签?

加成

我试图使用全局变量.因为g必须使用约束而不是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");
}
Run Code Online (Sandbox Code Playgroud)

加成#2

我试过了

            "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'
Run Code Online (Sandbox Code Playgroud)

编译成

            lea esi, pGlob
            lea esi, OFFSET FLAT:pGlob // BAD
            //compilation fails with: Error: suffix or operands invalid for `lea'
Run Code Online (Sandbox Code Playgroud)

似乎只需要定义函数局部变量.全局变量可以添加到预告片中,但不是必需的.两者都有效:

            "mov esi, pGlob\n\t" // OK
            "mov esi, %[_pGlob]\n\t" // OK
Run Code Online (Sandbox Code Playgroud)

编译成

             mov esi, OFFSET FLAT:pGlob
             mov esi, OFFSET FLAT:pGlob
Run Code Online (Sandbox Code Playgroud)

我定义了一个函数局部变量.它必须在约束部分中定义:

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");
Run Code Online (Sandbox Code Playgroud)

不幸的是,应该确定什么是全局变量和什么是局部变量.这并不容易,因为asm代码可以在C宏中定义,甚至周围的函数也不确定.我认为只有预编译器才能获得足够的信息.也许代码必须预先编译gcc -E ....

我意识到没有在约束部分中定义输出,优化器可以消除一些代码.

TIA!

ams*_*ams 5

是的,您需要明确指定寄存器.海湾合作委员会不会为你做那件事.并且你不能(通常)将C变量名放在ASM字符串中.

对我来说,你的最终代码块看起来非常好,但在GCC中你不需要选择自己使用哪些寄存器.您还应该使用volatile关键字来防止编译器认为代码没有做任何事情,因为它没有输出.

试试这个:

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");
}
Run Code Online (Sandbox Code Playgroud)

这样编译器就会处理加载变量并为您选择寄存器(从而消除从一个寄存器到另一个寄存器的无意义复制).理想情况下,你也可以消除显式使用edx,但这里并不是必需的.

当然,在这个愚蠢的例子中,我只需要用C语言重新编写整个内容,让编译器完成它的工作.