代码之家  ›  专栏  ›  技术社区  ›  Frank V

GCC的asm扩展版本

  •  4
  • Frank V  · 技术社区  · 16 年前

    我从没想过我会发布一个集会问题。:-)

    GCC ,有一个 extended version of the asm function . 此函数可以接受四个参数:汇编代码、输出列表、输入列表和覆盖列表。

    覆盖列表中的寄存器是否已清零 ? 以前在那里的值会发生什么变化(来自其他正在执行的代码)。

    使现代化 :在考虑到目前为止我的答案(谢谢!)时,我想补充一点,尽管clobber列表中列出了一个寄存器,但它(在我的例子中)正在pop中使用( popl )指挥部。没有其他参考资料。

    3 回复  |  直到 16 年前
        1
  •  7
  •   DaveR    16 年前

    不,它们没有归零。覆盖列表的用途(通常称为 打击者名单 )通知GCC,根据asm指令,clobber列表中列出的寄存器将被修改,因此编译器应保留任何当前处于活动状态的寄存器。

    例如,在x86上 cpuid 指令使用四个固定寄存器以四个部分返回信息: %eax , %ebx , %ecx %edx ,基于的输入值 %eax . 如果我们只对结果感兴趣 %ebx ,然后我们可以(天真地)写:

    int input_res1 = 0; // also used for first part of result 
    int res2;
    __asm__("cpuid" : "+a"(input_res1), "=b"(res2) ); 
    

    这将得到C变量中结果的第一部分和第二部分 input_res1 res2 ; 然而 如果 %edx 保存其他资料;它们将被 cpuid 不知情 打击者名单 :

    int input_res1 = 0; // also used for first part of result 
    int res2;
    __asm__("cpuid" : "+a"(input_res1), "=b"(res2)
                    : : "%ecx", "%edx" );
    

    正如我们告诉GCC的那样 %ecx %edx 将被此asm调用覆盖,它可以正确处理这种情况-通过不使用 %ecx ,或将其值保存到 asm 功能和恢复后。

    关于你的第二个问题(为什么你会看到一个登记册列在一个 popl 指示)-假设您的 asm

    __asm__("popl %eax" : : : "%eax" );
    

    然后,这里的代码从堆栈中弹出一个项目,但是它不关心实际值——它可能只是保持堆栈平衡,或者该值在该代码路径中不需要。以这种方式书写,而不是:

    int trash // don't ever use this.
    __asm__("popl %0" : "=r"(trash));
    

        2
  •  5
  •   Community Mohan Dere    6 年前

    如果“调零”的意思是“寄存器中的值被替换为0,以防止我知道其他函数在做什么”,那么不,寄存器在使用前不会调零。但这不重要,因为你告诉GCC你打算这么做 百货商店 那里的信息,不是你想要的 阅读

    将此信息提供给GCC,以便(阅读文档)在完成汇编代码时“无需猜测哪些寄存器或内存位置将包含要使用的数据”(例如,不必记住数据是否位于堆栈寄存器或其他寄存器)。

    使现代化

    gas ld collect2 对二进制文件执行更多操作)。部件块的存在是为了将文本直接传递给 气体 而且,存在“Culblist”(和输入列表),这样编译器就可以做任何设置,以在C、C++、艾达、java等方面传递信息,而这些信息是在 另一方面,确保寄存器中当前的任何重要信息都可以通过在程序集块运行之前将其复制到内存中(并在运行之后从内存中复制回来)来从程序集块中得到保护。

    另一种方法是保存和恢复每个汇编代码块的每个寄存器。在具有大量寄存器的RISC机器上,可能会变得昂贵(例如,安腾有128个通用寄存器、128个浮点寄存器和64个1位寄存器)。

    我已经有一段时间没写汇编代码了。我使用GCC的命名寄存器特性的经验比使用特定寄存器的经验多得多。那么,看一个例子:

    #include <stdio.h>
    
    long foo(long l)
    {
        long result;
        asm (
            "movl %[l], %[reg];"
            "incl %[reg];"
            : [reg] "=r" (result)
            : [l] "r" (l)
        );
        return result;
    }
    
    int main(int argc, char** argv)
    {
        printf("%ld\n", foo(5L));
    }
    

    reg 在汇编代码中,GCC将自动复制到 result 完成时可变。在C代码和汇编代码中,不需要为这个变量指定不同的名称;我这样做只是为了证明这是可能的。无论GCC决定使用哪个物理寄存器——无论 %%eax %%ebx , %%ecx ,等等——当我进入汇编块时,GCC将负责将该寄存器中的任何重要数据复制到内存中,以便在汇编块结束之前充分使用该寄存器。

    l L 当我进入装配块时。GCC还将执行任何必要的记录保留,以在我进入汇编块之前保护该寄存器中的任何数据。

    "addl %[reg], %%ecx;"
    

    由于GCC的编译器部分不检查汇编代码,因此它不会保护其中的数据 %%ecx . 如果我幸运的话, %%ecx 可能恰好是GCC决定用于 %[reg] %[l] . 如果我运气不好,我会“神秘地”更改程序其他部分的值。

        3
  •  4
  •   Ana Betts    16 年前

    我怀疑覆盖列表只是给GCC一个提示,在ASM调用中不要在这些寄存器中存储任何有价值的内容;由于GCC不分析您给它的ASM,并且某些指令有副作用,会触及代码中未明确命名的其他寄存器,因此这是告诉GCC的方法。

    推荐文章