[Vm-dev] questions about a couple of primitives
eliot.miranda at gmail.com
Mon Aug 31 20:40:09 UTC 2020
On Mon, Aug 31, 2020 at 1:35 PM Florin Mateoc <florin.mateoc at gmail.com>
> Hi Eliot,
> Thank you for your reply and for the extra details about #instVarAt:.
> To answer your question, I would have expected the primitive to fail for
> non-pointers objects, as I associate in my mind instvars with named
> instvars, but I am not invested in this (mis)association, so thank you for
> correcting it.
> But regarding #ifCurtailed, I know that primitives198 1nd 199 are not
> "real", I was referring to something else. The primitive failure code in
> the method #ifCurtailed never touches the argument, regardless of the path
> taken. I think that is incorrect, even if only for documentation purposes.
I don't understand. I see no error code. We're talking about this method
"Evaluate the receiver with an abnormal termination action.
Evaluate aBlock only if execution is unwound during execution
of the receiver. If execution of the receiver finishes normally do
not evaluate aBlock. N.B. This method is *not* implemented as a
primitive. Primitive 198 always fails. The VM uses prim 198 in a
context's method as the mark for an ensure:/ifCurtailed: activation."
| complete result |
result := self valueNoContextSwitch.
complete := true.
> All the best,
> On Mon, Aug 31, 2020 at 3:42 PM Eliot Miranda <eliot.miranda at gmail.com>
>> Hi Florin,
>> On Mon, Aug 31, 2020 at 7:30 AM Florin Mateoc <florin.mateoc at gmail.com>
>>> 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
>> 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,
>> best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Vm-dev