我知道加载/存储队列架构,以便于存储加载转发和消除无序推测负载的歧义。这是使用匹配的加载和存储地址完成的。
如果早期存储是未对齐的地址且负载取决于它,则此匹配地址技术将不起作用。我的问题是,如果第二次加载是无序发布的,它是如何被早期商店消除歧义的?或者现代建筑用什么政策来处理这种情况?
答案 0 :(得分:3)
简短的回答是它取决于架构,但理论上,未对齐的操作不一定必然阻止架构执行存储转发。然而,实际上,未对齐加载操作所代表的转发可能性大得多,这意味着可能根本不支持从这些位置转发,或者可能不如对齐的情况支持转发。
长期的答案是,任何特定的架构都会有各种可以有效处理的场景,以及那些他们无法处理的场景。
旧的或非常简单的架构可能根本没有任何存储转发功能。这些体系结构可能根本不执行,或者可能具有一些无序功能,但可能只是等到所有先前的存储都已提交,然后再执行任何负载。
下一级别的复杂性是一种至少具有某种CAM来检查先前存储地址的架构。此体系结构可能没有存储转发,但是一旦加载地址并且所有先前的存储地址都已知(并且没有匹配),就可以允许加载按顺序执行或无序执行。如果与先前存储匹配,则架构可以等到存储器在执行加载之前提交(这将从L1读取存储的值,如果有的话)。
接下来,我们有类似于上述的架构,等到先前的存储地址已知并且还存储转发。行为与上述相同,只是当加载地址命中先前存储时,存储数据将转发到加载,而不等待它提交给L1。
上述问题的一个大问题是,在上述设计中,负载仍然无法执行,直到所有先前的存储地址都已知。这抑制了无序执行。接下来,我们添加猜测 - 如果观察到特定IP的负载而不是依赖于先前的存储,我们只是让它执行(读取其值),即使之前的存储地址不是&#39不知道。在退休时,将进行第二次检查以确保没有命中到先前的商店是正确的,如果没有,将会有某种类型的管道清理和恢复。预计点击先前存储的负载会等到商店数据(可能还有地址)可用,因为它们需要存储转发。 1
这就是我们今天所处的位置。还有更先进的技术,其中许多都属于memory renaming的旗帜,但据我所知,它们没有得到广泛部署。
最后,我们回答您的原始问题:所有这些都与未对齐的载荷相互作用。上面的大部分内容都没有改变 - 我们只需要更准确地了解 hit 的定义是什么,其中加载从上一个商店读取数据。
您有几种情况:
在大多数平台上,无论是否对齐,都存在所有三种可能的情况。但是,在对齐值的情况下,第二种情况(部分重叠)只能在较大的存储跟随较小的负载时发生,并且如果平台仅支持一次负载大小,则根本不支持情况(2)。
理论上,在场景(1)中可以直接 1 存储到转载转发,但在场景(2)或(3)中则不行。
要捕获(1)的许多实际情况,您只需要检查存储和加载地址是否相同,并且负载不大于存储。这仍然错过了较小的负载完全包含在较大的存储中的情况,无论是否对齐。
对齐有助于上述检查更容易:您需要比较较少的地址位(例如,32位加载可以忽略地址的最后两位),并且比较的可能性较小:一个4字节的加载只能以两种可能的方式包含在8字节的存储中(在存储地址或存储地址+4),而未对齐的操作可以完全包含在五种不同的方式中(在加载地址偏移任何一种情况下)来自商店的0,1,2,3或4个字节)。
这些差异在硬件中很重要,其中存储队列必须看起来像实现这些比较的完全关联CAM。比较越普遍,需要的硬件越多(或者查找的延迟越长)。早期的硬件可能只捕获了相同的地址" (1)的情况,但趋势是捕捉更多的情况,包括对齐和不对齐。这是great overview。
1 如何最好地执行此类内存依赖性推测是WARF持有patents并且基于actively suing各种各样的CPU制造商。
2 direct 我指的是从单个商店到以下商店。原则上,您可能还有更复杂的存储转发形式,它可以占用多个先前存储的一部分并将它们转发到单个负载,但是如果当前架构实现这一点,我也不清楚。
答案 1 :(得分:1)
对于像x86这样的ISA,加载和存储可以是多个字节宽且不对齐。存储可以写入一个或多个字节,其中加载可以读取它们的子集。或者另一方面,商店写入的整个数据可以是后续加载读取的数据的子集。
现在要检测订单违规(商店检查较年轻的执行加载),应检测到这种重叠。我相信即使存在未对齐的访问,如果LSQ存储每个访问的地址和大小(多少字节)(以及其值,PC等其他信息),也可以实现这一点。利用该信息,LSQ可以计算每个加载读取/存储写入的每个地址(地址< ="指令中涉及的地址"<地址+大小)以查找地址是否匹配。
要在此主题上添加更多内容,实际上,商店到转载转发(商店检查较年轻未执行的加载)可能仅限于某些情况,例如完美对齐的访问(对不起,我不知道现代处理器中实现的细节)。能够处理大多数情况的正确转发机制(从而相应地提高性能)是以增加复杂性为代价的。请记住,对于不受支持的转发情况,您可以始终停止加载,直到匹配的存储回写到缓存,并且加载可以直接从缓存中读取其值。