优化 ifTrue:ifFalse:在 Smalltalk 中

Optimising ifTrue:ifFalse: in Smalltalk

Smalltalk Blue Book 说编译器优化了 ifTrue:ifFalse: 和使用特殊操作码的朋友,以避免必须创建闭包并对实际布尔对象进行动态调度。

如果我理解正确,那么:

o ifTrue: [ ^ 'yes' ] ifElse: [ ^ 'no' ]

...变成类似:

    push o
    jump_if_false 1
    return 'yes'
    jump 2
1:
    return 'no'
2:

(我检查过,GNU Smalltalk 正是这样做的。)

如果 oBoolean,这会很好地工作,但我不明白如果不是的话会发生什么。毕竟,编译器 不知道o 是什么类型。它不能保证它是布尔值。它甚至不知道 ifTrue:ifFalse: 方法是否具有传统语义。

例如:

Fnord ifTrue: tb ifFalse: fb
    "muhahaha"
    tb value: self.
    fb value: self and: 9.
    ^ 7

要使此实现起作用,true 和 false 块 必须 是闭包,分别采用一个和两个参数。那么编译器如何知道生成闭包呢?还是允许编译器在这种情况下中断?

这是字节码(在我使用的方言中)

1  frameless prolog
2  load R with instance o       ; o is an ivar in my code
4  test jump false 13 
7  load R with literal 'yes'
9  return
10 jump 16
13 load R with literal 'no'
15 return
16 return self

这是本地化代码(简体):

    cmp eax, false       ; test jump false
    jz @2
    cmp eax, true
    jz @1
    call mustBeBoolean   ; <--- here!
@1: mov eax, 'true'
    ret
    jmp @3               ; jump
@2: mov eax, 'no'
    ret
@3: move eax, esi
    ret

如您所见,是 JIT 编译器(a.k.a.nativizer)检查 #ifTrue:ifFalse: 的接收者是否为布尔值。更准确地说,如果接收者不是布尔值,test jump false 字节码的原生化会发出将发送 mustBeBoolean 通知的代码。

编辑

关于你的第二个问题,如果发件人(明确地)内联块参数,则另一个 class 中的 #ifTrue:ifFalse: 的实现将不起作用:

receiver ifTrue: ['yes'] ifFalse: ['no']

如果 receiverFnord 的实例(即使 receiver 理解 #ifTrue:ifFalse:,此代码将失败并返回 #mustBeBoolean。原因是,在这种情况下,编译器将像我们上面看到的那样优化 #ifTrue:ifFalse: 而不是将其作为常规消息发送。

但是,表达式

receiver ifTrue: [:a | <whatever>] ifFalse: [:a :b | <your code>]

fa := [:a | <whatever>].
fb := [:a :b | <yourcode>].
receiver ifTrue: fa ifFalse: fb

不会优化,意思是#ifTrue:ifFalse:会发