为什么分支指令使用状态寄存器



在大多数指令集中,分支指令是基于状态寄存器上的标志执行的。

在高级语言中,布尔值是一种数据类型,可以由计算表达式(通常为 ==,!=,<,>,>=,<= 和其他变体)创建并存储在变量中。

为什么程序集选择在状态标志上分支,而不是将布尔结果存储在寄存器中的指令?

考虑到允许将测试结果存储在变量中的语言数量,我认为指令集会随之发展,从而可能创建更高效的程序。

这种将测试结果存储到状态寄存器中然后在标志上分支的惯例仅仅是传统,还是有理由认为基于标志的方法比能够直接从通用寄存器存储和测试更有效?

如果你想"将布尔结果存储在寄存器中",你需要单独的指令来<<=>>=等。您还需要一组用于有符号比较,另一组用于无符号比较。

在 x86 指令集中,所有这些指令都有一个指令,cmp(设置一堆标志),然后你使用适当的jxxsetxx指令来查看你感兴趣的标志(例如,对于条件跳转,你使用 jbjbe/jnajajae/jnb 表示无符号("下面"/"上面"), 和jljle/jngjgjge/jnl为签名("更少"/"更大"))。

这种方法的另一个特点是,您可以在执行突变操作(例如携带加法或借用减法)后实际检查标志。示例(假设esi指向一个 128 位数字,该数字正在添加到edi指向的另一个 128 位数量):

add [edi], [esi]
adc [edi+4], [esi+4]
adc [edi+8], [esi+8]
adc [edi+12], [esi+12]
jc overflow

这只是 5 条指令(好吧,假设;实际上,x86 指令不能同时将两个操作数都作为地址,一个必须是寄存器,这意味着加载该寄存器将需要更多的指令)。使用"寄存器中的布尔结果"方法听起来会更复杂(但我想不会太多,如果你使用的是允许三向加法的指令集)。

Chris的回答是正确的。我只想补充一点,除了不想为了保存比较结果而捆绑一个大的寄存器之外,标志位自然会从算术寄存器操作中掉出来,而专门指定的标志寄存器对他们来说是一个好地方。

每当在两个寄存器之间执行加法时,都可能溢出到进位,而减法只是加法的变体。

寄存器的高阶位,如果数字是二进制补码,则为符号位。

此外,在每次加法/减法之后,一些特殊的硬件会检测结果是否全部为零,这是另一个要进入标志的地方。

所有的算术比较都归结为这些的组合,因此它们可以很容易地用于条件分支、长整数数学等。

我最喜欢的基础硬件示例是Harry Porters的中继计算机。在这里,您可以看到标志寄存器如何真正帮助最小化硬件并简化指令集。

首先,您认为布尔答案和标志之间的区别是什么? 真/假与真/假? 一样。 高级语言在变量中浪费了一大堆位,因此基本上其中一个位保存布尔结果。 现实情况是,只有效率非常低的编译器才会在寄存器中生成并浪费所有这些位。 通常使用条件上的单个或一系列比较和分支,并且不使用gpr来实现该高级语言。 其他时候,根据布尔值的复杂性,肯定会使用 GPR,并且使用布尔 alu 运算和其他 GPR 来计算该布尔结果,然后有一个最终比较,如果零和分支如果零或不完成任务(如果你不打算比较它并根据比较做一些事情,你为什么要计算布尔值? 优化器将删除所有否则代码)。

典型的方法是从 alu 操作中掉出的四个标志,零、负、进位(又名无符号溢出,又名借用)和签名溢出。 新西兰文化中心。 然后是有条件指令的分支洗衣清单。 您具有在任何 alu 操作上计算条件的效率。 大多数 alu 操作都会刻录寄存器输出,即使您不关心该输出也是如此。 但通常比较指令(减去而不保存结果)对于大多数条件就足够了,并且存在。 有时,如果幸运的话,您会得到一个测试指令(AND 不保存结果)。 大多数时候你知道比较,它只是一个比较一个条件分支。 有时,您可以设置一次标志,然后连续执行两个或多个条件分支,而不必重新计算条件标志,它们通过失败的分支保留。 这是例外,而不是规则。 旗帜是免费赠品可能是这是流行方法的原因。

有一个比较指令的洗衣清单是相当合理的,如果 a == b,如果 a

我知道有一个 psr 是一个 gpr,这意味着它真的不是 gpr,因为它很特殊,但它像 gpr 一样被使用/访问。 所以你的 alu 输出在 gpr 中删除了这些标志,没有条件分支的洗衣列表,相反,我认为有两个,如果寄存器 Y 中的位 X 被设置,则分支,或者如果寄存器 Y 中的位 X 未设置,则分支。(如果设置了 y 中的位 x,则可能比 SKIP 更糟糕,如果未设置 y 中的位 x,则可能比跳过更糟糕) 并且您必须连续将一个或多个分支组合在一起以获得更复杂的分支(如果相等或大于则分支)。

据我所知,有一个没有任何标志,它基本上有一个比较和跳跃,如果相等,比较和跳跃,如果不相等。 基于寄存器。 您必须合成所有其他条件,有符号或无符号溢出,n位等。 同时烧掉gprs和指令周期,效率非常低。 我可以看到其中的美丽,同时讨厌不得不烧掉寄存器和这么多周期所涉及的痛苦。 我认为目标是避免将处理器状态标志从一个指令携带到另一个指令,并让管道处理这个问题(由于合成 alu 标志所涉及的所有数学的中间结果,权衡是更多的管道危险)。

几乎任何处理器都可以使用 gprs 和 alu 运算完成所有布尔工作,导致寄存器要么为零,要么不为零,然后你可以根据处理器执行最后的一两条指令,如果该寄存器为零跳转。 如果您不想,则不必在比较上使用分支的洗衣清单。

最重要的是,在gprs中存储单比特结果是一个巨大的浪费。 我希望你能理解这是低效的,所以你的论点一直提到使用 gprs 是 IMO 有缺陷的。 某种标志之所以有效,仅仅是因为它们不使用 gprs。 无论是没有标志(在一个指令中比较和跳转)一个标志,还是多个标志。 自己动手与很多比较,很少的分支与很多分支和免费比较都有其优点和缺点。 我认为四旗方法是最受欢迎的,因为旗帜是alu免费赠品,而且,因为我们长期以来一直习惯这样做。

最新更新