[Vm-dev] questions about a couple of primitives

Eliot Miranda eliot.miranda at gmail.com
Mon Aug 31 19:42:13 UTC 2020

Hi Florin,

On Mon, Aug 31, 2020 at 7:30 AM Florin Mateoc <florin.mateoc at gmail.com>

> Hi,
> I had some unexpected results while looking at some primitives in the
> system, and I wanted to ask if they are expected/intentional:
> 1. 'ab' instVarAt: 1    =>    97    (and 'ab' instVarAt: 2    =>    98)
>     I would have thought that #instVarAt: has pointer granularity, so I am
> curious if the observed behavior is an accident or intentional (and maybe
> even used somewhere)

Since a ByteString's indexed inst vars are bytes this is as expected.  What
would you have expected?  Smalltalk-80 has always behaved this way.

There is a little more to this story.  In Spur, instVarAt:[put:] is
actually implemented by a new primitive slotAt:[put:].  Why?

Spur has a lazy become scheme which means that become is implemented by
morphing objects into forwarders to copies of objects.  So if a become: b,
then the system allocated copies of a and b, say a' and b', and morphs a
into a forwarder to b', and b into a forwarder to a'.  Forwarders are
followed lazily, either when a message is sent to a forwarder or when a
primitive encounters a forwarder somewhere within the objects it consumes.
When a primitive fails the VM scans the input arguments to a depth specific
to the primitive and if it finds references to forwarders, fixes them up to
point to the targets of the forwarders, and retries the primitive.  The old
implementation of instVarAt:[put:] had primities that failed for indexes
beyond the named instance variables, and handled indexed inst vars in
primitive failure code:

Object>>instVarAt: index
"Primitive. Answer a fixed variable in an object. The numbering of the
variables corresponds to the named instance variables. Fail if the index
is not an Integer or is not the index of a fixed variable. Essential. See
Object documentation whatIsAPrimitive."

<primitive: 73>
"Access beyond fixed variables."
^self basicAt: index - self class instSize

Chris Muller uses instVarAt:[put:] on large arrays in his Magma database.
He was noticing a severe slow down in Magma on Spur because
instVarAt:[put:] was failing, the entire Array was being scanned for
forwarders, and then the primitive actually failed and the basicAt:put: ran.

The solution to this was to replace primitives 73 & 74 with the new
slotAt:[put:] primitives 173 & 174. Now the primitive does not fail, and
performance is restored (and much improved because Spur is faster).

> 2. | o | o := Object new. (WeakArray with: o) pointsTo: o    =>    true
>     I thought the main use case for #pointsTo: was to find hard references
> (e.g. for chasing memory leaks). The current behavior actually makes that
> use case a little more difficult to implement, since you have to special
> case weak references. When would one be interested in finding weak
> references?

I can't answer this.  The design decision was made a whole ago.  It would
be easy to add pointsStronglyTo: and implement that correctly.  Remember
that references from named inst vars of weak objects are strong references.
Only references from indexed inst vars of weak objects are weak.

> 3. The comment and the primitive used in #ensure: and #ifCurtailed: are
> the same, but the primitive failure code is different - the one for
> #ifCurtailed seems buggy, it never evaluates the argument

Ah, this is a neat hack.  The primitive numbers are not actually
primitives,  These primitives always fail, and the blocks are evaluated
with the valueNoContextSwitch send in the method body.  Instead the
primitive numbers are used by the VM to mark the activations of ensure: and
ifCurtailed: as unwind-protect frames.  This was one of Andreas' neatest
hacks (am I right in thinking this was Andreas Raab's scheme?), in that he
added unwind-protect without needing e.g. another status bit in the
CompiledMethod header.  He could just use the primitive number that was
already there.

As far as ifCurtailed: not evaluating its argument, that is its semantics.
 ensure: always evaluates its argument, after evaluating its body.
 ifCurtailed: only evaluates its argument if a non-local return or
exception return is taken and the normal return path is not taken.  See
Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks.

None of the above are critical, but I am curious about them.
> Thank you in advance for any clarifications,
> Florin

best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20200831/96b44bd4/attachment.html>

More information about the Vm-dev mailing list