Assembly 如何知道汇编代码正在使用RAM?

Assembly 如何知道汇编代码正在使用RAM?,assembly,Assembly,我是一个新的装配工,这是一个基本的问题 我刚刚听说了使用的概念 我通过编译了C++代码 g++ -O3 main.cpp -S -o main3.s main.cpp 这是一个基本程序,可以将所有变量存储到CPU寄存器中。因此,我猜它不使用RAM。我想知道检查汇编代码是否使用RAM的标准是什么?主内存存储的不仅仅是变量。事实上,当您运行程序时,您的操作系统会为负责运行可执行文件的进程保留一些空间(称为地址空间) 编译生成的汇编代码存储在一个部分(.text部分),数据存储在.data部分,静态

我是一个新的装配工,这是一个基本的问题

我刚刚听说了使用的概念

<>我通过

编译了C++代码
g++ -O3 main.cpp -S -o main3.s
main.cpp


这是一个基本程序,可以将所有变量存储到CPU寄存器中。因此,我猜它不使用RAM。我想知道检查汇编代码是否使用RAM的标准是什么?

主内存存储的不仅仅是变量。事实上,当您运行程序时,您的操作系统会为负责运行可执行文件的进程保留一些空间(称为地址空间

编译生成的汇编代码存储在一个部分(
.text
部分),数据存储在
.data
部分,静态变量初始化为
.bss
部分,等等。例如,字符串通常存储在只读部分(
.rodata


因此答案是否定的,每个程序在运行时都必须使用内存。

在您链接的片段中,Jason Turner刚刚说C局部变量都适合寄存器,因此编译器不必花费额外的指令

它使用RAM来存储代码和数据,只是不使用任何堆栈内存来存储局部变量。i、 e.本地变量的RAM字节数为零,当然不是零字节总数。他甚至说这个游戏可以编译1005字节(代码+数据)


读取asm时,注意到堆栈中缺少加载/存储,例如在x86-64上使用RSP(或RBP,如果用作帧指针)的寻址模式时,可以检测到这一点

对于不是很大的函数来说,这是完全正常的。内联函数调用是以其他方式实现的关键,因为编译器在调用非内联函数时通常必须使内存“同步”(反映C抽象机的正确值)

int foo(int num) {
    int tmp = num * num;
    return tmp;
}
获取寄存器中的
num
,并将
tmp
保留在那里。Jason的演讲使用了Godbolt,这里有一个链接,由gcc7.3编译,有优化也有优化:

 foo:   # with optimization: all operands are registers
    imul    edi, edi
    mov     eax, edi
    ret

foo:    # without optimization:
    push    rbp
    mov     rbp, rsp                     # make a stack frame with RBP
    mov     DWORD PTR [rbp-20], edi      # spill num to the stack
      # start of code for first C statement
    mov     eax, DWORD PTR [rbp-20]      # reload it
    imul    eax, DWORD PTR [rbp-20]      # and use it from memory again
    mov     DWORD PTR [rbp-4], eax       # spill tmp to the stack
      # end of first C statement

    mov     eax, DWORD PTR [rbp-4]       # load tmp into the return value register, eax)
    pop     rbp
    ret
这不必为
子rsp,24
保留任何堆栈空间,因为它使用rsp下方的红色区域作为溢出/重新加载的局部区域

显然,启用优化后,即使编译器在一个大型复杂函数中的寄存器用完了,并且不得不溢出一些东西,您也不会得到如此糟糕的代码
-O0
是一种反优化模式,其中每个C语句都获得一个单独的asm块,因此您可以设置断点和修改变量,使代码仍能工作。甚至在
gdb
中跳转到另一个源代码行


Re:x86有多少个寄存器,如谈话中所述:

i386有8个体系结构整数寄存器。它有一些段寄存器,您可以滥用它们来保留额外的值,如果它有一个FPU,则有8个x87 80位FP堆栈寄存器。Jason对16的猜测听起来是假的,但他可能将AL/AH、BL/BH计算为单独的8位寄存器,因为您可以独立使用它们。但与EAX不同,因为窄寄存器是完整寄存器的子集

(注意,在AMD上,AL和AH根本不是独立的;使用其中一个对另一个有错误的依赖性,即在整个EAX/RAX上。 在奔腾P5MMX之前(包括奔腾P5MMX)的CPU上,根本没有部分寄存器惩罚,因为没有无序执行或寄存器重命名。)

他声称现代x86-64有数百个寄存器,这也绝对是假的,除非你计算所有的控制寄存器和特定于模型的寄存器。但是堆栈内存要比那些寄存器快得多,而且无论如何都不能在它们中放入任意值。由于只有16个体系结构整数寄存器(其中一个是堆栈指针,因此实际上可以在一个大函数中使用15个寄存器),当需要更多的变量同时“活动”时,仍然需要额外的指令来溢出或至少重新加载内容

寄存器重命名到一个大的物理寄存器池是非常好的,并且可以找到指令级并行性。但是,您只能通过为不同的值重用相同的整数寄存器来利用这些寄存器。(即,使同一寄存器的两次使用实际上是独立的。)


Haswell有一个用于整型/GP寄存器的168项物理寄存器文件,还有一个用于重命名FP/向量寄存器的168项向量/FP寄存器文件。但在架构上,它在x86-64模式下运行时只有16 GP/16 YMM,在ia-32模式下只有8/8。

I在链接的youtube视频中,演讲者说了一些不同的话。那么,他是什么意思?你可以在
.LC0-3
上看到字符串。还要注意,这只是
主功能。它将与C++库链接,并放心使用,这将使用大量的RAM。@ JeSts,那么这意味着什么?“JoCar:我假设杰森是指“动态分配的虚拟内存”(例如,由代码分配的内存< MARROCK)(<代码> >或代码>新< /COD> >)。因为他使用像视频芯片的控制寄存器这样的东西来存储位置值,所以不仅仅是CPU。
call
有一个隐含的堆栈用法,首先,编写一个没有
call
main()
的C程序几乎不可能“使用零字节RAM”这意味着您不能使用使用堆栈的
call/ret
push/pop
int foo(int num) {
    int tmp = num * num;
    return tmp;
}
 foo:   # with optimization: all operands are registers
    imul    edi, edi
    mov     eax, edi
    ret

foo:    # without optimization:
    push    rbp
    mov     rbp, rsp                     # make a stack frame with RBP
    mov     DWORD PTR [rbp-20], edi      # spill num to the stack
      # start of code for first C statement
    mov     eax, DWORD PTR [rbp-20]      # reload it
    imul    eax, DWORD PTR [rbp-20]      # and use it from memory again
    mov     DWORD PTR [rbp-4], eax       # spill tmp to the stack
      # end of first C statement

    mov     eax, DWORD PTR [rbp-4]       # load tmp into the return value register, eax)
    pop     rbp
    ret