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 |
> 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 |
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 |
In reply to this post by Marcus Denker-4
2015-10-07 16:49 GMT+02:00 Marcus Denker <[hidden email]>:
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
|
Hi Nicolai,
On Wed, Oct 7, 2015 at 2:13 PM, Nicolai Hess <[hidden email]> wrote:
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
_,,,^..^,,,_ best, Eliot |
2015-10-08 1:30 GMT+02:00 Eliot Miranda <[hidden email]>:
I think I will always get confused by BlockClosure code at the bytecode level :)
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.
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).
Hm, I don't know :)
|
Hi Nicolai,
On Thu, Oct 8, 2015 at 12:09 AM, Nicolai Hess <[hidden email]> wrote:
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."
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)
Marcus, what is bytecodeOffset? Is it the distance from the first byte code or the distance from the first literal or...?
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).
_,,,^..^,,,_ best, Eliot |
2015-10-08 9:39 GMT+02:00 Eliot Miranda <[hidden email]>:
For a moment, I was unsure in which context the pushConstant:nil are executed, on construction or when executing the block.
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 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.
I did not write that code, I just try to understand what I see.
I do believe, I just don't understand.
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?
|
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 |
In reply to this post by Nicolai Hess
2015-10-08 23:45 GMT+02:00 Eliot Miranda <[hidden email]>:
[ :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.
|
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 |
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 |
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:
_,,,^..^,,,_ best, Eliot |
Free forum by Nabble | Edit this page |