投稿人:Jacob Bramley,2010 年 8 月 5 日
if/else
在前一篇博文中(“条件代码 1”),我阐述了某些指令可以设置一些全局条件代码,这些代码可用于有条件地执行代码。我对用法举了些例子。其中一个示例是 C 语言 if/else 结构的汇编实施:
cmp r0, #20
bhi do_something_else
do_something:
@ This code runs if (r0 <= 20).
b continue @ Prevent do_something_else from executing.
do_something_else:
@ This code runs if (r0 > 20).
continue:
@ Other code.
该示例是有效的,可以在任何 ARM 核心上运行。不过,如果在每一情形中仅需要执行一两条指令,这是高效的解决方案吗?请考虑以下 C 代码:
if (a >= 10) {
a = 10;
} else {
a = a + 1;
}
显然,该代码将递增 a 直到达到或超过限值 10,此时它将被设为 10。将它映射到我们的 if/else 示例,这可能会按照如下所示在汇编中实施:
a
cmp r0, #10
blo r0_is_small
r0_is_big:
mov r0, #10
b continue
r0_is_small:
add r0, r0, #1
上述代码执行两条指令中的一条,即 mov 或 add。然而,它使用两条分支指令来实现。如果没有分支预测,执行这些分支将需要花费几个周期。即使使用分支预测,这种模式可能也不容易预测。最后,即便有完美的分支预测,每条分支指令占 4 个字节指令内存,因此代码大小可能会成为问题。
mov
add
ARM 指令集的一个特点是,几乎每一个指令编码都包含代表条件代码的 4 位字段。如果通过指令附带的条件,则执行该指令。否则,它没有作用,就如您使用了 nop 指令一样。借助这一知识,我们可以按照如下所示更高效地实施上一示例:
nop
movhs r0, #10
addlo r0, r0, #1
在 ARM 指令集中,条件代码利用指令中的 4 位字段进行编码。该编码中的 3 个位用于标识运算,第 4 位则反转条件。例如,eq 条件正好与 ne 条件相反。JIT 编译器的作者或许有兴趣了解条件代码中最低有效位可以反转,来获得相反的条件代码。例如,eq(等于)编码为 '0000',而 ne (不等于)则编码为 '0001'。这对所有条件代码都有效,但除了 al (始终)条件外,其编码为 '1110'。将指令集的十六分之一分派给从不执行的指令很浪费。于是,指令集的这一部分被用于无法有条件执行的少数指令。
eq
ne
'0000'
'0001'
al
'1110'
以下是 ARM 指令集中始终无条件执行的指令的一些示例:
blx <label>
blx <register>
vadd
pld
dmb
dsb
isb
像往常一样,ARMv7-AR 架构参考手册包含最完整、最准确的信息,指令集参考卡也是如此。
在少数处理器拥有分支预测、代码大小大受限制的时期,条件执行曾经是节省代码空间又提高许多程序中性能的绝佳方式。这对当今实时处理器和微控制器而言,依然如此。不过,ARM 应用级处理器包含分支预测器,常常使基于分支的 if/else 结构比条件指令更加吸引人。预测的分支可能成本很低,在一些情形中甚至没有成本。此外,条件执行在一些情形中会妨碍无序执行,因为它会添加额外的指令流依赖项。
在一些情形中,可能很难了解对于特定应用程序应该使用条件执行还是传统的条件分支。不过,作为一般经验法则,或许最好将条件指令用于包含三条或以下指令的序列,而将分支用于更长的序列。性能最佳的解决方案因处理器而异,因为它们拥有不同的管线和分支预测器设计;此外,也会根据您所使用的具体指令序列而不同。也请注意,最快速的解决方案并不一定是最小的。
在原始的 16 位 Thumb 指令集中,只有分支才可设有条件。Thumb-2 中增加了 it 指令,提供与 ARM 中条件指令相似的功能和行为。Thumb-2 的 it指令也可有条件地执行通常在 ARM 态中无条件执行的一些指令。现在我对此不再赘述,但可能会在未来的博文中加以介绍。
it