[Newcompiler] Finding enough bytecodes for closures.

Klaus D. Witzel klaus.witzel at cobss.com
Tue May 1 17:52:24 UTC 2007


On Tue, 01 May 2007 18:42:45 +0200, Marcus Denker wrote:
> On 01.05.2007, at 09:45, Klaus D. Witzel wrote:
>> On Mon, 30 Apr 2007 22:47:05 +0200, Bryce wrote:
>>> Klaus D. Witzel writes:
>>>  > Hi Bryce,
>>>  >
>>>  > as part of your investigation, did you observe any chance to
>>> reduce the
>>>  > burden of having an instance of BlockClosure *and* of
>>> ClosureEnvironment,
>>>  > that'd be interesting.
>>>
>>> First the new compiler only creates a BlockClosure and an
>>> ClosureEnvironment if the block captures variables from it's
>>> surrounding scope.
>>>
>>>    self ifAbsent: [Set new].
>>>
>>> Builds the BlockClosure at compile time. In this case, especially if
>>> the block closure isn't called the closure code will be much faster.
>>
>> Sure. But who wants BlockClosures without having "free" variables,
>> this would be a contradiction and not worth the effort building a
>> new compiler for them :|
>>
>
> But there are many cases of BlockClosures that reference no external
> variables... e.g. along the example given by bryce:
>
> 	someDict at: #test ifAbsentPut: ['hello']
>
> here the block has no variables at all, thus is does not need to have
> an Environment.

Sure (now repeated, how many times? :) There are cases where no Env's are  
needed. But you cannot design+build the new compiler for just the cases  
where no Env is needed?

> For blocks with no free vars, the newCompiler generates the
> BlockClosure at compiletime, see
> ASTTranslatorForValue>>#acceptBlockNode:

This method is my best friend, especially Preferences  
compileBlocksAsClosures ifFalse:ifTrue: :)

> 	aBlockNode freeVars isEmpty ifTrue: [
> 		"Create block at compile time"
> 		methodBuilder pushBlock: blockMethod.
> 	] ifFalse: [
> 		"Create block at run time"
> 		| outerScope envVar |
> 		outerScope := aBlockNode scope outerScope.
> 		envVar := outerScope hasEscapingEnv
> 			ifTrue: [outerScope thisEnvVar]
> 			ifFalse: [outerScope receiverVar].
> 		methodBuilder pushBlockMethod: blockMethod.
> 		envVar emitLocalValue: methodBuilder.
> 		methodBuilder send: #createBlock:.
> 	].
>
> The old compiler generates in all cases code where the block's
> bytecode is inlined in the method and uses
> #blockCopy: to generate a MethodContext at *runtime*.

The above code is really, really a nice solution: everything's in one  
place, comprehensible and easy changeable :)

> Now the story when you actually send #value...
>
> BlockContext: The vm has already a Context, so it fills it up with
> all the runtime data and start executing.
> BlocKClosure: The vm executes a *method* that is referenced from the
> Closure. This means, it needs
> 			  to set up a context. Here there comes in the very clever
> optimization that the VM can
>                            re-use context, thus, if we take care (not
> accessing thisContext), we do not need to create
>                           a context but reuse it.

Exactly (*reuse*, see also below).

> The idea now is that "reusing" a context should not be slower then
> actually using BlockContext, with the
> additional feature that we have the optimzation described above.
> 	

Sure :)

BTW: I've modfied ASTTranslatorForValue>>#acceptBlockNode: to do exactly  
that.

>
>>
>>> Yes it is possible to eliminate many ClosureEnvironments, they're
>>> only
>>> needed when variables are actually captured from that scope.
>>
>> Sure "only if needed" but otherwise nobody would need a new
>> compiler which handles full BlockClosures.
>>
>
> But they are not needed in *all* cases. ['I am a block'] does not
> need an environment for storing variables.

*absolutely* *no* *problem* *and* *never* *seen* *as* *a* *problem* *here*  
(apologies for the emphasis) but that cannot be taken as the general case  
(and just the latter is my point, nothing more). Instead, efficient code  
needs to be generated for precisely the opposite case(s). Is it possible  
we agree on this one?

>>> Removing
>>> the closures is just creating extra kinds of block similar to the
>>> different kinds of context that Eliot added in VisualWorks
>>> 5i. Creating extra context types is probably not needed to equal
>>> or better
>>> the current code in most cases.
>>
>> Yes (extra context types probably not needed to equal or better),
>> me too thinks that's not really needed.
>
> It would be nice to make it simple and uniform.... the current
> BlockContext scheme preceeds the contex-recycling
> scheme...

There's one particular reason why #pushActiveContextBytecode has to cancel  
(as noted by Bryce in the other thread) the recycling possibility of  
contexts: any piece of code could have stored the context (MethodContext  
and BlockContext alike) behind the VM's back, say for example into a slot  
of a longer living object (like in an iVar). From then on recycling is  
impossible.

Can we agree that this situation (cause+effect) cannot change, regardless  
of using MethodContext, BlockContext, BlockClosures or any replacement for  
the latter?

> and having Blocks and Methods be one thing would be nice.

It already does so here, and it reuses the context object when sending  
#value to closures, the same way as it reuses "old" BlockContexts. But it  
does not change the situation of context-recycling :(

Cheers
Klaus

> 	Marcus




More information about the Newcompiler mailing list