questions about a couple of primitives

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
23 messages Options
12
Reply | Threaded
Open this post in threaded view
|

questions about a couple of primitives

Florin Mateoc-4
 
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)

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?

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

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

Florin
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
 
Hi Florin,

On Mon, Aug 31, 2020 at 7:30 AM Florin Mateoc <[hidden email]> wrote:
 
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

HTH
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Florin Mateoc-4
 
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.

All the best,
Florin

On Mon, Aug 31, 2020 at 3:42 PM Eliot Miranda <[hidden email]> wrote:
 
Hi Florin,

On Mon, Aug 31, 2020 at 7:30 AM Florin Mateoc <[hidden email]> wrote:
 
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

HTH
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
 
Hi Florin,

On Mon, Aug 31, 2020 at 1:35 PM Florin Mateoc <[hidden email]> wrote:
 
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 right?

BlockClosure>>ifCurtailed: aBlock
"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 |
<primitive: 198>
result := self valueNoContextSwitch.
complete := true.
^result
 
All the best,
Florin

Cheers!
 

On Mon, Aug 31, 2020 at 3:42 PM Eliot Miranda <[hidden email]> wrote:
 
Hi Florin,

On Mon, Aug 31, 2020 at 7:30 AM Florin Mateoc <[hidden email]> wrote:
 
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

HTH
_,,,^..^,,,_
best, Eliot


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Florin Mateoc-4
 
Hi Eliot,

Sorry about top-posting previously. Stupid Gmail!

Eliot Miranda via squeak.org 

4:40 PM (4 minutes ago)


to Open

On Mon, Aug 31, 2020 at 4:40 PM Eliot Miranda <[hidden email]> wrote:
 
Hi Florin,

On Mon, Aug 31, 2020 at 1:35 PM Florin Mateoc <[hidden email]> wrote:
 
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 right?

BlockClosure>>ifCurtailed: aBlock
"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 |
<primitive: 198>
result := self valueNoContextSwitch.
complete := true.
^result
 



I did not mention an error code, I was talking about the "primitive failure code", referring to the Smalltalk code that gets invoked when the primitive fails (the three rows after the pragma).
This Smalltalk code does not touch the method's argument (aBlock), so it looks as if aBlock is never evaluated

Florin
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

timrowledge
In reply to this post by Eliot Miranda-2
 


> On 2020-08-31, at 12:42 PM, Eliot Miranda <[hidden email]> wrote:
>
> 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.

The exception handling was added between 2.3 & 2.6 back in 1999. The TFEI guys did a large chunk of it, I'm just about certain Craig came up with the #valueUninterruptably cleverness to make it work on VMs without  direct support, I did the original VM support and using the 'fake' prim was probably the result of some discussions on how to tag contexts easily  between a bunch of us.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Strange OpCodes: XER: Exclusive ERror


Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Florin Mateoc-4
In reply to this post by Florin Mateoc-4
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

On Mon, Aug 31, 2020 at 4:51 PM Florin Mateoc <[hidden email]> wrote:
Hi Eliot,

Sorry about top-posting previously. Stupid Gmail!

Eliot Miranda via squeak.org 

4:40 PM (4 minutes ago)


to Open

On Mon, Aug 31, 2020 at 4:40 PM Eliot Miranda <[hidden email]> wrote:
 
Hi Florin,

On Mon, Aug 31, 2020 at 1:35 PM Florin Mateoc <[hidden email]> wrote:
 
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 right?

BlockClosure>>ifCurtailed: aBlock
"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 |
<primitive: 198>
result := self valueNoContextSwitch.
complete := true.
^result
 



I did not mention an error code, I was talking about the "primitive failure code", referring to the Smalltalk code that gets invoked when the primitive fails (the three rows after the pragma).
This Smalltalk code does not touch the method's argument (aBlock), so it looks as if aBlock is never evaluated

Florin
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
 


On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.

On Mon, Aug 31, 2020 at 4:51 PM Florin Mateoc <[hidden email]> wrote:
Hi Eliot,

Sorry about top-posting previously. Stupid Gmail!

Eliot Miranda via squeak.org 

4:40 PM (4 minutes ago)


to Open

On Mon, Aug 31, 2020 at 4:40 PM Eliot Miranda <[hidden email]> wrote:
 
Hi Florin,

On Mon, Aug 31, 2020 at 1:35 PM Florin Mateoc <[hidden email]> wrote:
 
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 right?

BlockClosure>>ifCurtailed: aBlock
"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 |
<primitive: 198>
result := self valueNoContextSwitch.
complete := true.
^result
 



I did not mention an error code, I was talking about the "primitive failure code", referring to the Smalltalk code that gets invoked when the primitive fails (the three rows after the pragma).
This Smalltalk code does not touch the method's argument (aBlock), so it looks as if aBlock is never evaluated

Florin


--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Florin Mateoc-4
 



On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:
 


On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.



Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm

Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Chris Muller-3
In reply to this post by Eliot Miranda-2
 
Eliot,

> 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).

I just checked, apparently I'm still using my own #slotAt:[put:] from
2014, which reads:

slotAt: anInteger
    "Flat slot access.  Answer the object referenced by the receiver
at its anInteger'th slot."
    | namedSize |
    ^ anInteger > (namedSize:=self class instSize)
        ifTrue: [ self basicAt: (anInteger-namedSize) ]
        ifFalse: [ self instVarAt: anInteger ]

The timestamp on #instVarAt: using primitve 173 is 2017, so it sounds
like I can go back to simply using #instVarAt: instead of my own
#slotAt:, do you agree?

Thanks.

 - Chris
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Florin Mateoc-4
In reply to this post by Florin Mateoc-4
 


On Mon, Aug 31, 2020 at 6:29 PM Florin Mateoc <[hidden email]> wrote:



On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:
 


On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.



Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm


To be more pedantic, neither #ensure: nor #ifCurtailed: disclose what is really happening on the unwind path, but at least #ensure: shows some code that conceptually matches its semantics.
In both cases, there is magic happening inside #valueNoContextSwitch, which, although it does not take any arguments, it knows how to (call a method that knows how to) invoke, if necessary, its caller's argument.
Yes, by walking the stack and peeking inside the contexts' temps and then acting upon them anything is possible, but the resulting code is anything but readable.

I would argue for passing the #ensure: and #ifCurtailed: arguments to the #valueNoContextSwitch method/primitive, thus making it possible to avoid #resume:through: - I think such methods are fine for simulation/debugger, but not for runtime.
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
In reply to this post by Florin Mateoc-4
 


On Aug 31, 2020, at 3:30 PM, Florin Mateoc <[hidden email]> wrote:





On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:
 


On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.



Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm

No it isn’t.  The vm detects an attempt to unwind across an unwind protect (either ensure: if ifCurtailed:), and sends aboutToReturn:to: to the current context.  Everything happens up in the image from that point.
For the third time see Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks.


Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
In reply to this post by Chris Muller-3
 
Hi Chris,

> On Aug 31, 2020, at 4:04 PM, Chris Muller <[hidden email]> wrote:
>
> 
> Eliot,
>
>> 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).
>
> I just checked, apparently I'm still using my own #slotAt:[put:] from
> 2014, which reads:
>
> slotAt: anInteger
>    "Flat slot access.  Answer the object referenced by the receiver
> at its anInteger'th slot."
>    | namedSize |
>    ^ anInteger > (namedSize:=self class instSize)
>        ifTrue: [ self basicAt: (anInteger-namedSize) ]
>        ifFalse: [ self instVarAt: anInteger ]
>
> The timestamp on #instVarAt: using primitve 173 is 2017, so it sounds
> like I can go back to simply using #instVarAt: instead of my own
> #slotAt:, do you agree?

I do :-)

>
> Thanks.
>
> - Chris
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
In reply to this post by Florin Mateoc-4
 


On Aug 31, 2020, at 5:11 PM, Florin Mateoc <[hidden email]> wrote:




On Mon, Aug 31, 2020 at 6:29 PM Florin Mateoc <[hidden email]> wrote:



On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:
 


On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.



Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm


To be more pedantic, neither #ensure: nor #ifCurtailed: disclose what is really happening on the unwind path, but at least #ensure: shows some code that conceptually matches its semantics.
In both cases, there is magic happening inside #valueNoContextSwitch, which, although it does not take any arguments, it knows how to (call a method that knows how to) invoke, if necessary, its caller's argument.
Yes, by walking the stack and peeking inside the contexts' temps and then acting upon them anything is possible, but the resulting code is anything but readable.

I would argue for passing the #ensure: and #ifCurtailed: arguments to the #valueNoContextSwitch method/primitive, thus making it possible to avoid #resume:through: - I think such methods are fine for simulation/debugger, but not for runtime.

-1. Putting this in the vm adds a whole level of execution suspension & resumption which isn’t there.  Since Smalltalk has first class activation records it can (and does) elegantly implement a number of very complex control structures (such as unwind protect evaluation, and exception delivery) above the vm.
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Florin Mateoc-4
 


On Mon, Aug 31, 2020 at 8:25 PM Eliot Miranda <[hidden email]> wrote:
 


On Aug 31, 2020, at 5:11 PM, Florin Mateoc <[hidden email]> wrote:




On Mon, Aug 31, 2020 at 6:29 PM Florin Mateoc <[hidden email]> wrote:



On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:
 


On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.



Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm


To be more pedantic, neither #ensure: nor #ifCurtailed: disclose what is really happening on the unwind path, but at least #ensure: shows some code that conceptually matches its semantics.
In both cases, there is magic happening inside #valueNoContextSwitch, which, although it does not take any arguments, it knows how to (call a method that knows how to) invoke, if necessary, its caller's argument.
Yes, by walking the stack and peeking inside the contexts' temps and then acting upon them anything is possible, but the resulting code is anything but readable.

I would argue for passing the #ensure: and #ifCurtailed: arguments to the #valueNoContextSwitch method/primitive, thus making it possible to avoid #resume:through: - I think such methods are fine for simulation/debugger, but not for runtime.

-1. Putting this in the vm adds a whole level of execution suspension & resumption which isn’t there.  Since Smalltalk has first class activation records it can (and does) elegantly implement a number of very complex control structures (such as unwind protect evaluation, and exception delivery) above the vm.

Sorry, I disagree. And I would not put exception delivery in the same boat - that one does exactly what you say, it implements something in the image, above the vm, not just elegantly, but also putting more power in the hands of the developer. But everything is out there in the open, browsable, readable, debuggable and easily understandable.
In this case we have a message sent to a context by the vm, which is not what I would call above the vm. If one browses the code, reads the code and the comments, of both #ifCurtailed: and #valueNoContextSwitch, nothing makes sense, there is no indication whatsoever of what is happening under the hood, no mention of the message that the vm sends to the context. Nor is anything visible in the debugger. Maybe this is indeed all justified for performance reasons, and since I am not the one who implemented it or who would implement an alternative, you can of course ignore my opinion. It is but a tiny corner of the image. But I think it is an important one, and you seem to deny that there is even any readability problem with these methods. Oh, well

Anyway, I only now noticed that the discussion is off list - if I did that, it was unintentional, I just clicked reply without checking that it goes to list or not
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

fniephaus
 
On Tue, Sep 1, 2020 at 5:32 AM Florin Mateoc <[hidden email]> wrote:

>
>
>
>
> On Mon, Aug 31, 2020 at 8:25 PM Eliot Miranda <[hidden email]> wrote:
>>
>>
>>
>>
>> On Aug 31, 2020, at 5:11 PM, Florin Mateoc <[hidden email]> wrote:
>>
>> 
>>
>>
>> On Mon, Aug 31, 2020 at 6:29 PM Florin Mateoc <[hidden email]> wrote:
>>>
>>>
>>>
>>>
>>> On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:
>>>>
>>>>
>>>>
>>>>
>>>> On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
>>>>>
>>>>>
>>>>> I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does
>>>>
>>>>
>>>> I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)
>>>>
>>>> As I said earlier,  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.
>>>>
>>>> Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.
>>>>
>>>
>>>
>>> Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm
>>>
>>
>> To be more pedantic, neither #ensure: nor #ifCurtailed: disclose what is really happening on the unwind path, but at least #ensure: shows some code that conceptually matches its semantics.
>> In both cases, there is magic happening inside #valueNoContextSwitch, which, although it does not take any arguments, it knows how to (call a method that knows how to) invoke, if necessary, its caller's argument.
>> Yes, by walking the stack and peeking inside the contexts' temps and then acting upon them anything is possible, but the resulting code is anything but readable.
>>
>> I would argue for passing the #ensure: and #ifCurtailed: arguments to the #valueNoContextSwitch method/primitive, thus making it possible to avoid #resume:through: - I think such methods are fine for simulation/debugger, but not for runtime.
>>
>>
>> -1. Putting this in the vm adds a whole level of execution suspension & resumption which isn’t there.  Since Smalltalk has first class activation records it can (and does) elegantly implement a number of very complex control structures (such as unwind protect evaluation, and exception delivery) above the vm.
>
>
> Sorry, I disagree. And I would not put exception delivery in the same boat - that one does exactly what you say, it implements something in the image, above the vm, not just elegantly, but also putting more power in the hands of the developer. But everything is out there in the open, browsable, readable, debuggable and easily understandable.
> In this case we have a message sent to a context by the vm, which is not what I would call above the vm. If one browses the code, reads the code and the comments, of both #ifCurtailed: and #valueNoContextSwitch, nothing makes sense, there is no indication whatsoever of what is happening under the hood, no mention of the message that the vm sends to the context. Nor is anything visible in the debugger. Maybe this is indeed all justified for performance reasons, and since I am not the one who implemented it or who would implement an alternative, you can of course ignore my opinion. It is but a tiny corner of the image. But I think it is an important one, and you seem to deny that there is even any readability problem with these methods. Oh, well

I must admit it took quite a while to get this right in TruffleSqueak,
and the code for supporting it in both TruffleSqueak and RSqueak is
quite messy (see [1] and [2]). aboutToReturn:through: is not mentioned
in the Blue Book, so it was probably added later on. [3] mentions it
briefly, but I do think better in-image documentation would be very
useful.

Let me rephrase Florin's question: What keeps us from using the
following ifCurtailed: implementation (inspired by Clément's version
in [4])?

==============================
ifCurtailed: curtailBlock
    "{Code comment with good documentation of the mechanism}"
    | result curtailed |
    <primitive: 198>
    curtailed := true.
    [ result := self valueNoContextSwitch.
      curtailed := false ] ensure: [ curtailed ifTrue: [ curtailBlock value ] ].
    ^ result
==============================

Cheers,
Fabio

[1] https://github.com/hpi-swa/trufflesqueak/blob/d1f5bf327d774f30d163f019006fd8df15fc2784/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/nodes/AboutToReturnNode.java#L39
[2] https://github.com/hpi-swa/RSqueak/blob/d33005c8aa7c11b7af349b0585f5faa140c4db72/rsqueakvm/interpreter_bytecodes.py#L543
[3] http://www.mirandabanda.org/cogblog/2009/01/14/under-cover-contexts-and-the-big-frame-up/
[4] http://pharobooks.gforge.inria.fr/PharoByExampleTwo-Eng/latest/Exceptions.pdf

>
> Anyway, I only now noticed that the discussion is off list - if I did that, it was unintentional, I just clicked reply without checking that it goes to list or not
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
In reply to this post by Florin Mateoc-4
 
Hi Florin,

On Aug 31, 2020, at 8:32 PM, Florin Mateoc <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 8:25 PM Eliot Miranda <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 6:29 PM Florin Mateoc <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.


Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm


To be more pedantic, neither #ensure: nor #ifCurtailed: disclose what is really happening on the unwind path, but at least #ensure: shows some code that conceptually matches its semantics.
In both cases, there is magic happening inside #valueNoContextSwitch, which, although it does not take any arguments, it knows how to (call a method that knows how to) invoke, if necessary, its caller's argument.
Yes, by walking the stack and peeking inside the contexts' temps and then acting upon them anything is possible, but the resulting code is anything but readable.

I would argue for passing the #ensure: and #ifCurtailed: arguments to the #valueNoContextSwitch method/primitive, thus making it possible to avoid #resume:through: - I think such methods are fine for simulation/debugger, but not for runtime.

-1. Putting this in the vm adds a whole level of execution suspension & resumption which isn’t there.  Since Smalltalk has first class activation records it can (and does) elegantly implement a number of very complex control structures (such as unwind protect evaluation, and exception delivery) above the vm.

Sorry, I disagree.

There is no need to apologize.  We disagree.  That is clear.

And I would not put exception delivery in the same boat - that one does exactly what you say, it implements something in the image, above the vm, not just elegantly, but also putting more power in the hands of the developer. But everything is out there in the open, browsable, readable, debuggable and easily understandable.
In this case we have a message sent to a context by the vm, which is not what I would call above the vm.

Your criticism doesn’t make sense to me. aboutToReturn:to: is the result of the VM detecting a non-local return attempting to return past and unwind protect frame.  [Fabio, this is not described by the blue book because Smalltalk-80 prior to the late 80’s didn’t have unwind protect; I think I’m right in saying that Peter Deutsch added it to ObjectWorks v5; I don’t know when it was added to Smalltalk-V].  Similarly cannotReturn: is the result of an attempt to return to a nil sender.  cannotResume: is the result of an attempt to result a process with an invalid suspendedContext.  doesNotUnderstand: is the result of an attempt to send a message that is not understood. attemptToAssign:to:withIndex: is the result of an attempt to assign to the inst var of a read-only object.

So aboutToReturn:to: is one of several send-backs from the VM made when errors occur.  There is nothing unusual about it.  It is in house style, harmonious with the overall system architecture.  And that the response, just like the response to the other send-backs, is implemented in Smalltalk does indeed lift things above the vm.

If one browses the code, reads the code and the comments, of both #ifCurtailed: and #valueNoContextSwitch, nothing makes sense, there is no indication whatsoever of what is happening under the hood, no mention of the message that the vm sends to the context. Nor is anything visible in the debugger. Maybe this is indeed all justified for performance reasons, and since I am not the one who implemented it or who would implement an alternative, you can of course ignore my opinion. It is but a tiny corner of the image. But I think it is an important one, and you seem to deny that there is even any readability problem with these methods. Oh, well

I invited you to submit an improved comment.  I pointed you to the implementation of the evaluation of the unwinds.

Anyway, I only now noticed that the discussion is off list - if I did that, it was unintentional, I just clicked reply without checking that it goes to list or not

The discussion is on list.  I hit reply all.  Perhaps I shouldn’t have.
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Florin Mateoc-4
 
Hi Eliot,

On Tue, Sep 1, 2020 at 11:12 AM Eliot Miranda <[hidden email]> wrote:
 
Hi Florin,

On Aug 31, 2020, at 8:32 PM, Florin Mateoc <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 8:25 PM Eliot Miranda <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 6:29 PM Florin Mateoc <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.


Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm


To be more pedantic, neither #ensure: nor #ifCurtailed: disclose what is really happening on the unwind path, but at least #ensure: shows some code that conceptually matches its semantics.
In both cases, there is magic happening inside #valueNoContextSwitch, which, although it does not take any arguments, it knows how to (call a method that knows how to) invoke, if necessary, its caller's argument.
Yes, by walking the stack and peeking inside the contexts' temps and then acting upon them anything is possible, but the resulting code is anything but readable.

I would argue for passing the #ensure: and #ifCurtailed: arguments to the #valueNoContextSwitch method/primitive, thus making it possible to avoid #resume:through: - I think such methods are fine for simulation/debugger, but not for runtime.

-1. Putting this in the vm adds a whole level of execution suspension & resumption which isn’t there.  Since Smalltalk has first class activation records it can (and does) elegantly implement a number of very complex control structures (such as unwind protect evaluation, and exception delivery) above the vm.

Sorry, I disagree.

There is no need to apologize.  We disagree.  That is clear.

And I would not put exception delivery in the same boat - that one does exactly what you say, it implements something in the image, above the vm, not just elegantly, but also putting more power in the hands of the developer. But everything is out there in the open, browsable, readable, debuggable and easily understandable.
In this case we have a message sent to a context by the vm, which is not what I would call above the vm.

Your criticism doesn’t make sense to me. aboutToReturn:to: is the result of the VM detecting a non-local return attempting to return past and unwind protect frame.  [Fabio, this is not described by the blue book because Smalltalk-80 prior to the late 80’s didn’t have unwind protect; I think I’m right in saying that Peter Deutsch added it to ObjectWorks v5; I don’t know when it was added to Smalltalk-V].  Similarly cannotReturn: is the result of an attempt to return to a nil sender.  cannotResume: is the result of an attempt to result a process with an invalid suspendedContext.  doesNotUnderstand: is the result of an attempt to send a message that is not understood. attemptToAssign:to:withIndex: is the result of an attempt to assign to the inst var of a read-only object.

So aboutToReturn:to: is one of several send-backs from the VM made when errors occur.  There is nothing unusual about it.  It is in house style, harmonious with the overall system architecture.  And that the response, just like the response to the other send-backs, is implemented in Smalltalk does indeed lift things above the vm.


While I was not initially aware about #aboutToReturn:to:, it is not so much about the fact that the vm sends a message (as you say, that part is in house style), it is what we do with it. You are right, we are doing it on the image side. Except the in-image part of this whole uses, as I mentioned, simulation/debugger kind of code, which might as well have been done in the vm. It is this one-two punch that makes it different than the other vm sent messages that you listed.

If one browses the code, reads the code and the comments, of both #ifCurtailed: and #valueNoContextSwitch, nothing makes sense, there is no indication whatsoever of what is happening under the hood, no mention of the message that the vm sends to the context. Nor is anything visible in the debugger. Maybe this is indeed all justified for performance reasons, and since I am not the one who implemented it or who would implement an alternative, you can of course ignore my opinion. It is but a tiny corner of the image. But I think it is an important one, and you seem to deny that there is even any readability problem with these methods. Oh, well

I invited you to submit an improved comment.  I pointed you to the implementation of the evaluation of the unwinds.

How about adding something like:

"The abnormal termination is detected by the vm and signalled as an #aboutToReturn:to: message sent to a reified current context, with the context of the #ifCurtailed: invocation as an argument.
The current context then walks the stack to unwind and execute any unwind blocks (including the one protected by the #ifCurtailed: invocation) - see Context>>#resume:through"


Anyway, I only now noticed that the discussion is off list - if I did that, it was unintentional, I just clicked reply without checking that it goes to list or not

The discussion is on list.  I hit reply all.  Perhaps I shouldn’t have.

The messages had suddenly stopped appearing on the list for me - although it was still August at the time in my timezone, the list had already moved on to September :)
Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

timrowledge
In reply to this post by Eliot Miranda-2
 


> On 2020-09-01, at 8:12 AM, Eliot Miranda <[hidden email]> wrote:
>
>  [Fabio, this is not described by the blue book because Smalltalk-80 prior to the late 80’s didn’t have unwind protect; I think I’m right in saying that Peter Deutsch added it to ObjectWorks v5; I don’t know when it was added to Smalltalk-V].

The ObjectWorks version was probably added in the release for mid-1990; I spent that xmas adding the VM support to the BrouHaHa/Archimedes system. I can remember it reasonably well because it was damn hard and I almost convinced myself there was a memory hardware problem in my Acorn protoype machine and that lead me to spend actual money on an A540. That was £2500, which is about US$7500 now. As a comparison, a Range Rover cost £2000 then...


tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
<-------- The information went data way -------->


Reply | Threaded
Open this post in threaded view
|

Re: questions about a couple of primitives

Eliot Miranda-2
In reply to this post by Florin Mateoc-4
 


On Sep 1, 2020, at 9:04 AM, Florin Mateoc <[hidden email]> wrote:


Hi Eliot,

On Tue, Sep 1, 2020 at 11:12 AM Eliot Miranda <[hidden email]> wrote:
 
Hi Florin,

On Aug 31, 2020, at 8:32 PM, Florin Mateoc <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 8:25 PM Eliot Miranda <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 6:29 PM Florin Mateoc <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 6:00 PM Eliot Miranda <[hidden email]> wrote:

On Mon, Aug 31, 2020 at 2:00 PM Florin Mateoc <[hidden email]> wrote:
 
I think this is especially confusing since the comment says that the primitive always fails, and then the expectation is that the Smalltalk code that follows is executed instead. But that code does not do what the method actually does

I disagree.  It does exactly what the method does (it *is* the implementation of the method) unless the stack is unwound.  Yes, the comment could point the reader to Context>>#resume:through: which runs the ensure: & ifCurtailed: blocks on unwind.  Bit otherwise ifCurtailed: is not somehow magically not executed.  It is what it is ;-)

As I said earlier,  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.

Can I confirm that your dissatisfaction is with the comment?  Or do you really think the ifCurtailed: method does not execute verbatim in the absence of unwinds?  If the former, you're welcome to submit an improved comment.  If the latter, you're mistaken.


Of course I agree that the ifCurtailed: method does execute verbatim in the absence of unwind. But the method does not only execute in the absence of unwinds. So my "dissatisfaction" is not just with the comment. While it could be somewhat be addressed by a comment, I think this is an instance where the vm is caught cheating. The shown Smalltalk code is not what gets executed in the presence of unwinds (as opposed to the code shown in #ensure: ). The execution of the argument block is hidden inside the vm


To be more pedantic, neither #ensure: nor #ifCurtailed: disclose what is really happening on the unwind path, but at least #ensure: shows some code that conceptually matches its semantics.
In both cases, there is magic happening inside #valueNoContextSwitch, which, although it does not take any arguments, it knows how to (call a method that knows how to) invoke, if necessary, its caller's argument.
Yes, by walking the stack and peeking inside the contexts' temps and then acting upon them anything is possible, but the resulting code is anything but readable.

I would argue for passing the #ensure: and #ifCurtailed: arguments to the #valueNoContextSwitch method/primitive, thus making it possible to avoid #resume:through: - I think such methods are fine for simulation/debugger, but not for runtime.

-1. Putting this in the vm adds a whole level of execution suspension & resumption which isn’t there.  Since Smalltalk has first class activation records it can (and does) elegantly implement a number of very complex control structures (such as unwind protect evaluation, and exception delivery) above the vm.

Sorry, I disagree.

There is no need to apologize.  We disagree.  That is clear.

And I would not put exception delivery in the same boat - that one does exactly what you say, it implements something in the image, above the vm, not just elegantly, but also putting more power in the hands of the developer. But everything is out there in the open, browsable, readable, debuggable and easily understandable.
In this case we have a message sent to a context by the vm, which is not what I would call above the vm.

Your criticism doesn’t make sense to me. aboutToReturn:to: is the result of the VM detecting a non-local return attempting to return past and unwind protect frame.  [Fabio, this is not described by the blue book because Smalltalk-80 prior to the late 80’s didn’t have unwind protect; I think I’m right in saying that Peter Deutsch added it to ObjectWorks v5; I don’t know when it was added to Smalltalk-V].  Similarly cannotReturn: is the result of an attempt to return to a nil sender.  cannotResume: is the result of an attempt to result a process with an invalid suspendedContext.  doesNotUnderstand: is the result of an attempt to send a message that is not understood. attemptToAssign:to:withIndex: is the result of an attempt to assign to the inst var of a read-only object.

So aboutToReturn:to: is one of several send-backs from the VM made when errors occur.  There is nothing unusual about it.  It is in house style, harmonious with the overall system architecture.  And that the response, just like the response to the other send-backs, is implemented in Smalltalk does indeed lift things above the vm.


While I was not initially aware about #aboutToReturn:to:, it is not so much about the fact that the vm sends a message (as you say, that part is in house style), it is what we do with it. You are right, we are doing it on the image side. Except the in-image part of this whole uses, as I mentioned, simulation/debugger kind of code, which might as well have been done in the vm. It is this one-two punch that makes it different than the other vm sent messages that you listed.

And as I already explained, putting this in the vm would necessitate adding a whole new level of execution suspension/return while the vm remembers the continuation of the return as it evaluates the unwinds, which can themselves raise errors.  Putting this in the vm adds significant complications there while all the required mechanism is already available above the vm.  Do, as I already stayed, putting evaluation off winds in the vm does not make (sound engineering) sense to me.


If one browses the code, reads the code and the comments, of both #ifCurtailed: and #valueNoContextSwitch, nothing makes sense, there is no indication whatsoever of what is happening under the hood, no mention of the message that the vm sends to the context. Nor is anything visible in the debugger. Maybe this is indeed all justified for performance reasons, and since I am not the one who implemented it or who would implement an alternative, you can of course ignore my opinion. It is but a tiny corner of the image. But I think it is an important one, and you seem to deny that there is even any readability problem with these methods. Oh, well

I invited you to submit an improved comment.  I pointed you to the implementation of the evaluation of the unwinds.

How about adding something like:

"The abnormal termination is detected by the vm and signalled as an #aboutToReturn:to: message sent to a reified current context, with the context of the #ifCurtailed: invocation as an argument.
The current context then walks the stack to unwind and execute any unwind blocks (including the one protected by the #ifCurtailed: invocation) - see Context>>#resume:through"

Sounds good.  Are you going to submit to inbox?



Anyway, I only now noticed that the discussion is off list - if I did that, it was unintentional, I just clicked reply without checking that it goes to list or not

The discussion is on list.  I hit reply all.  Perhaps I shouldn’t have.

The messages had suddenly stopped appearing on the list for me - although it was still August at the time in my timezone, the list had already moved on to September :)
12