6

我正在阅读这个问题,它已被接受。我阅读了评论,但我无法弄清楚产生优化的原因。

为什么在使用以下代码时,汇编代码中会出现分支?

x >= start && x <= end

编辑:
为清楚起见,我想了解接受的答案产生的优化原因。据我了解,这是编译器生成的汇编代码中存在的分支。我想了解为什么生成的代码中有一个分支。

4

1 回答 1

10

请注意,链接的问题具有根本不同的表达方式

x >= start && x++ <= end

这是根本不同的,因为这里的第二个子表达式有副作用。我会解释的。

请注意,这&&是一个短路运算符。这意味着如果x >= start评估为false,则机器可以对 的评估进行分支x <= end

更准确地说,当编译器发出指令时x >= start && x <= end,它可以发出指令以在评估为假x <= end时进行分支。x >= start

但是,我强调在上述陈述中使用了can这个词。这样做的原因是因为x <= end没有副作用,因此编译器是否分支对其进行评估并不重要。

但是,如果第二个表达式确实有副作用,编译器必须对其进行分支。因为&&是一个短路运算符,所以 in a && b, ifb有任何副作用,如果计算结果为;则不能观察到它们。这是 C 和大多数(如果不是所有类似 C 的语言)中短路的要求。afalse

所以,特别是当你看

define POINT_IN_RANGE_AND_INCREMENT(p, range) 
    (p <= range.end && p++ >= range.start)

请注意,第二个表达式p++ >= range.start有副作用。即,(后)递增p。但是只有在评估为1时才能观察到这种副作用。因此,编译器必须对if评估为 false的评估进行分支。p <= range.endtruep++ >= range.startp <= range.end

这导致分支的原因是因为机器要评估该表达式,它使用这样一个事实,即如果p <= range.end评估为false,那么它会自动知道整个表达式的评估结果为false,因此它不应该评估p++ >= range.start,因为它有副作用。因此,它必须对表达式的第二部分求值进行分支。所以在装配中:

Ltmp1301:
 ldr    r1, [sp, #172] @ 4-byte Reload
 ldr    r1, [r1]
 cmp    r0, r1
 bls    LBB44_32
 mov    r6, r0         @ if the result of the compare is false 
 b      LBB44_33       @ branch over evaluating the second expression
                       @ to avoid the side effects of p++
LBB44_32:
 ldr    r1, [sp, #188] @ 4-byte Reload
 adds   r6, r0, #1
Ltmp1302:
 ldr    r1, [r1]
 cmp    r0, r1
 bhs    LBB44_36

非常感谢Oli Charlesworth的富有洞察力的评论。

于 2013-06-14T17:48:55.947 回答