I recently released SuffixConditionals for Visualworks Smalltalk. In the release announcement I said:

Also, the standard variants are optimized in the VM so using the suffix variants will introduce a slight performance penalty.

It turns out that this statement was not entirely correct. The standard variants are optimized as I said. However, the optimization is done by the compiler, not the VM. The code for the optimization is in the Smalltalk image, making it accessible without having to compile and distribute a modified VM.

In Visualworks Smalltalk, there is a class hierarchy rooted at ProgramNode. These classes represent the nodes in the abstract syntax tree (AST) that the Smalltalk compiler produces. A few of these classes, such as LoopNode and ConditionalNode, have a comment that states, “Instances of the class are only created temporarily for code generation.”

Tracing back from the references to these classes, you end up at MessageNode, which represents a message send. MessageNode maintains a registry of MacroSelectors – message sends that can be transformed into more optimal ASTs before generating code. The registry is initialized in MessageNode class>>initialize; there, you can see all of the message sends that are optimized.

MessageNode implements all of the emit* methods used to generate code. The first time one of these messages is sent to the node, it attempts to “expand” any MacroSelectors and then continues to generate the code from the expanded AST.

In order to optimize a message that isn’t already handled by the base VW image, it is necessary to add the message selector to the registry and to implement a method for transforming the AST. The latest version of SuffixConditionals (version 4 as of this writing) does this for if:, unless:, and if:else:.

So now, using SuffixConditionals no longer imposes the performance penalty I warned about earlier.

These optimizations generally yield a significant performance boost. The downside, though, is that the standard Smalltalk search tools (like “find implementers” and “find senders”) operate on compiled methods, so are looking at the optimized code, not the original source. If you look in your image for senders of ifTrue:, for example, you’ll only see references to unoptimized sends; most sends of ifTrue: won’t show up in the list. The price is generally worth it, but you need to keep this in mind when navigating your Smalltalk code.

Thanks to Steffen Märcker for implementing this optimization for SuffixConditionals and contributing it back to the project, and for teaching me something about Smalltalk that I didn’t know before.