NOTE: pmichaud estimates that the overhead for nqp caused by Parrot's lack of an lvalue model is about 4%. This may be worth fixing eventually but it's probably not a low-hanging fruit.
<pmichaud> cotto_work: I do expect to try to generate smarter pir, yes. However, the structure of Parrot doesn't allow for a lot smarter PIR. (I'm hoping parrot will change that... but I don't expect it to happen anytime in the near future.) <cotto_work> ok. How could Parrot enable smarter code generation? <pmichaud> cotto_work: well, one of the big stumbling blocks at the moment is that lexicals can only be PMCs so anything that goes into a lexical ends up having to be boxed into a PMC an even bigger stumbling block is that Parrot doesn't have a good lvalue model or assignment semantics it tends to confuse "bind" with "assign" at least, it does that for most of the default PMC types there's also not a good reference model for keeping track of object references as a result, the code that nqp generates tends to have to be somewhat pessimistic, such as continually refetching lexical and global symbol tables because of the possibility of a symbol being rebound <cotto_work> That explains some of the generated code then. <pmichaud> cotto_work: I do plan to have an option whereby code can say "assume this lexical/symbol isn't being rebound" but since parrot doesn't have a good assignment model, most folks end up doing rebinds partcl-nqp just went through this yesterday (until I fixed it this morning) whereby symbols were being re-bound to new PMCs instead of changing the value of an existing multiply-bound PMC <cotto_work> What's the difference between assignment and binding? <pmichaud> assignment changes the value of an existing PMC binding causes a symbol table entry to point to a PMC so, for example $P0 = box 1 $P1 = $P0 these are examples of binding if after the above, I do: $P0 = box 3 then $P1 ends up still being bound to the PMC containing 1 and $P0 is bound to a new PMC containing 3 okay so far? <cotto_work> yes <pmichaud> $P0 = box 1 $P1 = $P0 assign $P0, 3 in this case, $P0 and $P1 still point to the same PMC, and the value of that PMC has changed to 3 still okay? <cotto_work> yes <pmichaud> that's the difference now then, let's look at something like $P0 = new ['ResizablePMCArray'] $P0 = 1 $P1 = $P0 in this case, $P1 and $P0 are pointing to the same PMC (containing 1) but if we then do... $P0 = 3 that's a binding, not an assignment such that $P1 refers to the old PMC (1), while $P0 refers to the new one (3) in other words, changing the value in the array has broken the bind <moritz> pmichaud: this is becoming an FAQ... you should put that on a wiki page or so <pmichaud> moritz: well, I have a few hours car ride today, perhaps I can type it up. but I also plan to do a NQP/compiler tutorial <cotto_work> Thanks. <pmichaud> where it gets *really* nasty is with $P0 = find_lex '$a' 'foo'() if the "foo" subroutine does store_lex '$a', $P99 to put a new value in lexical $a then when we get back from 'foo', $P0 no longer points to the PMC associated with $a because store_lex does a bind and not an assign <cotto_work> Then $P0 will have an out-of-date value <pmichaud> exactly so, every time we need access to $a, we end up having to do a re-fetch because it's possible some other operation will have re-bound it to a different PMC ideally there would be an opcode that makes it easier to do assignment there is an opcode called 'assign', but it tends to do weird things with the built-in PMCs for example $P0 = new ['ResizablePMCArray'] store_lex '$a', $P0 which sets $a to be a RPA now we want to do the equivalent of $a = 3 -- i.e., change it to an Integer 3 $P1 = find_lex '$a' assign $P1, 3 # oooops! <cotto_work> That changes the length to 3, doesn't it? <pmichaud> what we want is $P1 to be an Integer 3 right so the 'assign' opcode doesn't do what we want here late last year we added the 'copy' opcode to try to do this so we'd have $P1 = find_lex '$a' $P2 = box 3 copy $P1, $P2 (replace $P1 with $P2) but it's horribly inefficient, because it involves making a clone of $P2 and then copying that structure into the PMC header for $P1 but so far it's about the best we can do everything gets worse when we start talking about aggregate access because there's not a copy $P0, $P2 opcode yet and this leads to allison's comment that it really should be the PMCs/vtables that determine behavior instead of opcodes (which is where the model completely breaks down) <cotto_work> There's a lot conspiring to prevent good code generation from nqp. Thanks you for that explanation. <pmichaud> sure thing! ultimately it's that Parrot (vtable and core types) doesn't have a good container/value/lvalue model or, at least, one that doesn't map well to the HLL's we're targeting.