Strongtalk
Implementation

Why the VM was so fast

Strongtalk's speed did not come from its types. It came from watching the program run.

Every value is a tagged word

In the 32-bit VM every value fits in one machine word. The low two bits say what it is: 00 for a SmallInteger, whose value is the other 30 bits, and 01 for a pointer to an object in the heap. So the range of a SmallInteger is −536,870,912 to 536,870,911; anything bigger becomes a LargeInteger, automatically and invisibly. Adding two SmallIntegers is a couple of machine instructions, with no memory allocated.

Primitives with failure codes

Basic operations — arithmetic, indexing, creating objects — are primitives written inside the VM. Smalltalk code calls them with a special syntax and says what to do if they fail:

+ other <Number> ^<Number>
    ^{{self primitiveAdd: other
        ifFail: [:err <Symbol> | self retry: #+ coercing: other]}}

A primitive that cannot do its job answers a symbol such as #SmallIntegerOverflow, #DivisionByZero, #OutOfBounds or #FirstArgumentHasWrongType — the VM defines 136 of them — and the Smalltalk code decides what happens next, for example redoing the sum with LargeIntegers.

Interpret first, then optimize what matters

New code starts in a fast interpreter. Every message send site keeps an inline cache recording the classes of the objects it has seen and the method each one used. Counters notice methods that run often. When a method gets hot, the optimizing compiler reads those caches — the type feedback — and compiles specialised machine code: it inlines the methods actually used, replaces sends with direct calls and keeps the common case on a fast path, with a check that falls back if a surprise turns up.

The compiler also recognises the core control messages — ifTrue:, ifFalse:, and:, or:, whileTrue:, to:by:do: and value — and turns them into plain jumps when their arguments are literal blocks.

Deoptimization

Optimized code makes assumptions. When one stops holding — a class is redefined, a method changed — the VM turns optimized activations back into ordinary ones and carries on. That is what let Strongtalk keep Smalltalk's live, change-anything development style while running fast.

Why the types weren't used for speed

It sounds natural to use declared types to generate faster code, but Strongtalk deliberately didn't. Types are optional and describe protocols, not classes, so they would rarely say exactly which method will run. Run-time feedback says precisely that. Keeping the two separate meant the types could stay purely about correctness — and the same ideas could be carried into Java, where they became HotSpot.

This site's edition keeps the tagged-integer range, the primitives and their failure codes, and inlines the same control messages; it runs bytecode in an interpreter rather than generating machine code. See how this edition was made.