VM support changes required by the LowcodeVM
These are the changes in the support sources that are required by the LowcodeVM.
I am also putting a copy of the full XML based specification of the Lowcode instruction set that is used to generate the StackInterpreter and Cogit implementation of the instructions.
The Lowcode SmalltalkHub project ( http://smalltalkhub.com/#!/~ronsaldo/Lowcode ) contains the generator scripts.
To generate the implementations of the Lowcode instructions to the StackInterpreter and the Cogit, the following script can be used:
LowcodeCogitGenerator generateFromFileNamed: 'lowcode.xml' to: 'LowcodeCog.st'
To generate the IRLowcodeBuilder (extended OpalCompiler IRBuilder) generation methods, the following script can be used:
LowcodeOpalCompilerGenerator generateFromFileNamed: 'lowcode.xml'.
For generating the x86 assembly for testing, I am using a script like the following one:
compilationContext := CompilationContext new encoderClass: OpalEncoderForSistaV1.
ir := IRLowcodeBuilder buildIR: [ :builder |
builder compilationContext: compilationContext.
addTemps: #(__lowcodeFrameMark __lowcodePreviousNativeStackPointer__ __lowcodeNativeFramePointer__ __lowcodeNativeStackPointer__ __lowcodeCalloutState__);
method := ir compiledMethod.
method setFrameBit: true.
StackToRegisterMappingCogit genAndDis: method options: #(
The first instruction of a method that uses Lowcode must be localFrameSize: . This instructions builds
a stack frame that is used for primitive local variables, the secondary native stack, and it is never inspected by the GC. A Lowcode native stack frame is always pinned.
For tracking the presence of a native stack frame, and the necesity of cleaning the native stack frame when returning from a method, the temporary is used as flag by putting a special object that is compared by identity. I used this approach because I did not find any available bit in the stack frame for putting this flag. It also has the advantage of giving room for implementing a mechanism for native stack context mapping, by replacing this mark object with an object that represents a stack frame that should not be cleaned up on method return, and to avoid the overhead of allocating a complete object on the start of a Lowcode method.
For doing callout from the StackInterpreter, the lowcodeCalloutState is used by the interpreter. The StackInterpreter cannot use the normal C stack immediately because it pushes a big pressure in the C compiler. The Cogit version just uses the C stack for the callouts.