变量声明中的内联汇编语句和函数内的外部变量声明

Inline assembly statements in the variable declarations and extern variable declarations inside a function

这是 C 程序(包括内联汇编)中的一个函数,已编译并且 运行 可以使用 gcc。

  1. 变量声明末尾的 asm 语句是什么?
  2. 为什么要在函数内部声明外部变量?与在函数外声明相比有什么效果?
extern unsigned char __heap_base;
extern unsigned char __heap_limit;

caddr_t _sbrk_r (struct _reent *r,int incr)
{
extern   unsigned char  __bottom_of_heap asm ("__heap_base");
extern   unsigned char  __limit_of_heap  asm ("__heap_limit");
register unsigned char *__stack_ptr  asm ("sp");
...
}

在另一个汇编文件中,__heap_base、__heap_limit定义如下(节选)。

                .global  __heap_base
                .global  __heap_limit

__user_thread_space:
                .equ     __heap_base,  __user_thread_space + THREAD_AREA
                .equ     __heap_limit, __heap_base         + HEAP_SIZE

https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html 正在为那些 C 变量设置 asm 符号名称。这使您可以设置 asm 名称,而不管编译器通常是否在前面附加一个额外的 _,例如,或覆盖 C++ 名称改编。或者在这种情况下,只需使用与 C var 名称不同的 asm 符号名称。


堆栈指针的 register ... asm("regname") 全局变量也记录在案:https://gcc.gnu.org/onlinedocs/gcc/Global-Register-Variables.html.

分配给那个变量肯定会破坏事情,甚至阅读它也没有完全明确定义/保证的语义。当编译器修改 SP 本身为局部变量创建 space 时,为具有大量参数的函数推送参数,或者 wrt。分配。例如,如果 CSE of __stack_ptr 即使在使用 alloca 的循环中也是可能的,我也不会感到惊讶,让编译器假设 C 变量每次都具有相同的值读,如果你没有明确地写它。但我也希望你得到的任何值都在当前函数的堆栈帧中的某个位置,这与 C 程序中关于 asm 堆栈指针的有意义的说法差不多。我当然不会尝试编写执行 __stack_ptr = new_stack;

的上下文切换函数

请注意,这两种使用 asm 关键字的语法本质上是相同的:两者都在某种程度上告诉编译器:当您在正在制作的 asm 源代码中打印对此变量的引用时,请使用此名称。 (当然它必须知道它是寄存器还是符号,尤其是在 load/store 机器上而不是 CISC)。


Why is extern variable declared inside a function?

我假设只是为了限制这些声明的范围。

在普通的 ISO C 中,您可以在函数内部 extern char bar;,编译器将引用全局变量 bar。 (至少 gcc -Wall -Wextra -Wpedantic 没有任何抱怨,你可以从 ASM 中看到没有任何 asm() 覆盖,它使用普通的 bar 作为 asm 符号名称:https://godbolt.org/z/j1KznMEPG)

即它使用该名称引用静态存储中的变量,C 声明作用域为该函数而不是全局作用域。

(顺便说一句,函数内部的 static var 通常有一个涉及函数名称的 asm 名称,因此它不会与另一个函数中的同名静态变量冲突。当然,这是 extern 而不是 static,因此使用普通的全局名称是有道理的。)