[Vm-dev] VM Maker: VMMaker.oscog-eem.1522.mcz

Ben Coman btc at openinworld.com
Thu Nov 19 15:19:02 UTC 2015


Thanks Bert, and Eliot for your detailed response, that clarified a lot.

I am now clear that the Intepreters do process Bytecodes not Slang.

But just checking... I always thought of Slang as just the
subset-language, yet from "the thing that does the Smalltalk to C
translation is called Slang" it seems like Slang is also the program
that generates the C code (?).

So the VM-Slang code can be used in two ways:
1. Generated to C code (which process is it that does this?)
2. Used with the VM simulator. Now does the VM-slang run "on top" of
the VM Simulator (i.e. the VM-Simulator interprets Slang), or (I think
this more likely) does the VM-Slang run directly in an Image on a
standard VM with the VM-Simulator beside it in the image providing
support methods?

cheers -ben


On Thu, Nov 19, 2015 at 5:56 PM, Bert Freudenberg <bert at freudenbergs.de> wrote:
>
>> On 19.11.2015, at 00:05, Ben Coman <btc at openinworld.com> wrote:
>>
>> How does Slang related to the interpreter?
>> i.e. Is it Slang that the interpreter interprets?
>
> Slang is the language the interpreter and Cog / Spur etc. are written in.
>
> It looks like Smalltalk, but has C semantics. There are no classes or objects, it pretty much only operates on integers and pointers. It can be run directly as Smalltalk (which is what the VM simulator does) but its main purpose is to be transcribed 1-to-1 to C to be compiled by a C compiler into a VM binary.
>
> - Bert -

On Thu, Nov 19, 2015 at 8:33 AM, Eliot Miranda <eliot.miranda at gmail.com> wrote:
>> The VM is written as a Smalltalk program.  There are three different versions of the interpreter in it, Interpreter, which is a classical blue-book interpreter that uses context objects to represent method activations, the STackInterpreter that avoids creating contexts as much as possible and uses stack frames to represent method activations, and CoInterpreter, a subclass of StackInterpreter that can interoperate with the JIT so that machine code can execute Smalltalk on its own, interleaved with the CoInterpreter interpreting methods that have yet to be fitted or are judged too big to be worth jitting.  All of these interpreters interpret Smalltalk bytecocde methods.  They contain methods like the following, which is the pushInstVar byte code used to push an instance variable of the receiver onto the stack:
>>
>> pushReceiverVariableBytecode
>> <expandCases>
>> self pushReceiverVariable: (currentBytecode bitAnd: 16rF).
>> self fetchNextBytecode
>>
>> pushReceiverVariable: fieldIndex
>>
>> self internalPush: (objectMemory fetchPointer: fieldIndex ofObject: self receiver).
>>
>> All three implement their interpreter as a loop that indexes an Array of selectors for each byte code of methods like the above:
>>
>> StackInterpreterSimulator>>run
>> "Just run"
>> quitBlock := [displayView ifNotNil:
>>   [displayView containingWindow ifNotNil:
>> [:topWindow|
>> ((World submorphs includes: topWindow)
>> and: [UIManager default confirm: 'close?']) ifTrue:
>> [topWindow delete]]].
>>  ^self].
>> self initStackPages.
>> self loadInitialContext.
>> self internalizeIPandSP.
>> self fetchNextBytecode.
>> [true] whileTrue:
>> [self assertValidExecutionPointers.
>> atEachStepBlock value. "N.B. may be nil"
>> self dispatchOn: currentBytecode in: BytecodeTable.
>> self incrementByteCount].
>> localIP := localIP - 1.
>> "undo the pre-increment of IP before returning"
>> self externalizeIPandSP
>>
>> dispatchOn: anInteger in: selectorArray
>> "Simulate a case statement via selector table lookup.
>> The given integer must be between 0 and selectorArray size-1, inclusive.
>> For speed, no range test is done, since it is done by the at: operation."
>>
>> self perform: (selectorArray at: (anInteger + 1)).
>>
>> The BytecodeTable has 256 entries.  Depending on the byte code set the first 16 elements might be #pushReceiverVariableBytecode, the next 16 #pushTemporaryVariableBytecode and so on.
>>
>> This is fine for developing the VM but can't yield useful performance in practice.  We need somehow to translate the Smalltalk code into something a real machine can run.  We do this in two steps.  First, the Smalltalk code is converted from Smalltalk to C.  This is "trivial" because in fact the VM is written in a subset of C that is "trivially" translatable to C.  There are no Dictionaries or Sets in the bowels of the VM, just whole and for looks and performs mapped either to inlining of code or to calling through function pointers.  The thing that does the Smalltalk to C translation is called Slang.  The pours trees of the methods of the VM are translated into Slang's parse trees which in turn implement analysis and output code that allows the entire VM to be written out as a C program.  The last step is to compile this using a common or garden C compiler.
>>
>> Here's what the code of the interpreter loop looks like.  You'll be curious about the <expandCases> pragma above in pushReceiverVariableBytecode.  It and a couple of other pragmas are used to guide Slang in producing the interpreter loop's C code:
>>
>>         /* begin internalizeIPandSP */
>>         localIP = pointerForOop(GIV(instructionPointer));
>>         localSP = pointerForOop(GIV(stackPointer));
>>         localFP = pointerForOop(GIV(framePointer));
>>         /* begin fetchNextBytecode */
>>         currentBytecode = byteAtPointer(++localIP);
>>
>>         /* begin initExtensions */
>>
>>         while (1) {
>>                 bytecodeDispatchDebugHook();
>>
>>                 VM_LABEL(bytecodeDispatch);
>>                 switch (currentBytecode) {
>>                 case 0:
>>                         /* pushReceiverVariableBytecode */
>>                         {
>>                                 VM_LABEL(pushReceiverVariableBytecode);
>>                                 /* begin fetchNextBytecode */
>>                                 currentBytecode = byteAtPointer(++localIP);
>>
>>                                 /* begin pushReceiverVariable: */
>>                                 /* begin internalPush: */
>>                                 longAtPointerput((localSP -= BytesPerOop), longAt(((longAt(localFP + FoxReceiver)) + BaseHeaderSize)));
>>                         }
>>                         break;
>>                 case 1:
>>                         /* pushReceiverVariableBytecode */
>>                         {
>>                                 VM_LABEL(pushReceiverVariableBytecode1);
>>                                 /* begin fetchNextBytecode */
>>                                 currentBytecode = byteAtPointer(++localIP);
>>
>>                                 /* begin pushReceiverVariable: */
>>                                 /* begin internalPush: */
>>                                 longAtPointerput((localSP -= BytesPerOop), longAt(((longAt(localFP + FoxReceiver)) + BaseHeaderSize) + 4 /* (currentBytecode bitAnd: 15) << self shiftForWord */));
>>                         }
>>                         break;
>>
>> So we get to develop the VM in Smalltalk, but the code is reasonably well compiled by a C compiler and hence acceptably efficiently executed by a commodity microprocessor.
>>
>>
>> And of course the Cogit is another story :-)
>>
>> _,,,^..^,,,_
>> best, Eliot
>
>
>
>
> --
> _,,,^..^,,,_
> best, Eliot
>


More information about the Vm-dev mailing list