[squeak-dev] sends of #class and #== considered harmful, may we please stop?
Marcus Denker
marcus.denker at inria.fr
Fri Nov 30 12:21:02 UTC 2018
>>
>> +1. Again see Marcus’ super cool reification of inlined blocks in Pharo, even if it is slooooow :-)
>
> Yes, it is very slow. The goal was more these two:
>
> 1) do not loose the clever student in the first lecture. They do the “lets implement 3 value logic” and are *very* upset when they realise that the system is not as nice as they thought :-)
> 2) understand the problem better
>
> and of course, it is not much code. This is the full code (and it can be turned off by a setting):
>
….
> So this means we do the compilation for every execution. I wonder if this could not be speed up by caching… maybe even one could do something similar at the JIT level?
>
I now implemented caching of the compiled DoIt. It adds a method property #mustBeBooleanCache which is a dictionary that stores the doit per pc.
The cached method itself stores the pc after the jump. (#mustBeBooleanJump):
mustBeBooleanDeOptimizeIn: context
"Permits to redefine methods inlined by compiler.
Take the ast node corresponding to the mustBeBoolean error, compile it on the fly and executes it as a DoIt. Then resume the execution of the context.
the generated DoIts are cached in the calling method"
| ret cache method |
cache := context method propertyAt: #mustBeBooleanCache ifAbsentPut: [ IdentityDictionary new ].
"compile a doit method for the unoptimized expression"
method := cache at: (context pc - 1) ifAbsent: [self mustBeBooleanCompileExpression: context andCache: cache ].
"Execute the generated method with the pc still at the optimzized block so that the lookUp can read variables defined in the optimized block"
ret := context receiver withArgs: {context} executeMethod: method.
"resume the context at the instruction following the send when returning from deoptimized code."
context pc: (method propertyAt: #mustBeBooleanJump).
^ret.
with the compilation done in this method:
mustBeBooleanCompileExpression: context andCache: cache
"Permits to redefine methods inlined by compiler.
Take the ast node corresponding to the mustBeBoolean error, compile it on the fly and executes it as a DoIt. Then resume the execution of the context."
| sendNode methodNode pc method pcAfterJump |
"get the message send node that triggered mustBeBoolean"
pc := context pc - 1.
sendNode := context sourceNode sourceNodeForPC: pc.
"Rewrite non-local returns to return to the correct context from send"
RBParseTreeRewriter new
replace: '^ ``@value' with: 'ThisContext home return: ``@value';
executeTree: sendNode.
"Build doit node to perform send unoptimized"
methodNode := sendNode copy asDoitForContext: context.
"Keep same compilation context as the sender node's"
methodNode compilationContext: sendNode methodNode compilationContext copy.
"Disable inlining so the message send will be unoptimized"
methodNode compilationContext compilerOptions: #(- optionInlineIf optionInlineAndOr optionInlineWhile).
"Generate the method"
OCASTSemanticCleaner clean: methodNode.
method := methodNode generate.
"store the pc of the instruction following the send when returning from deoptimized code."
pcAfterJump := sendNode irInstruction nextBytecodeOffsetAfterJump.
method propertyAt: #mustBeBooleanJump put: pcAfterJump.
"cache the method we just created"
cache at: pc put: method.
^method
seems lead to a factor 200 speedup for trivial examples. The “hot” code-path now just gets the method from the cache and executes it,
so all the overhead of compilation. And the mapping pc-AST is not needed at runtime.
Marcus
More information about the Squeak-dev
mailing list
|