how does CPU retrieve multibyte from memory

时间:2018-04-20 00:34:38

标签: assembly memory cpu-architecture cpu-registers

Hi I just a newbie to assembly programming. I'm confused how CPU retrieve multibyte (e.g 32 bits for 32 bit machine) from memory. Let say we have an integer i which occupies 4 bytes in memory (starting address at 0x100) so when we use IA32 assembly programming, we just write something like:

movl 8(%esp), %eax

where esp is current stack pointer. 8 is just the offset from the stack pointer address to variable i so when the ia32 instruction executes, cpu just retrieve the byte at 0x100, what about the rest of bytes at 0x101, 0x102, 0x103? How CPU retrieve 32 bits all in once?

Edited: new questions I think I was fundamental wrong on the understanding the word size. But I am still confused but how does 32 bits machine retrieve long integer which is 8 bytes 64 bit, maybe using movq but again what about accessing an objects which is 256 bytes? does CPU just issues movq 4 times? how does cpu know in advance that how many time it need to issue a mov command to retrieve the large size of object?

2 个答案:

答案 0 :(得分:3)

  

32位机器如何检索长整数,即8字节64位

如果您在整数寄存器中执行此操作,编译器必须使用多条指令,因为架构不提供加载指令一次使用两个32位寄存器。所以CPU只看到两个单独的加载指令。

考虑这些函数compiled by gcc7.3 -O3 -m32 for 32-bit x86,在堆栈上传递args,在edx:eax中返回64位整数(在EDX中为高一半,在EAX中为低一半)。即i386 System V ABI。

int64_t foo(int64_t a) {
    return a + 2;
}
    movl    4(%esp), %eax
    movl    8(%esp), %edx
    addl    $2, %eax
    adcl    $0, %edx                   # add-with-carry
    ret


int64_t bar(int64_t a, int64_t b) {
    return a + b;
}

    movl    12(%esp), %eax      # low half of b
    addl    4(%esp), %eax       # add low half of a
    movl    16(%esp), %edx
    adcl    8(%esp), %edx       # carry-in from low-half add
    ret

CPU本身提供了程序员/编译器在处理大于寄存器的数据时可以使用的指令。 CPU仅支持作为指令集一部分的宽度,而不支持任意宽度。这就是我们拥有软件的原因。

在x86上,编译器可以选择将movq用于XMM或MMX寄存器,并使用paddq,特别是如果这是可以存储64位的更大函数的一部分结果在内存中某处而不是在整数寄存器中需要它。但这只能达到矢量寄存器的极限,它们只支持64位宽的元素。没有128位加法指令。

  

cpu如何预先知道发出mov命令以检索大尺寸对象需要多长时间?

CPU只需按程序顺序执行一次每条指令。 (或者在内部做任何想做的事情,让幻觉这样做。)

x86 CPU必须知道如何将任何可能的x86指令解码为正确的内部操作。如果CPU一次只能加载128位,则必须将像vmovups (%edi), %ymm0这样的256位向量加载解码为内部的多个加载操作(如AMD那样)。请参阅David Kanter's write-up on the Bulldozer microarchitecture

或者它可以将其解码为在加载端口(如Sandybridge)中需要两个周期的特殊加载操作,因此256位加载/存储不会花费额外的前端带宽,只有额外的时间在加载/存储端口。

或者,如果从L1d缓存到执行单元的内部数据路径足够宽(Haswell及更高版本),它可以解码为由缓存/加载端口内部处理的单个简单加载uop,就像{{1}一样},或特别是mov (%edi), %eax(向量寄存器中的32位零扩展加载)。

256 字节是32个qwords;没有当前的x86 CPU可以在一次操作中加载那么多。

256 是4个qwords,或者是一个AVX vmovd (%edi), %xmm0寄存器。现代Intel CPU(Haswell及更高版本)具有宽的内部数据路径,并且实际上可以一次从缓存传输256位到向量加载执行单元,将ymm作为单个uop执行。 有关缓存中的宽容量如何为L1d缓存提供极高的吞吐量/带宽的详细信息,请参阅How can cache be that fast?

答案 1 :(得分:1)

一般情况下,CPU可以从内存中加载多个字节,因为它们的设计是为了这样做而且它们的ISA支持它。

例如,他们的寄存器,内部总线,缓存设计和内存子系统都是为此而设计的。在物理上,一个能够加载64位值的处理器可能在各个地方有64条并行线,以便在CPU周围移动64位(8字节) - 但其他设计也是可能的,例如较小的总线16位,一次传输两个字节,甚至是一次一位传输位的位串行点对点连接。同一CPU的不同部分可以使用不同的设计和不同的物理宽度。例如,从DRAM读取N位可以实现为从C芯片并行读取M位,结果在存储器控制器处合并,因此芯片需要支持比核心到存储器路径的其他部分更小程度的并行性。

ISA固有支持的宽度可能与硬件实现的自然宽度不同。例如,当英特尔添加AVX ISA扩展(第一个支持256位(16字节)加载和存储)时,底层硬件最初将其实现为一对128位操作。后来的CPU架构(Haswell)最终将其实现为完整的256位宽度操作。即使在今天,低成本的x86芯片也可能将大型负载/存储操作分成更小的单元。

最终,这些都是CPU的内部细节。您可以依赖的是记录的行为,例如可以原子方式加载的值的大小,或者记录它的CPU,加载类型值所需的时间。如何实现内部更多的是电气工程/ CPU设计问题,有很多方法可以做到。