LLVM MISched 指令调度程序使用处理器功能单元、流水线和延迟的声明性 TableGen 描述。想象一下,试图从这些声明中确定英特尔优化参考手册中的编码指南的等效项。
从广义上讲,静态调度 OOO 处理器的目标/技术是什么?它何时在 B 之前调度指令 A,何时为 OOO 处理器在 B 之后调度 A?
超标量处理器可以一次执行多条指令。按顺序处理的处理器将仅考虑其原始顺序中的指令。乱序 (OOO( 处理器可以不按顺序执行指令,然后按顺序提交结果。猜测对这个问题无关紧要,但我认为这些处理器是流水线的。想想A53(按顺序(和哈斯韦尔(OOO(。
OOO 处理器接下来将执行哪条指令是处理器在运行时做出的调度决策。因此,这通常称为动态调度。按顺序处理器执行哪条指令是由编译器在编译程序时决定的。因此,这通常称为静态调度。
但是,编译器也会静态地定位/调度 OOO 处理器。在按顺序和 OOO 情况下,编译器可以查看一个大的指令窗口;编译器必须处理寄存器压力;在这两种情况下,编译器都希望保持功能单元忙碌。OOO处理器通常还可以重命名寄存器,从而减轻寄存器压力。
鉴于 OOO 处理器动态调度指令,提前编译器应该做些什么来帮助解决这个问题?
您通常是正确的,但编译时调度仍然可以稍微提高执行速度。发生这种情况是因为编译器可以以更优化的方式重新排列指令以加快解码速度(x86 的旧变体只有在序列满足某些约束的情况下才能并行解码多个指令(或将它们更紧密地打包在处理器的指令缓冲区中。引用Robert Morgan的"构建优化编译器":
The compiler should schedule the insns as if the processor were
not an out-of-order execution processor. The more effective this
schedule is, the larger the size of the effective insns buffer.
在实践中,胜利通常很小(百分之几(。
这实际上不是一个调度决策本身,而是一个优化。基本上,查看LLVM的addILPOpts((为OOO超标量后端添加的传递可以很好地了解什么是可能的。早期的 if 转换是生成将并行运行的代码,并避免必须串行运行的代码。
LLVM具有用于OOO超标量的EarlyIfConverter通行证。它被PowerPC,X86,AMDGPU,SystemZ和AArch64后端使用。EarlyIfConverter 并行计算两个表达式,并插入一个选择来选择一个:TII->insertSelect(...(
// Early if-conversion is for out-of-order CPUs that don't have a lot of
// predicable instructions. The goal is to eliminate conditional branches that
// may mispredict.
//
// Instructions from both sides of the branch are executed speculatively, and a
// cmov instruction selects the result.
此传递由后端在 addILPOpts(( 中添加。它使用 ILP并行评估两个备选方案,而不是有条件地评估一个,然后再评估另一个。