Inlining contexts requires care to ensure that it's possible to
de-optimise them safely and that the semantics are never changed even when code is changed or when reflection is used. Things to consider: * De-optimisation * Blocks returning from the method and accessing state. * Changing code * debuggers and profilers (stack walking) * become: and changeClassTo: and #primitiveChangeClassTo: * other reflection including #instVarAt:put: * Seaside At the moment it's possible to de-optimise any context individually which is very convenient but means that the VM must not need to be aware of whether a context is native or interpreted except when it's executing it. The VM will refer to the home context of a block, so everything it accesses must be in the same locations whether interpreted or compiled. Being able to de-optimise a context without needing to worry if there are block contexts is a great simplification when ensuring Exupery doesn't cause crashes on de-optimisation. De-optimisation can be triggered by various things like saving the image, modifying a context, saving it in Seaside. Native contexts can not be in the image when it's saved as the machine code they depend on will not be there, and even if it is it's unlikely to be at the same location. Seaside, and the profiler have opposite problems for de-optimisation with Seaside we want to de-optimise the context to make it safe to persist while with the profiler we want to leave the context optimised so profiling doesn't change the performance characteristics. Currently de-optimisation only converts a context from native to interpreted, it uses the same object to represent both. With inlining de-optimisation will need to create contexts. Because we don't know what the contexts are for we should preserve identity including during de-optimisation. Any context that creates a block is referred to from the block so it'll need to be created. Only changing code will require removing inlining. Because of reflection we can not guarantee that data will stay the same from when we leave a method until when we return. But the method is decided at send time, so the type check that replaces the send is good enough to deal with this case. Inlined code will need to sanity check it's state on re-entry if it's relying on knowing the class of any variable, this can not be avoided because the class could have changed via primitiveChangeClassTo: or become:. The Design: =========== Simple methods that don't create contexts will be fully inlined into the calling context. A slot will be kept to hold a pointer to a "proxy" context if it is ever accessed via reflection, this lets the proxy context be reused if it's ever accessed again. The proxy context will access state in the parent context. If a method creates a block a real object will be created. It's variables and stack will be used, and so will it's PC. This is so that the block can return out of it via a ^ return into the surrounding part of the inlined context. While a context object will be created, the code dealing with it will be inlined, so the inlined method will manage multiple contexts depending on where it is. Always creating the context that creates a block ensures that it and the block can be de-optimised as de-optimisation will not change the location of the context's variables. If it was inlined into another context object then it's variables and stack would be located at a different location. Also the return code would need to do something special to return into it. If a block is inlined for it's entire lifetime then it can be removed fully. This is the the optimisation's goal because it's probably inlined an entire loop and if not sends will be converted to simple jumps inside the inlined method. If code is changed then initially all effected contexts will be de-optimised. This involves a full memory scan via allInstances to find the Exupery contexts. This could be optimised by either de-activating the inlining by modifying the code or using a context stack to minimise the amount of memory to scan. Modifying code to de-activate the inlining is the simpler optimisation, it just involves writing an unconditional jump over the type test to jump directly to the unexpected case thus avoiding the inlined code. ContextPart>>sender will need to be modified so the sender can answer it's newest inlined context rather than itself if it's an inlined context. De-optimisation will be done in the image without requiring any VM extensions. It may be worthwhile introducing a "uncommon" trap that lets compiled code bail out and de-optimise if the assumptions it's built for become invalid. At the moment native code can deal with both the expected and unexpected cases. Removing the need to deal with the unexpected cases should reduce the size of native code needed. Bryce _______________________________________________ Exupery mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/exupery |
Thanks very much for these emails, these are useful to those of us
following from the sidelines but I think they will also be useful as a rudimentary form of documentation as this project progresses. Ken _______________________________________________ Exupery mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/exupery signature.asc (196 bytes) Download Attachment |
Free forum by Nabble | Edit this page |