Problems with a large block

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

Problems with a large block

Marco Naddeo-2
Hi,

I have some problems with the large block at bottom:

-sending the message argumentNames gives me an empty array #()
-sending the message sourceNode gives me a strange result and not a
RBBlockNode, as I would expect

Best,
Marco



[ :h :s :v |
     | min chroma hdash X red green blue |
     red := 0.
     green:= 0.
     blue := 0.
     chroma := s * v.
     hdash := h / 60.
     X := chroma * (1.0 - ((hdash % 2) - 1.0) abs).
     (hdash < 1.0)
       ifTrue: [ red := chroma.
             green := X ]
       ifFalse: [
         (hdash < 2.0)
           ifTrue: [ red := X.
                 green := chroma ]
           ifFalse: [
             (hdash < 3.0)
               ifTrue: [ green := chroma.
                     blue := X ]
               ifFalse: [
                 (hdash < 4.0)
                   ifTrue: [ green := X.
                         blue := chroma ]
                   ifFalse: [
                     (hdash < 5.0)
                       ifTrue: [ red := X.
                               blue := chroma ]
                       ifFalse: [
                         (hdash <= 6.0)
                           ifTrue: [ red := chroma.
                                 blue := X ]
                       ]
                   ]
               ]
           ]
       ].
     min := v - chroma.
     { #red -> ((red + min) * 255).
       #green -> ((green + min) * 255).
       #blue -> ((blue + min) * 255) } ]



--
Marco Naddeo

PhD Student
Dipartimento di Informatica
Università degli Studi di Torino
Corso Svizzera 185, 10149 Torino, Italy

Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Marcus Denker-4

> On 07 Oct 2015, at 15:49, Marco Naddeo <[hidden email]> wrote:
>
> Hi,
>
> I have some problems with the large block at bottom:
>
> -sending the message argumentNames gives me an empty array #()
> -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect
>

This happens already with:

[  :h :s :v |    | min chroma hdash X red green blue | ] sourceNode

I will check… it is a bug in the mapping pc -> AST.

        Marcus





Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Henrik Nergaard
In reply to this post by Marco Naddeo-2
Hi Marco,

Had some fun with the code you posted :D.
This *should* be the equivalent, but with less variables so that you can access the RBBlockNode.

[ :h :s :v | | chroma rgb |
        chroma := s*v.
        rgb := { 0 . 0 . 0 }.
        rgb
        at: ((((h + 60) // 120) \\ 3) + 1) put: chroma;
        at: ((((h // 60) + 2 \\ 3) * 5 \\ 3) + 1) put: (chroma * (1.0 - (((h/60) \\ 2) - 1.0) abs));
        collect: [ :part | (part + (v - (chroma) )) * 255 ]
] sourceNode .


Best Regards,
Henrik


-----Original Message-----
From: Pharo-dev [mailto:[hidden email]] On Behalf Of Marco Naddeo
Sent: Wednesday, October 7, 2015 3:49 PM
To: Pharo-dev <[hidden email]>
Subject: [Pharo-dev] Problems with a large block

Hi,

I have some problems with the large block at bottom:

-sending the message argumentNames gives me an empty array #() -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect

Best,
Marco



[ :h :s :v |
     | min chroma hdash X red green blue |
     red := 0.
     green:= 0.
     blue := 0.
     chroma := s * v.
     hdash := h / 60.
     X := chroma * (1.0 - ((hdash % 2) - 1.0) abs).
     (hdash < 1.0)
       ifTrue: [ red := chroma.
             green := X ]
       ifFalse: [
         (hdash < 2.0)
           ifTrue: [ red := X.
                 green := chroma ]
           ifFalse: [
             (hdash < 3.0)
               ifTrue: [ green := chroma.
                     blue := X ]
               ifFalse: [
                 (hdash < 4.0)
                   ifTrue: [ green := X.
                         blue := chroma ]
                   ifFalse: [
                     (hdash < 5.0)
                       ifTrue: [ red := X.
                               blue := chroma ]
                       ifFalse: [
                         (hdash <= 6.0)
                           ifTrue: [ red := chroma.
                                 blue := X ]
                       ]
                   ]
               ]
           ]
       ].
     min := v - chroma.
     { #red -> ((red + min) * 255).
       #green -> ((green + min) * 255).
       #blue -> ((blue + min) * 255) } ]



--
Marco Naddeo

PhD Student
Dipartimento di Informatica
Università degli Studi di Torino
Corso Svizzera 185, 10149 Torino, Italy


Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Nicolai Hess
In reply to this post by Marcus Denker-4


2015-10-07 16:49 GMT+02:00 Marcus Denker <[hidden email]>:

> On 07 Oct 2015, at 15:49, Marco Naddeo <[hidden email]> wrote:
>
> Hi,
>
> I have some problems with the large block at bottom:
>
> -sending the message argumentNames gives me an empty array #()
> -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect
>

This happens already with:

[  :h :s :v |    | min chroma hdash X red green blue | ] sourceNode

I will check… it is a bug in the mapping pc -> AST.

That's interesting, the way the pc is mapped to the AST (ir instructionForPC: )
takes the "length" of the instruction bytecode into account and the length of a pushclosure includes
all bytes needed for the pushing the local temps (pushConstant nil. pushConstant nil ....)

But the startPC of a block context starts at the first push, not after, that means

self method sourceNodeForPC: self startpc - 1
starts the search after the push closure bytecode but before any push local temp byte code.

Either we don't include the bytecodes for the "pushConstant:nil" in the bytecode offset, or we start the search at

self method sourceNodeForPC: self startpc - 1 + self numLocalTemps

 

        Marcus






Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Eliot Miranda-2
Hi Nicolai,

On Wed, Oct 7, 2015 at 2:13 PM, Nicolai Hess <[hidden email]> wrote:


2015-10-07 16:49 GMT+02:00 Marcus Denker <[hidden email]>:

> On 07 Oct 2015, at 15:49, Marco Naddeo <[hidden email]> wrote:
>
> Hi,
>
> I have some problems with the large block at bottom:
>
> -sending the message argumentNames gives me an empty array #()
> -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect
>

This happens already with:

[  :h :s :v |    | min chroma hdash X red green blue | ] sourceNode

I will check… it is a bug in the mapping pc -> AST.

That's interesting, the way the pc is mapped to the AST (ir instructionForPC: )
takes the "length" of the instruction bytecode into account and the length of a pushclosure includes
all bytes needed for the pushing the local temps (pushConstant nil. pushConstant nil ....)

But the startPC of a block context starts at the first push, not after, that means

self method sourceNodeForPC: self startpc - 1
starts the search after the push closure bytecode but before any push local temp byte code.

Either we don't include the bytecodes for the "pushConstant:nil" in the bytecode offset, or we start the search at

self method sourceNodeForPC: self startpc - 1 + self numLocalTemps

No, no, no, no, no :-).  The PC in question is the pc of the block creation byte code.  The startpc of a block is the first bytecode following the block creation byte code.  That *includes* any pushConstant: nil byte codes establishing temps in the block.  Instead, it is expected that "self method sourceNodeForPC: self startpc - 1" answers the same as "self method sourceNodeForPC: self blockCreationPC", where self blockCreationPC is the pc of the block's block creation bytecode.

In Squeak we have
BlockClosure>>blockCreationPC
"Answer the pc for the bytecode that created the receuver."
| method |
method := self method.
^method encoderClass
pcOfBlockCreationBytecodeForBlockStartingAt: startpc
in: method
 
This is because we support (as you will soon enough) different byte code sets so it is wrong to assume the size of the block creation bytecode is 4.  But for now you could use

self method sourceNodeForPC: self startpc - 4

and make sure that the compiler maps a block creation bytecode to the source node for the entire block.

The way to think about "self method sourceNodeForPC: self startpc - 1 + self numLocalTemps" not masking sense is that a pc inside the block must map to a statement inside the block, not the block itself.

HTH


 

        Marcus









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

Re: Problems with a large block

Nicolai Hess


2015-10-08 1:30 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Wed, Oct 7, 2015 at 2:13 PM, Nicolai Hess <[hidden email]> wrote:


2015-10-07 16:49 GMT+02:00 Marcus Denker <[hidden email]>:

> On 07 Oct 2015, at 15:49, Marco Naddeo <[hidden email]> wrote:
>
> Hi,
>
> I have some problems with the large block at bottom:
>
> -sending the message argumentNames gives me an empty array #()
> -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect
>

This happens already with:

[  :h :s :v |    | min chroma hdash X red green blue | ] sourceNode

I will check… it is a bug in the mapping pc -> AST.

That's interesting, the way the pc is mapped to the AST (ir instructionForPC: )
takes the "length" of the instruction bytecode into account and the length of a pushclosure includes
all bytes needed for the pushing the local temps (pushConstant nil. pushConstant nil ....)

But the startPC of a block context starts at the first push, not after, that means

self method sourceNodeForPC: self startpc - 1
starts the search after the push closure bytecode but before any push local temp byte code.

Either we don't include the bytecodes for the "pushConstant:nil" in the bytecode offset, or we start the search at

self method sourceNodeForPC: self startpc - 1 + self numLocalTemps

No, no, no, no, no :-). 

I think I will always get confused by BlockClosure code at the bytecode level :)
 

self method sourceNodeForPC: self startpc - 4

We already have this little heuristic

instructionForPC: aPC
  0 to: -3 by: -1 do: [ :off |
        (self firstInstructionMatching: [:ir | ir bytecodeOffset = (aPC - off) ]) ifNotNil: [:it |^it]]

AFAUI this 0 to -3 offset is used because we have 1,2,3 or 4 byte-length bytecodes.

   
 

and make sure that the compiler maps a block creation bytecode to the source node for the entire block.

in the above code (instructionForPC) we try to find the instruction for the pc (here the pc of the closure creation code), but I don't really understand what
"bytecodeOffset" is, it looks like (bytecodeIndex+startPC), but for a IRPushClosureCopy, it looks like the bytecodeIndex does include
the number of local temps resp. (numberOfLocalTemps times the bytecodelength for pushConstant:nil).

 

The way to think about "self method sourceNodeForPC: self startpc - 1 + self numLocalTemps" not masking sense is that a pc inside the block must map to a statement inside the block, not the block itself.


Hm, I don't know :)
 
HTH


 

        Marcus









--
_,,,^..^,,,_
best, Eliot

Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Eliot Miranda-2
Hi Nicolai,

On Thu, Oct 8, 2015 at 12:09 AM, Nicolai Hess <[hidden email]> wrote:


2015-10-08 1:30 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Wed, Oct 7, 2015 at 2:13 PM, Nicolai Hess <[hidden email]> wrote:


2015-10-07 16:49 GMT+02:00 Marcus Denker <[hidden email]>:

> On 07 Oct 2015, at 15:49, Marco Naddeo <[hidden email]> wrote:
>
> Hi,
>
> I have some problems with the large block at bottom:
>
> -sending the message argumentNames gives me an empty array #()
> -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect
>

This happens already with:

[  :h :s :v |    | min chroma hdash X red green blue | ] sourceNode

I will check… it is a bug in the mapping pc -> AST.

That's interesting, the way the pc is mapped to the AST (ir instructionForPC: )
takes the "length" of the instruction bytecode into account and the length of a pushclosure includes
all bytes needed for the pushing the local temps (pushConstant nil. pushConstant nil ....)

But the startPC of a block context starts at the first push, not after, that means

self method sourceNodeForPC: self startpc - 1
starts the search after the push closure bytecode but before any push local temp byte code.

Either we don't include the bytecodes for the "pushConstant:nil" in the bytecode offset, or we start the search at

self method sourceNodeForPC: self startpc - 1 + self numLocalTemps

No, no, no, no, no :-). 

I think I will always get confused by BlockClosure code at the bytecode level :)

What exactly do you find confusing?  The byte codes are simple if you read the right source.  Perhaps you could talk with Clément; he's local and understands the byte code set as well as I do.  If you can't talk to him you could read the class comment of EncoderForV3PlusClosures in a Squeak 4.6 or 5.0 image, or the class comment of EncoderForSistaV1 in the BytecodeSets package on http://source.squeak.org/VMMaker.

If you're confused by the scheme for closing over variables have you read my blog posts on the closure compiler?

I wish you hadn't deleted this bit, it is "the truth":

"The PC in question is the pc of the block creation byte code.  The startpc of a block is the first bytecode following the block creation byte code.  That *includes* any pushConstant: nil byte codes establishing temps in the block.  Instead, it is expected that "self method sourceNodeForPC: self startpc - 1" answers the same as "self method sourceNodeForPC: self blockCreationPC", where self blockCreationPC is the pc of the block's block creation bytecode.

In Squeak we have
BlockClosure>>blockCreationPC
"Answer the pc for the bytecode that created the receuver."
| method |
method := self method.
^method encoderClass
pcOfBlockCreationBytecodeForBlockStartingAt: startpc
in: method
 
This is because we support (as you will soon enough) different byte code sets so it is wrong to assume the size of the block creation bytecode is 4."

 

self method sourceNodeForPC: self startpc - 4

We already have this little heuristic

instructionForPC: aPC
  0 to: -3 by: -1 do: [ :off |
        (self firstInstructionMatching: [:ir | ir bytecodeOffset = (aPC - off) ]) ifNotNil: [:it |^it]]

AFAUI this 0 to -3 offset is used because we have 1,2,3 or 4 byte-length bytecodes.

Well this will break down with the Sista byte code set because one could conceivably have 7 or 8 byte bytecoedes when one includes prefixes.  In the Newspeak and Sista sets we extend the ranges of byte codes by allowing prefixes.  Notionally the number of prefixes is unlimited but in practice we're using only one of each of the two prefix bytecodes Prefix A & Prefix B (currently E0 and E1 in all sets).

But in the Sista set there is a field in the block creation byte code that reveals how many prefixes the byte code has so one can compute the actual start of the block creation byte code.  i.e.

EncoderForSistaV1 class>>pcOfBlockCreationBytecodeForBlockStartingAt: startpc in: method
"Answer the pc of the push closure bytecode whose block starts at startpc in method.
May need to back up to include extension bytecodes."

"* 224 11100000 aaaaaaaa Extend A (Ext A = Ext A prev * 256 + Ext A)
* 225 11100001 bbbbbbbb Extend B (Ext B = Ext B prev * 256 + Ext B)
** 250 11111010 eeiiikkk jjjjjjjj Push Closure Num Copied iii (+ExtA//16*8) Num Args kkk (+ ExtA\\16*8) BlockSize jjjjjjjj (+ExtB*256). ee = num extensions"
| numExtensions |
self assert: (method at: startpc - 3) = 250.
numExtensions := (method at: startpc - 2) >> 6.
^startpc - 3 - (numExtensions * 2)

and make sure that the compiler maps a block creation bytecode to the source node for the entire block.

in the above code (instructionForPC) we try to find the instruction for the pc (here the pc of the closure creation code), but I don't really understand what
"bytecodeOffset" is, it looks like (bytecodeIndex+startPC), but for a IRPushClosureCopy, it looks like the bytecodeIndex does include
the number of local temps resp. (numberOfLocalTemps times the bytecodelength for pushConstant:nil).

Marcus, what is bytecodeOffset?  Is it the distance from the first byte code or the distance from the first literal or...?

The way to think about "self method sourceNodeForPC: self startpc - 1 + self numLocalTemps" not making sense is that a pc inside the block must map to a statement inside the block, not the block itself.

Hm, I don't know :)

Why?  I designed the closure byte code set so I'm telling you :-).  It's safe to believe me.  For example, an initial pushConstant: nil byte code in a block either establishes the first local temp in that block, or is an argument of a send in the block.  In either case it refers to an element inside the block, not to the entire block itself.  the thing that defines the entire block is the block creation byte code, which includes the span (the size of) the block's bytecodes.  I chose to live with this annoying ambiguity because there were very few unused byte codes and I did;t want to use all of them for the closure byte code set.  And I chose the closure architecture so I could write a faster VM than one that accessed temps directly in outer scopes (which I go into at length on my blog).

HTH


 

        Marcus









--
_,,,^..^,,,_
best, Eliot




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

Re: Problems with a large block

Nicolai Hess


2015-10-08 9:39 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Thu, Oct 8, 2015 at 12:09 AM, Nicolai Hess <[hidden email]> wrote:


2015-10-08 1:30 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Wed, Oct 7, 2015 at 2:13 PM, Nicolai Hess <[hidden email]> wrote:


2015-10-07 16:49 GMT+02:00 Marcus Denker <[hidden email]>:

> On 07 Oct 2015, at 15:49, Marco Naddeo <[hidden email]> wrote:
>
> Hi,
>
> I have some problems with the large block at bottom:
>
> -sending the message argumentNames gives me an empty array #()
> -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect
>

This happens already with:

[  :h :s :v |    | min chroma hdash X red green blue | ] sourceNode

I will check… it is a bug in the mapping pc -> AST.

That's interesting, the way the pc is mapped to the AST (ir instructionForPC: )
takes the "length" of the instruction bytecode into account and the length of a pushclosure includes
all bytes needed for the pushing the local temps (pushConstant nil. pushConstant nil ....)

But the startPC of a block context starts at the first push, not after, that means

self method sourceNodeForPC: self startpc - 1
starts the search after the push closure bytecode but before any push local temp byte code.

Either we don't include the bytecodes for the "pushConstant:nil" in the bytecode offset, or we start the search at

self method sourceNodeForPC: self startpc - 1 + self numLocalTemps

No, no, no, no, no :-). 

I think I will always get confused by BlockClosure code at the bytecode level :)

What exactly do you find confusing? 

For a moment, I was unsure in which context the pushConstant:nil are executed, on construction or when executing the block.
 
The byte codes are simple if you read the right source.  Perhaps you could talk with Clément; he's local and understands the byte code set as well as I do.  If you can't talk to him you could read the class comment of EncoderForV3PlusClosures in a Squeak 4.6 or 5.0 image, or the class comment of EncoderForSistaV1 in the BytecodeSets package on http://source.squeak.org/VMMaker.

If you're confused by the scheme for closing over variables have you read my blog posts on the closure compiler?

Yes I read your blog and it is a great source of information, not only about the current state and what is new, but even about the history of the smalltalk vm - this is always good to know.
 

I wish you hadn't deleted this bit, it is "the truth":

I didn't want to hide "the truth" :) I thought this was the same I wrote and understand, the startPC starts at the first pushConstant:nil for creating space, for the local temp, on the stack.
 

"The PC in question is the pc of the block creation byte code.  The startpc of a block is the first bytecode following the block creation byte code.  That *includes* any pushConstant: nil byte codes establishing temps in the block.  Instead, it is expected that "self method sourceNodeForPC: self startpc - 1" answers the same as "self method sourceNodeForPC: self blockCreationPC", where self blockCreationPC is the pc of the block's block creation bytecode.

In Squeak we have
BlockClosure>>blockCreationPC
"Answer the pc for the bytecode that created the receuver."
| method |
method := self method.
^method encoderClass
pcOfBlockCreationBytecodeForBlockStartingAt: startpc
in: method
 
This is because we support (as you will soon enough) different byte code sets so it is wrong to assume the size of the block creation bytecode is 4."

 

self method sourceNodeForPC: self startpc - 4

We already have this little heuristic

instructionForPC: aPC
  0 to: -3 by: -1 do: [ :off |
        (self firstInstructionMatching: [:ir | ir bytecodeOffset = (aPC - off) ]) ifNotNil: [:it |^it]]

AFAUI this 0 to -3 offset is used because we have 1,2,3 or 4 byte-length bytecodes.

Well this will break down with the Sista byte code

I did not write that code, I just try to understand what I see.
 
set because one could conceivably have 7 or 8 byte bytecoedes when one includes prefixes.  In the Newspeak and Sista sets we extend the ranges of byte codes by allowing prefixes.  Notionally the number of prefixes is unlimited but in practice we're using only one of each of the two prefix bytecodes Prefix A & Prefix B (currently E0 and E1 in all sets).

But in the Sista set there is a field in the block creation byte code that reveals how many prefixes the byte code has so one can compute the actual start of the block creation byte code.  i.e.

EncoderForSistaV1 class>>pcOfBlockCreationBytecodeForBlockStartingAt: startpc in: method
"Answer the pc of the push closure bytecode whose block starts at startpc in method.
May need to back up to include extension bytecodes."

"* 224 11100000 aaaaaaaa Extend A (Ext A = Ext A prev * 256 + Ext A)
* 225 11100001 bbbbbbbb Extend B (Ext B = Ext B prev * 256 + Ext B)
** 250 11111010 eeiiikkk jjjjjjjj Push Closure Num Copied iii (+ExtA//16*8) Num Args kkk (+ ExtA\\16*8) BlockSize jjjjjjjj (+ExtB*256). ee = num extensions"
| numExtensions |
self assert: (method at: startpc - 3) = 250.
numExtensions := (method at: startpc - 2) >> 6.
^startpc - 3 - (numExtensions * 2)

and make sure that the compiler maps a block creation bytecode to the source node for the entire block.

in the above code (instructionForPC) we try to find the instruction for the pc (here the pc of the closure creation code), but I don't really understand what
"bytecodeOffset" is, it looks like (bytecodeIndex+startPC), but for a IRPushClosureCopy, it looks like the bytecodeIndex does include
the number of local temps resp. (numberOfLocalTemps times the bytecodelength for pushConstant:nil).

Marcus, what is bytecodeOffset?  Is it the distance from the first byte code or the distance from the first literal or...?

The way to think about "self method sourceNodeForPC: self startpc - 1 + self numLocalTemps" not making sense is that a pc inside the block must map to a statement inside the block, not the block itself.

Hm, I don't know :)

Why?  I designed the closure byte code set so I'm telling you :-).  It's safe to believe me. 

I do believe, I just don't understand.
 
For example, an initial pushConstant: nil byte code in a block either establishes the first local temp in that block, or is an argument of a send in the block.  In either case it refers to an element inside the block, not to the entire block itself.  the thing that defines the entire block is the block creation byte code, which includes the span (the size of) the block's bytecodes.  I chose to live with this annoying ambiguity because there were very few unused byte codes and I did;t want to use all of them for the closure byte code set.  And I chose the closure architecture so I could write a faster VM than one that accessed temps directly in outer scopes (which I go into at length on my blog).

I read it, but I don't understand how can calling
self method sourceNodeForPC: self startpc -1
depend on the pc inside the block? Isn't this only to get the sourceNode of the block object?


 

HTH


 

        Marcus









--
_,,,^..^,,,_
best, Eliot




--
_,,,^..^,,,_
best, Eliot

Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Marco Naddeo-2
In reply to this post by Henrik Nergaard
Hi Henrik,

thank you but my problem is not that particular block, but rather the
fact that I need to navigate the AST of *any* block (no matter which one
and how much is big) to retrieve the last statement in it...

Anyhow, for the moment I can use your refactoring to make my tests
succeed. ;-) So thanks.

Marco


Il 07/10/2015 18:05, Henrik Nergaard ha scritto:

> Hi Marco,
>
> Had some fun with the code you posted :D.
> This *should* be the equivalent, but with less variables so that you can access the RBBlockNode.
>
> [ :h :s :v | | chroma rgb |
> chroma := s*v.
> rgb := { 0 . 0 . 0 }.
> rgb
> at: ((((h + 60) // 120) \\ 3) + 1) put: chroma;
> at: ((((h // 60) + 2 \\ 3) * 5 \\ 3) + 1) put: (chroma * (1.0 - (((h/60) \\ 2) - 1.0) abs));
> collect: [ :part | (part + (v - (chroma) )) * 255 ]
> ] sourceNode .
>
>
> Best Regards,
> Henrik
>
>
> -----Original Message-----
> From: Pharo-dev [mailto:[hidden email]] On Behalf Of Marco Naddeo
> Sent: Wednesday, October 7, 2015 3:49 PM
> To: Pharo-dev <[hidden email]>
> Subject: [Pharo-dev] Problems with a large block
>
> Hi,
>
> I have some problems with the large block at bottom:
>
> -sending the message argumentNames gives me an empty array #() -sending the message sourceNode gives me a strange result and not a RBBlockNode, as I would expect
>
> Best,
> Marco
>
>
>
> [ :h :s :v |
>       | min chroma hdash X red green blue |
>       red := 0.
>       green:= 0.
>       blue := 0.
>       chroma := s * v.
>       hdash := h / 60.
>       X := chroma * (1.0 - ((hdash % 2) - 1.0) abs).
>       (hdash < 1.0)
>         ifTrue: [ red := chroma.
>               green := X ]
>         ifFalse: [
>           (hdash < 2.0)
>             ifTrue: [ red := X.
>                   green := chroma ]
>             ifFalse: [
>               (hdash < 3.0)
>                 ifTrue: [ green := chroma.
>                       blue := X ]
>                 ifFalse: [
>                   (hdash < 4.0)
>                     ifTrue: [ green := X.
>                           blue := chroma ]
>                     ifFalse: [
>                       (hdash < 5.0)
>                         ifTrue: [ red := X.
>                                 blue := chroma ]
>                         ifFalse: [
>                           (hdash <= 6.0)
>                             ifTrue: [ red := chroma.
>                                   blue := X ]
>                         ]
>                     ]
>                 ]
>             ]
>         ].
>       min := v - chroma.
>       { #red -> ((red + min) * 255).
>         #green -> ((green + min) * 255).
>         #blue -> ((blue + min) * 255) } ]
>
>
>
> --
> Marco Naddeo
>
> PhD Student
> Dipartimento di Informatica
> Università degli Studi di Torino
> Corso Svizzera 185, 10149 Torino, Italy
>
>
>

--
Marco Naddeo

PhD Student
Dipartimento di Informatica
Università degli Studi di Torino
Corso Svizzera 185, 10149 Torino, Italy

Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Nicolai Hess
In reply to this post by Nicolai Hess


2015-10-08 23:45 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Thu, Oct 8, 2015 at 2:27 PM, Nicolai Hess <[hidden email]> wrote:
Hello Eliot, and thanks for your time.


In our (pharo) implementation for sourceNodeForPC, we start at the AST-Node of
the compiled method,  get the IR (IntermediateRepresentation) and scan this sequence of IRNodes
until we finally find that one, that maps to the bytecode of the closure creation code (this should be a IRPushClosureCopy).
The problem is now, we don't find (always) the right IRNode, and it depens on the number of local temps, if this is > 3 we don't hit the right one.

That's very strange.  Can you construct a synthetic example of two simple blocks, one with three temps and and one with four temps and show me the difference in the byte code?


[ :x | | a b c | a:=b:=c:=x ]         "calling #sourceNode gives a RBBlockNode - ok"
Bytecode
13 <8F 01 00 0B> closureNumCopied: 0 numArgs: 1 bytes 17 to 27
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <10> pushTemp: 0
21 <81 43> storeIntoTemp: 3
23 <81 42> storeIntoTemp: 2
25 <81 41> storeIntoTemp: 1
27 <7D> blockReturn
28 <7C> returnTop


[ :x | | a b c d | a:=b:=c:=d:=x ]    "calling #sourceNode gives a RBMethodNode - not ok"
Bytecode
13 <8F 01 00 0E> closureNumCopied: 0 numArgs: 1 bytes 17 to 30
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <73> pushConstant: nil
21 <10> pushTemp: 0
22 <81 44> storeIntoTemp: 4
24 <81 43> storeIntoTemp: 3
26 <81 42> storeIntoTemp: 2
28 <81 41> storeIntoTemp: 1
30 <7D> blockReturn
31 <7C> returnTop

As I wrote above, in Pharo, to get the sourceNode we try to find the push closure bytecode from "startpc - 1" by traversing
the IR and try to get the IRNode with matching "bytecodeOffset". The bytecodeOffsets for the above blocks:

[ :x | | a b c | a:=b:=c:=x ]
"bytecodeOffset -> IR"
19->pushClosureCopyCopiedValues: #() args: #(#x)
28->returnTop
20->pushTemp: #x
22->storeTemp: #c
24->storeTemp: #b
26->storeTemp: #a
27->blockReturnTop

[ :x | | a b c d | a:=b:=c:=d:=x ]
"bytecodeOffset -> IR"
20->pushClosureCopyCopiedValues: #() args: #(#x)
31->returnTop
21->pushTemp: #x
23->storeTemp: #d
25->storeTemp: #c
27->storeTemp: #b
29->storeTemp: #a
30->blockReturnTop

Both blocks don't have a bytecodeOffset matching the bytecode of the pushclosure (13), but we start the search from "startpc - 1" (= 16)
and do a loop from 0 to -3 (because bytecodes can have a length of 1,2 or 4)
For the first block, this works
bytecodeOffset 19 = 16 - (-3)
For the second block this does not work, somehow the bytecodeoffset value depends on the number of local temps. And for the
second block this is one greater.
 



And my interpretation was, the IRPushClosureCopy node has the wrong "bytecode offset" (whatever this is). The bytecodeOffset value (somehow)
depends on the number of local temps, but maybe this is right (for whatever this is used) and we are just using it wrong for in this situation.

So you need to ask Marcus how to interpret bytecodeOffset.  The thing is, it should be simple.  One has:

    BlockCreationBytecode
    any number of pushConstant: nil's (0 to N)
    first byte code in block
    last bytecode in block (always a blockReturnTop)

and a block's startup is always that of the byte immediately following the BlockCreationBytecode.  So if the byte code offset is somehow the span of the "any number of pushConstant: nil's (0 to N)" then there's a bug in the IR somewhere when the number of temps is > 3.  But if that's not what it means then, well, I don't know what the problem is.
 
Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Henrik Sperre Johansen

On 10 Oct 2015, at 10:05 , Nicolai Hess <[hidden email]> wrote:



2015-10-08 23:45 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Thu, Oct 8, 2015 at 2:27 PM, Nicolai Hess <[hidden email]> wrote:
Hello Eliot, and thanks for your time.


In our (pharo) implementation for sourceNodeForPC, we start at the AST-Node of
the compiled method,  get the IR (IntermediateRepresentation) and scan this sequence of IRNodes
until we finally find that one, that maps to the bytecode of the closure creation code (this should be a IRPushClosureCopy).
The problem is now, we don't find (always) the right IRNode, and it depens on the number of local temps, if this is > 3 we don't hit the right one.

That's very strange.  Can you construct a synthetic example of two simple blocks, one with three temps and and one with four temps and show me the difference in the byte code?


[ :x | | a b c | a:=b:=c:=x ]         "calling #sourceNode gives a RBBlockNode - ok"
Bytecode
13 <8F 01 00 0B> closureNumCopied: 0 numArgs: 1 bytes 17 to 27
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <10> pushTemp: 0
21 <81 43> storeIntoTemp: 3
23 <81 42> storeIntoTemp: 2
25 <81 41> storeIntoTemp: 1
27 <7D> blockReturn
28 <7C> returnTop


[ :x | | a b c d | a:=b:=c:=d:=x ]    "calling #sourceNode gives a RBMethodNode - not ok"
Bytecode
13 <8F 01 00 0E> closureNumCopied: 0 numArgs: 1 bytes 17 to 30
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <73> pushConstant: nil
21 <10> pushTemp: 0
22 <81 44> storeIntoTemp: 4
24 <81 43> storeIntoTemp: 3
26 <81 42> storeIntoTemp: 2
28 <81 41> storeIntoTemp: 1
30 <7D> blockReturn
31 <7C> returnTop

As I wrote above, in Pharo, to get the sourceNode we try to find the push closure bytecode from "startpc - 1" by traversing
the IR and try to get the IRNode with matching "bytecodeOffset". The bytecodeOffsets for the above blocks:

[ :x | | a b c | a:=b:=c:=x ]
"bytecodeOffset -> IR"
19->pushClosureCopyCopiedValues: #() args: #(#x)
28->returnTop
20->pushTemp: #x
22->storeTemp: #c
24->storeTemp: #b
26->storeTemp: #a
27->blockReturnTop

[ :x | | a b c d | a:=b:=c:=d:=x ]
"bytecodeOffset -> IR"
20->pushClosureCopyCopiedValues: #() args: #(#x)
31->returnTop
21->pushTemp: #x
23->storeTemp: #d
25->storeTemp: #c
27->storeTemp: #b
29->storeTemp: #a
30->blockReturnTop

Both blocks don't have a bytecodeOffset matching the bytecode of the pushclosure (13), but we start the search from "startpc - 1" (= 16)
and do a loop from 0 to -3 (because bytecodes can have a length of 1,2 or 4)
For the first block, this works
bytecodeOffset 19 = 16 - (-3)
For the second block this does not work, somehow the bytecodeoffset value depends on the number of local temps. And for the
second block this is one greater.
 



And my interpretation was, the IRPushClosureCopy node has the wrong "bytecode offset" (whatever this is). The bytecodeOffset value (somehow)
depends on the number of local temps, but maybe this is right (for whatever this is used) and we are just using it wrong for in this situation.

So you need to ask Marcus how to interpret bytecodeOffset.  The thing is, it should be simple.  One has:

    BlockCreationBytecode
    any number of pushConstant: nil's (0 to N)
    first byte code in block
    last bytecode in block (always a blockReturnTop)

and a block's startup is always that of the byte immediately following the BlockCreationBytecode.  So if the byte code offset is somehow the span of the "any number of pushConstant: nil's (0 to N)" then there's a bug in the IR somewhere when the number of temps is > 3.  But if that's not what it means then, well, I don't know what the problem is.
 

For some reason, bytecodeOffset is the *last* byte associated with IR.
This makes little sense to me, the main use seems to be to get correct offsets for different instructions in an IR sequence.
Since block IR includes temp initialization bytes, the offset is 5.

Changing the code to record *starting* offset of an IRNode in IRSequence, by:
mapBytesTo: instr
"Associate the current byte offset with instr. We fix this later to have the correct offset,
see #bytecodes"
instrMap add: instr -> (bytes size + 1)

and mapping bytes *before* visiting node in the IRTranslatorV2:
visitInstruction: instr
gen mapBytesTo: instr.
self visitNode: instr.

and the same in visitPushClosureCopy: closure
closure copiedValues do: [:name | 
gen mapBytesTo: closure.
gen pushTemp: (self currentScope indexForVarNamed: name).
].
--snip--

Plus changing IRMethod >> instructionForPC: aPC to scan backwards to find the start of in-mid PC (for now up to 3 bytes):

self compiledMethod. "generates the compiledMethod and optimize the ir. 
Removes the side-effect of optimizing the IR while looking for instruction, 
which results in incorrect found instruction"
self haltOnce.
0 to: 3 do: [ :off |
(self firstInstructionMatching: [:ir | ir bytecodeOffset = (aPC - off) ]) ifNotNil: [:it |^it]]

We get more sensible results from the above tests for argumentNames/sourceNode.
Amazingly, debugger range hilighting seems to keep working as well :)

Cheers,
Henry

signature.asc (859 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Henrik Sperre Johansen

On 12 Oct 2015, at 3:43 , Henrik Johansen <[hidden email]> wrote:


On 10 Oct 2015, at 10:05 , Nicolai Hess <[hidden email]> wrote:



2015-10-08 23:45 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Thu, Oct 8, 2015 at 2:27 PM, Nicolai Hess <[hidden email]> wrote:
Hello Eliot, and thanks for your time.


In our (pharo) implementation for sourceNodeForPC, we start at the AST-Node of
the compiled method,  get the IR (IntermediateRepresentation) and scan this sequence of IRNodes
until we finally find that one, that maps to the bytecode of the closure creation code (this should be a IRPushClosureCopy).
The problem is now, we don't find (always) the right IRNode, and it depens on the number of local temps, if this is > 3 we don't hit the right one.

That's very strange.  Can you construct a synthetic example of two simple blocks, one with three temps and and one with four temps and show me the difference in the byte code?


[ :x | | a b c | a:=b:=c:=x ]         "calling #sourceNode gives a RBBlockNode - ok"
Bytecode
13 <8F 01 00 0B> closureNumCopied: 0 numArgs: 1 bytes 17 to 27
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <10> pushTemp: 0
21 <81 43> storeIntoTemp: 3
23 <81 42> storeIntoTemp: 2
25 <81 41> storeIntoTemp: 1
27 <7D> blockReturn
28 <7C> returnTop


[ :x | | a b c d | a:=b:=c:=d:=x ]    "calling #sourceNode gives a RBMethodNode - not ok"
Bytecode
13 <8F 01 00 0E> closureNumCopied: 0 numArgs: 1 bytes 17 to 30
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <73> pushConstant: nil
21 <10> pushTemp: 0
22 <81 44> storeIntoTemp: 4
24 <81 43> storeIntoTemp: 3
26 <81 42> storeIntoTemp: 2
28 <81 41> storeIntoTemp: 1
30 <7D> blockReturn
31 <7C> returnTop

As I wrote above, in Pharo, to get the sourceNode we try to find the push closure bytecode from "startpc - 1" by traversing
the IR and try to get the IRNode with matching "bytecodeOffset". The bytecodeOffsets for the above blocks:

[ :x | | a b c | a:=b:=c:=x ]
"bytecodeOffset -> IR"
19->pushClosureCopyCopiedValues: #() args: #(#x)
28->returnTop
20->pushTemp: #x
22->storeTemp: #c
24->storeTemp: #b
26->storeTemp: #a
27->blockReturnTop

[ :x | | a b c d | a:=b:=c:=d:=x ]
"bytecodeOffset -> IR"
20->pushClosureCopyCopiedValues: #() args: #(#x)
31->returnTop
21->pushTemp: #x
23->storeTemp: #d
25->storeTemp: #c
27->storeTemp: #b
29->storeTemp: #a
30->blockReturnTop

Both blocks don't have a bytecodeOffset matching the bytecode of the pushclosure (13), but we start the search from "startpc - 1" (= 16)
and do a loop from 0 to -3 (because bytecodes can have a length of 1,2 or 4)
For the first block, this works
bytecodeOffset 19 = 16 - (-3)
For the second block this does not work, somehow the bytecodeoffset value depends on the number of local temps. And for the
second block this is one greater.
 



And my interpretation was, the IRPushClosureCopy node has the wrong "bytecode offset" (whatever this is). The bytecodeOffset value (somehow)
depends on the number of local temps, but maybe this is right (for whatever this is used) and we are just using it wrong for in this situation.

So you need to ask Marcus how to interpret bytecodeOffset.  The thing is, it should be simple.  One has:

    BlockCreationBytecode
    any number of pushConstant: nil's (0 to N)
    first byte code in block
    last bytecode in block (always a blockReturnTop)

and a block's startup is always that of the byte immediately following the BlockCreationBytecode.  So if the byte code offset is somehow the span of the "any number of pushConstant: nil's (0 to N)" then there's a bug in the IR somewhere when the number of temps is > 3.  But if that's not what it means then, well, I don't know what the problem is.
 

For some reason, bytecodeOffset is the *last* byte associated with IR.
This makes little sense to me, the main use seems to be to get correct offsets for different instructions in an IR sequence.
Since block IR includes temp initialization bytes, the offset is 5.

Changing the code to record *starting* offset of an IRNode in IRSequence, by:
mapBytesTo: instr
"Associate the current byte offset with instr. We fix this later to have the correct offset,
see #bytecodes"
instrMap add: instr -> (bytes size + 1)

and mapping bytes *before* visiting node in the IRTranslatorV2:
visitInstruction: instr
gen mapBytesTo: instr.
self visitNode: instr.

and the same in visitPushClosureCopy: closure
closure copiedValues do: [:name | 
gen mapBytesTo: closure.
gen pushTemp: (self currentScope indexForVarNamed: name).
].
--snip--

Hupps, the pushing of copiedValues are not part of the closure bytecode, so that needs to be 
closure copiedValues do: [:name |
gen pushTemp: (self currentScope indexForVarNamed: name).
].
gen mapBytesTo: closure.

Thanks testBlockAndContextSourceNode!

After a recompile of the test methods in case of cached IR (and offsets)
(RPackageOrganizer default packageNamed: 'OpalCompiler-Tests') classes do:#recompile
All (existing) tests are green with the attached change set (where haltOnce's were removed too), at least in the almost-release version of Pharo4 I tested in.

Cheers,
Henry





Opal-ByteCodeMapping.cs.zip (1K) Download Attachment
signature.asc (859 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Problems with a large block

Eliot Miranda-2
Thanks Henry.  Your approach seems much more rational.  For example,e, branches are to the start of a byte code sequence, and return addresses are to the start of the byte code following the send.  There is no case I can think of when the last byte of a byte code is an important ordinal, not even in cannotReturn: or illegalBytecode:.

On Mon, Oct 12, 2015 at 7:37 AM, Henrik Johansen <[hidden email]> wrote:

On 12 Oct 2015, at 3:43 , Henrik Johansen <[hidden email]> wrote:


On 10 Oct 2015, at 10:05 , Nicolai Hess <[hidden email]> wrote:



2015-10-08 23:45 GMT+02:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Thu, Oct 8, 2015 at 2:27 PM, Nicolai Hess <[hidden email]> wrote:
Hello Eliot, and thanks for your time.


In our (pharo) implementation for sourceNodeForPC, we start at the AST-Node of
the compiled method,  get the IR (IntermediateRepresentation) and scan this sequence of IRNodes
until we finally find that one, that maps to the bytecode of the closure creation code (this should be a IRPushClosureCopy).
The problem is now, we don't find (always) the right IRNode, and it depens on the number of local temps, if this is > 3 we don't hit the right one.

That's very strange.  Can you construct a synthetic example of two simple blocks, one with three temps and and one with four temps and show me the difference in the byte code?


[ :x | | a b c | a:=b:=c:=x ]         "calling #sourceNode gives a RBBlockNode - ok"
Bytecode
13 <8F 01 00 0B> closureNumCopied: 0 numArgs: 1 bytes 17 to 27
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <10> pushTemp: 0
21 <81 43> storeIntoTemp: 3
23 <81 42> storeIntoTemp: 2
25 <81 41> storeIntoTemp: 1
27 <7D> blockReturn
28 <7C> returnTop


[ :x | | a b c d | a:=b:=c:=d:=x ]    "calling #sourceNode gives a RBMethodNode - not ok"
Bytecode
13 <8F 01 00 0E> closureNumCopied: 0 numArgs: 1 bytes 17 to 30
17 <73> pushConstant: nil
18 <73> pushConstant: nil
19 <73> pushConstant: nil
20 <73> pushConstant: nil
21 <10> pushTemp: 0
22 <81 44> storeIntoTemp: 4
24 <81 43> storeIntoTemp: 3
26 <81 42> storeIntoTemp: 2
28 <81 41> storeIntoTemp: 1
30 <7D> blockReturn
31 <7C> returnTop

As I wrote above, in Pharo, to get the sourceNode we try to find the push closure bytecode from "startpc - 1" by traversing
the IR and try to get the IRNode with matching "bytecodeOffset". The bytecodeOffsets for the above blocks:

[ :x | | a b c | a:=b:=c:=x ]
"bytecodeOffset -> IR"
19->pushClosureCopyCopiedValues: #() args: #(#x)
28->returnTop
20->pushTemp: #x
22->storeTemp: #c
24->storeTemp: #b
26->storeTemp: #a
27->blockReturnTop

[ :x | | a b c d | a:=b:=c:=d:=x ]
"bytecodeOffset -> IR"
20->pushClosureCopyCopiedValues: #() args: #(#x)
31->returnTop
21->pushTemp: #x
23->storeTemp: #d
25->storeTemp: #c
27->storeTemp: #b
29->storeTemp: #a
30->blockReturnTop

Both blocks don't have a bytecodeOffset matching the bytecode of the pushclosure (13), but we start the search from "startpc - 1" (= 16)
and do a loop from 0 to -3 (because bytecodes can have a length of 1,2 or 4)
For the first block, this works
bytecodeOffset 19 = 16 - (-3)
For the second block this does not work, somehow the bytecodeoffset value depends on the number of local temps. And for the
second block this is one greater.
 



And my interpretation was, the IRPushClosureCopy node has the wrong "bytecode offset" (whatever this is). The bytecodeOffset value (somehow)
depends on the number of local temps, but maybe this is right (for whatever this is used) and we are just using it wrong for in this situation.

So you need to ask Marcus how to interpret bytecodeOffset.  The thing is, it should be simple.  One has:

    BlockCreationBytecode
    any number of pushConstant: nil's (0 to N)
    first byte code in block
    last bytecode in block (always a blockReturnTop)

and a block's startup is always that of the byte immediately following the BlockCreationBytecode.  So if the byte code offset is somehow the span of the "any number of pushConstant: nil's (0 to N)" then there's a bug in the IR somewhere when the number of temps is > 3.  But if that's not what it means then, well, I don't know what the problem is.
 

For some reason, bytecodeOffset is the *last* byte associated with IR.
This makes little sense to me, the main use seems to be to get correct offsets for different instructions in an IR sequence.
Since block IR includes temp initialization bytes, the offset is 5.

Changing the code to record *starting* offset of an IRNode in IRSequence, by:
mapBytesTo: instr
"Associate the current byte offset with instr. We fix this later to have the correct offset,
see #bytecodes"
instrMap add: instr -> (bytes size + 1)

and mapping bytes *before* visiting node in the IRTranslatorV2:
visitInstruction: instr
gen mapBytesTo: instr.
self visitNode: instr.

and the same in visitPushClosureCopy: closure
closure copiedValues do: [:name | 
gen mapBytesTo: closure.
gen pushTemp: (self currentScope indexForVarNamed: name).
].
--snip--

Hupps, the pushing of copiedValues are not part of the closure bytecode, so that needs to be 
closure copiedValues do: [:name |
gen pushTemp: (self currentScope indexForVarNamed: name).
].
gen mapBytesTo: closure.

Thanks testBlockAndContextSourceNode!

After a recompile of the test methods in case of cached IR (and offsets)
(RPackageOrganizer default packageNamed: 'OpalCompiler-Tests') classes do:#recompile
All (existing) tests are green with the attached change set (where haltOnce's were removed too), at least in the almost-release version of Pharo4 I tested in.

Cheers,
Henry








--
_,,,^..^,,,_
best, Eliot