Joh*_*nes 10 c++ compiler-construction assembly mips cpu-registers
在C++中,局部变量总是在堆栈上分配.堆栈是应用程序可以占用的允许内存的一部分.该内存保存在RAM中(如果没有换成磁盘).现在,C++编译器是否总是创建在堆栈中存储局部变量的汇编程序代码?
举例来说,以下简单代码:
int foo( int n ) {
return ++n;
}
Run Code Online (Sandbox Code Playgroud)
在MIPS汇编程序代码中,这可能如下所示:
foo:
addi $v0, $a0, 1
jr $ra
Run Code Online (Sandbox Code Playgroud)
如您所见,我根本不需要使用堆栈.C++编译器会识别出来,并直接使用CPU的寄存器吗?
编辑:哇,非常感谢您几乎立即和广泛的答案!foo的功能主体当然应该是return ++n;,而不是return n++;.:)
jal*_*alf 12
是.没有规则"变量总是在堆栈上分配".C++标准没有说明堆栈.它不假设堆栈存在,或存在寄存器.它只是说代码应该如何表现,而不是如何实现.
编译器只在必要时将变量存储在堆栈中 - 例如,当它们必须通过函数调用时,或者如果您尝试获取它们的地址.
编译器不是傻瓜.;)
免责声明:我不知道MIPS,但我确实知道一些x86,我认为原则应该是一样的..
在通常的函数调用约定中,编译器会将值推n送到堆栈以将其传递给函数foo.但是,fastcall您可以使用约定来告诉gcc通过寄存器传递值.(MSVC也有这个选项,但我不确定它的语法是什么.)
test.cpp:
int foo1 (int n) { return ++n; }
int foo2 (int n) __attribute__((fastcall));
int foo2 (int n) {
return ++n;
}
Run Code Online (Sandbox Code Playgroud)
编译以上内容g++ -O3 -fomit-frame-pointer -c test.cpp,我得到foo1:
mov eax,DWORD PTR [esp+0x4]
add eax,0x1
ret
Run Code Online (Sandbox Code Playgroud)
如您所见,它从堆栈中读取值.
这是foo2:
lea eax,[ecx+0x1]
ret
Run Code Online (Sandbox Code Playgroud)
现在它直接从寄存器中获取值.
当然,如果您内联函数,编译器将在较大函数的主体中进行简单的添加,而不管您指定的调用约定.但是当你无法内联时,这将会发生.
免责声明2:我不是说你应该不断猜测编译器.在大多数情况下,这可能是不实际和必要的.但不要以为它会产生完美的代码.
编辑1:如果您正在讨论普通的局部变量(不是函数参数),那么编译器会在它认为合适的情况下将它们分配到寄存器或堆栈中.
编辑2:看起来调用约定是特定于体系结构的,MIPS将传递堆栈上的前四个参数,正如Richard Pennington在他的回答中所述.因此,在您的情况下,您不必指定额外属性(实际上是x86特定的属性.)
是的,一个好的,优化的C/C++将优化它.甚至MUCH更多:在这里看到:费利克斯·冯·Leitners编译调查.
普通的C/C++编译器无论如何都不会将每个变量放在堆栈上.您的foo()函数的问题可能是变量可以通过堆栈传递给函数(系统的ABI(硬件/ OS)定义).
使用C的register关键字,您可以给编译器一个提示,即将变量存储在寄存器中可能会很好.样品:
register int x = 10;
Run Code Online (Sandbox Code Playgroud)
但请记住:x如果想要,编译器可以免费存储在寄存器中!
答案是肯定的,也许吧.它取决于编译器,优化级别和目标处理器.
在mips的情况下,前四个参数(如果很小)在寄存器中传递,返回值在寄存器中返回.因此,您的示例无需在堆栈上分配任何内容.
实际上,真相比小说更奇怪.在您的情况下,参数返回不变:返回的值是++运算符之前的n:
foo:
.frame $sp,0,$ra
.mask 0x00000000,0
.fmask 0x00000000,0
addu $2, $zero, $4
jr $ra
nop
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3676 次 |
| 最近记录: |