优化ifTrue:ifFalse:在Smalltalk中

Dav*_*ven 5 smalltalk

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

如果我理解正确的话,那么:

o ifTrue: [ ^ 'yes' ] ifElse: [ ^ 'no' ]
Run Code Online (Sandbox Code Playgroud)

...变得像:

    push o
    jump_if_false 1
    return 'yes'
    jump 2
1:
    return 'no'
2:
Run Code Online (Sandbox Code Playgroud)

(我查了一下,GNU Smalltalk正是这样做的.)

如果o是a Boolean,这将工作正常,但我不明白如果不是会发生什么.毕竟,编译器不知道是什么类型o.它不能保证它是一个布尔值.它甚至不知道该ifTrue:ifFalse:方法是否具有传统的语义.

例如:

Fnord ifTrue: tb ifFalse: fb
    "muhahaha"
    tb value: self.
    fb value: self and: 9.
    ^ 7
Run Code Online (Sandbox Code Playgroud)

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

Lea*_*lia 4

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

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
Run Code Online (Sandbox Code Playgroud)

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

    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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,JIT 编译器(又名 nativizer)检查 的接收者是否#ifTrue:ifFalse:是布尔值。更准确地说,字节码的本地化test jump false会发出代码,mustBeBoolean如果接收者不是布尔值,则该代码将发送通知。

编辑

关于你的第二个问题,#ifTrue:ifFalse:如果发送者内联(显式)块参数,则在另一个类中的实现将不起作用,如下所示:

receiver ifTrue: ['yes'] ifFalse: ['no']
Run Code Online (Sandbox Code Playgroud)

这段代码将会失败,#mustBeBoolean因为 ifreceiver是一个实例Fnord(即使receiver理解#ifTrue:ifFalse:)。原因是,在这种情况下,编译器将#ifTrue:ifFalse:像我们上面看到的那样进行优化,而不是将其作为常规消息发送。

然而,表达式

receiver ifTrue: [:a | <whatever>] ifFalse: [:a :b | <your code>]
Run Code Online (Sandbox Code Playgroud)

和

fa := [:a | <whatever>].
fb := [:a :b | <yourcode>].
receiver ifTrue: fa ifFalse: fb
Run Code Online (Sandbox Code Playgroud)

不会被优化,这意味着#ifTrue:ifFalse:将被发送。