Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么可以';GNU C基本内联asm语句中是否使用局部变量?_C_Gcc_Inline Assembly_Language Design - Fatal编程技术网

为什么可以';GNU C基本内联asm语句中是否使用局部变量?

为什么可以';GNU C基本内联asm语句中是否使用局部变量?,c,gcc,inline-assembly,language-design,C,Gcc,Inline Assembly,Language Design,为什么我不能使用main中的局部变量在基本asm内联中使用?它只允许在扩展asm中使用,但为什么会这样 (我知道局部变量位于返回地址之后的堆栈上(因此一旦函数返回就不能使用),但这不应该是不使用它们的原因) 以及基本asm的示例: int a = 10; //global a int b = 20; //global b int result; int main() { asm ( "pusha\n\t" "movl a, %eax\n\t" "movl b, %ebx\n\t" "im

为什么我不能使用
main
中的局部变量在基本asm内联中使用?它只允许在扩展asm中使用,但为什么会这样

(我知道局部变量位于返回地址之后的堆栈上(因此一旦函数返回就不能使用),但这不应该是不使用它们的原因)

以及基本asm的示例:

int a = 10; //global a
int b = 20; //global b
int result;
int main()
{
 asm ( "pusha\n\t"
 "movl a, %eax\n\t"
 "movl b, %ebx\n\t"
 "imull %ebx, %eax\n\t"
 "movl %eax, result\n\t"
 "popa");
printf("the answer is %d\n", result);
return 0;
}
扩展的示例:

int main (void) {

    int data1 = 10;  //local var - could be used in extended
    int data2 = 20;
    int result;

    asm ("imull %%edx, %%ecx\n\t"
          "movl %%ecx, %%eax" 
          : "=a"(result)
          : "d"(data1), "c"(data2));

    printf("The result is %d\n",result);

    return 0;
}
汇编时使用:
gcc-m32 somefile.c

站台:
uname-a

Linux 5.0.0-32-generic\34 Ubuntu SMP Wed Oct 2 02:06:48 UTC 2019 x86\u 64 x86\u 64 GNU/Linux

基本asm和“扩展asm”之间几乎没有区别;“基本asm”只是一种特殊情况,
asm\uuuu
语句没有输出、输入或缓冲列表。编译器不会在汇编字符串中对基本asm进行
%
替换。如果您想要输入或输出,您必须指定它们,然后人们称之为“扩展asm”

实际上,可以从“基本asm”访问外部(甚至文件范围静态)对象。这是因为这些对象(可能分别)在部件级别具有符号名称。但是,要执行这种访问,您需要注意它是否独立于位置(如果您的代码将链接到库或饼图可执行文件中),是否满足链接时可能施加的其他ABI约束,对于与链接时间优化和编译器可能执行的其他转换的兼容性,有各种各样的考虑。简而言之,这是一个坏主意,因为您不能告诉编译器基本asm语句修改了内存。没有办法保证安全

“内存”
clobber(扩展asm)可以安全地从asm模板按名称访问静态存储变量

基本asm的用例是只修改机器状态的东西,比如内核中的
asm(“cli”)
,以禁用中断,而不读取或写入任何C变量。(即使这样,您也经常使用“内存”清除器来确保编译器在更改机器状态之前完成了早期的内存操作。)

本地(自动存储,而非静态)变量基本上没有符号名称,因为它们不存在于单个实例中;在运行时,它们在其中声明的块的每个活动实例都有一个对象。因此,访问它们的唯一可能方法是通过输入/输出约束


来自MSVC领域的用户可能会发现这一点令人惊讶,因为MSVC的内联汇编方案通过将其内联asm版本中的局部变量引用转换为堆栈指针相对访问等方式解决了这一问题。然而,它提供的内联asm版本与优化编译器不兼容,并且在使用该类型内联asm的函数中几乎不会发生优化。GCC和在unix中与C一起发展起来的更大的编译器世界没有做任何类似的事情。

这是因为asm是一种定义语言,对于同一处理器系列上的所有编译器都是通用的。在使用
\uu asm\uu
关键字后,您可以可靠地使用任何好的处理器手册,然后开始编写有用的代码

但是它没有为C定义接口,老实说,如果你没有将你的汇编程序与你的C代码接口,那么为什么它会存在呢

非常简单的asm示例:生成调试中断;设置浮点寄存器模式(异常/精度)

每个编译器编写者都发明了自己的与C接口的机制。例如,在一个旧的编译器中,您必须在C代码中将要共享的变量声明为命名寄存器。在GCC和clang中,它们允许您使用非常混乱的两步系统引用输入或输出索引,然后将该索引与局部变量关联

该机制是asm标准的“扩展”

当然,asm并不是真正的标准。更改处理器,则asm代码为垃圾。一般来说,当我们谈论坚持c/c++标准而不使用扩展时,我们不谈论asm,因为您已经违反了所有可移植性规则

然后,最重要的是,如果要调用C函数,或者asm声明可由C调用的函数,则必须与编译器的调用约定相匹配。这些规则是隐含的。它们限制了您编写asm的方式,但根据某些标准,它仍然是合法的asm

但是,如果您只是编写自己的asm函数,并从asm调用它们,那么您可能不会受到c/c++约定的太多约束:制定自己的寄存器参数规则;返回您想要的任何寄存器中的值;制作或不制作堆叠框架;通过异常保留堆栈框架-谁在乎呢


请注意,您可能仍然受到平台可重定位代码约定的约束(这些不是“C”约定,但通常使用C语法进行描述),但这仍然是一种方法,您可以编写一块“可移植”asm函数,然后使用“扩展”嵌入式asm调用它们。

您可以在扩展汇编中使用局部变量,但是您需要告诉扩展程序集构造关于它们的信息。考虑:

#include <stdio.h>


int main (void)
{
    int data1 = 10;
    int data2 = 20;
    int result;

    __asm__(
        "   movl    %[mydata1], %[myresult]\n"
        "   imull   %[mydata2], %[myresult]\n"
        : [myresult] "=&r" (result)
        : [mydata1] "r" (data1), [mydata2] "r" (data2));

    printf("The result is %d\n",result);

    return 0;
}
在这种情况下,
0
data2
表示使其与操作数0相同。操作数按其出现的顺序编号,第一个输出操作数从0开始,一直到输入操作数。因此,当汇编代码开始时,
%[myresult]
将引用一些寄存器,其中
data2
的值已被放入该寄存器中,编译器将期望在汇编完成时,
result
的新值会出现在该寄存器中

执行此操作时,必须将约束与在装配中使用对象的方式相匹配。对于
r
约束,编译器提供
    __asm__(
        "   imull   %[mydata1], %[myresult]\n"
        : [myresult] "=r" (result)
        : [mydata1] "r" (data1), [mydata2] "0" (data2));
    asm ("imull %%edx, %%ecx\n\t"
          : "=c"(result)
          : "d"(data1), "c"(data2));