在进行流水线操作时,您能否将 mov 连续写入同一个寄存器,还是像 add 一样需要 3 个 NOP?

While pipelining, can you consecutively write mov to the same register, or does it require 3 NOPs like add does?

这是在合并流水线和您需要的必要 NOP 时通过 x86 实现 mov 和 add 的正确方法。

 mov , eax
 NOP 
 NOP
 NOP
 add , eax

如果我想用 mov 更改 eax,我是否可以立即用另一个 mov 覆盖它,因为您只是覆盖已经存在的内容,或者我是否需要再次写入 3 个 NOP 才能完成 WMEDF 循环?

mov , eax
mov , eax

mov , eax
NOP
NOP
NOP
mov , eax

This is the correct way to implement mov and add through x86 when incorporating pipelining and the necessary NOPs you need.

x86 完全不正确。 NOP 从不 x861.

上的正确性所必需的

如果输入还没有为指令准备好,它会等待它准备好。 (乱序执行可以隐藏这个并行等待多个依赖链...

我想我读过一些架构有一些指令,如果你过早读取结果,你会得到不可预测的值。这仅适用于一些指令(例如乘法),并且许多架构在架构上没有任何需要 NOP(或对其他寄存器的有用工作)的情况。

简单有序管道上的正常情况(如缓存未命中加载)由 pipeline interlocks 处理,如果需要,可以在硬件中有效地插入 NOP,而不需要软件包含会降低高速缓存速度的无用指令相同架构的性能(乱序)实现运行使用相同的二进制文件。


or do I need to write 3 NOPs again so it can finish the WMEDF cycle?

x86 ISA 不是围绕 classic RISC pipeline 设计的(如果那是该缩写应该表示的意思)。因此,即使像 i486 这样的标量有序流水线 x86 实现在内部与您所想的相似,也必须处理不使用 NOP 来创建延迟的代码。即他们必须自己检测数据依赖性。

当然,现代 x86 实现至少都是 2-wide 超标量(旧 Atom pre-Silvermont,或第一代 Xeon Phi,或 P5 Pentium)。这些 CPU 是有序的,但其他 CPU 是乱序的,完全寄存器重命名(Tomasulo 的算法),这避免了写后写入的危险,就像你正在谈论的那样.例如,Skylake 可以 运行

mov   , %eax
mov   , %eax
mov   , %eax
mov   , %eax
...
eventually jcc to make a loop

每个周期 4 mov 条指令,即使它们都写入相同的寄存器。

但请注意 mov , %al 在 Intel P6 系列(PPro/PII 到 Core2/Nehalem)以外的 CPU 上合并到 %rax,也许还有 Sandybridge(但不是后来的 CPU)像哈斯韦尔)。在那些为低位 8 重命名部分寄存器的 CPU 上,mov , %al 每个周期可以 运行 多条指令(受 ALU 端口限制)。但在其他人身上,它就像 add%rax。参见 。 (有趣的事实,在 Skylake 上每个时钟重复 mov %bl, %ah 运行s 4,每个时钟重复 mov 3, %ah 运行s 1。)


延伸阅读:

  • x86 上的寄存器重命名使用 Tomasulo 的算法,这是 OP 代码缓慢的情况,因为他们避免寄存器重用,没有为累加器留下足够的寄存器来隐藏 FP add / 的延迟FMA.
  • 我的回答更多地说明了现代 x86 上缺乏 WAW 和 WAR 危害,包括内存。

脚注:

  1. 在您不知道确切的跳转目标地址的漏洞利用中,可能需要 NOP sled 以确保正确性,以便该区域的任何位置的跳转都将执行 NOP,直到到达您的有效负载。