LSWVST Virtual-Machine

The LSWVST Virtual-Machine ( LSWVST-VM ) is written in LSWAsm a proprietary stand-alone Assembler.  The LSWVST-VM doesn't require any additional Libraries and can be reduced to a size of only 30 KB.

 

There exist several flavours of the VM ( in the subfolder of LSWVST installation directory Virtual-Machines:

Filename Description Size
LSWVST-dev-W32-exe full development VM including JIT-Engine 130KB
LSWVST-dev-vse-W32-exe dev. VM + VSE Image, SLL & Primitive compatibility 140 KB
LSWVST-rtm-W32.exe full runtime engine, excluding JIT-Engine & Development Primitives 60 KB
LSWVST-rtm-min-W32.exe minimal runtime engine without Float Support & Startup-Support & Dump Support 30KB
LSWVST-dev-WCE.exe full development VM including JIT-Engine for Windows CE 130 KB
LSWVST-rtm-WCE.exe same as *-rtm-W32.exe for Windows CE 60 KB
LSWVST-rtm.min-WCE.exe same as *-min.rtm-W32.exe for Windows CE 112 KB
LSWVST-dev-linux-ubuntu full development VM including JIT-Engine for Linux/Ubuntu 126 KB
LSWVST-rtm-W32.dll full runtime VM including JIT-Engine for DLL's 50 KB
LSWVST-rtm-WCE.dll same as *-rtm-W32.dll for Windows CE 50 KB
LSWVST-rtm-W64.dll same as *-rtm-W32.dll for Windows 64Bit 80 KB
LSWVST-rtm-W32.sys full runtime VM including JIT-Engine for NT Driver's 160 KB
LSWVST-OS32.img Experimental operating system including full development VM including LSWASM + Debugger , JIT-Engine for OS-Development and over 20000 Classes  40 MB
LSWVST-dev-W64.exe full development VM including JIT-Engine for Windows 64Bit 160 KB

LSWVST VM's can be indiviually configured, it is possible for instance to take out the Float or threading-support. The JIT Engine can be configured to add ANC support which adds about 100 KB in size.

The LSWVST-Smalltalk-Object images can be stored within the Virtual-Machine executable.

LSWVST VM and .Net CLR

In the days of .NET and managed-code it is not easy to rectify the development of an own virtual-machine. Our philosophy is to have the deepest operating-system-Integation of Smalltalk which is possible. The next operating-system from Microsoft - may  expose some of its operating-system functionality only in form of managed-code. This means for a Language which relies on its own virtual-machine which traditionally includes a "Just-In-Time" compiler a serious integration problem.

There exist several options:

  1. Try to run of top of the MS Virtual-Machine Technogogy ( also knows as .NET Common Language Runtime = CLR)
  2. Use COM / Interop and coexist with the CLR
  3. Translating a Subset of Smalltalk to MSIL
  4. Have a new Foreign-Function Interface mechansimn to Callout / Callin to/from the .NET CLR
  5. Run MSIL on top of Smalltalk
  6. Define a "High-Level" compatibility ( e.g. at the Level of Web-Services )
  7. Ignore .NET
  8. Develop a new .NET Virtual-Machine ( see LSWVST.Net )

We decided for 2, 3, 4 and 5,8.

Bytecode Execution

Our VM executes Bytecode with traditional JIT or adaptive compilation to Native Code, which is something similar to JIT. We don't call it JIT because there are some big technology differences between Just-in-Time compilation in Java or .NET and the ANC Compilation of our Smalltalk-VM. Bytecode is used at Development-time - which is translated on-the-fly to Native-Code. For a Production-Image we take out the Native-Code-Generation.

There are several options in our Virtual-Machine:

a) Traditional JIT of Bytecode - usually used in a Development-Scenario.

b) Compilation to Abstract-Assembly-Code which can be translated in Zero-Time to Machine-Code at Program-Load-Time.

c) Compilation to Pure-Assembly-Code

Option b and c gives Assembly-Code which is faster than C-Code.

MSIL Interpreted Execution

We have an MSIL-Interpreter which executes MSIL which is loaded by our Reflection engine. Of course this is an order of Magnitude slower than execution on the .NET CLR. But it gives us the Opportunity to speed up development for .NET. Our .NET Code need to be recompiled if we change Classes add/remove Methods.

Native Callout

Our Native Callout Interface follows the accepted way found in other Dialects.

Look at the example of a Smalltalk-Method defined in the class UserDLL with a Resource statement ( < ... > ) in the body.

messageBeep: anInteger
<api: MessageBeep ulong boolean>
^self invalidCallout

.NET Callout

We have added a new .NET Callout Interface to our Smalltalk. Look at the example of a Smalltalk-Method defined in the class SystemWindowsFormsAssemblyDLL with a Resource statement ( < ... > ) in the body.

messageBoxShow: messageString caption: captionString buttons: buttons
<.NET: DialogResult System.Windows.Forms.MessageBox.Show string string MessageBoxButtons>
^self invalidCallout

Both forms of Callouts are heavily optmized. Callouts can be recursivley interrupted by means of Callins. A special form of a Callin is a WM_ message sent which will be handled by a NotificationStrategy object in the Process-Object receiving the WM_message.

Our carefully designed Virtual-machine is stateless and this means that the VM behaves as a "First-Class" Object in the sense of the Operating-System or the outside world.

It is very essential to have an opimized Callout Interface for overall execution speed of Smalltalk-programs in LSWVST. We tried to achieve almost the same speed for Callouts as Primitive calls.

JIT-Engine with adaptive compilation to native Code

Adaptive compilation to Native Code - in short  ANC is a technique that goes beyond JIT - and we can beat C-Programs with execution speed. Why Smalltalk is said to loose speed - for instance because of its runtime type checking - why can be a technique which executes Smalltalk faster than C ?.

Compilation to Machine-Code for traditional Languages means traditionally Analysis ( Control-Flow, Data-Flow, Dependence and Alias Analysis ) , Optimization ( Register Allocation, Code-Scheduling, Control-Flow Optimization & more ). These complex techniques leads to highly optimized Native-Programs and it is evident that JIT cannot compete with these Compile-Time techniques. In Languages like LISP or Smalltalk, Java or C# most these techniques cannot be applied at all. But JIT can do better than traditional compilation in dynamic optimization because if we encounter an operation we now about how often it is already executed - we don't need much Analysis we can do Statistics. The drawback about JIT is that often it has to be reexecuted each time the program is invoked.

But again can we do better than JIT. Sure we can compile using Profiling information -  that take statistic overhead out of JIT - allows to use many of global optimization techniques from traditional language compilation and leads to what we call "Adaptive Compilation".

Our Smalltalk-Compiler can produce Abstract-Native-Code which is transformed at Installation-Time to Real-Native-Code when we know on which Processor architecure we should run.

Garbage collection

A big advantage of todays OO Languages are the automatic memory management features. A program - the Garbage collector which is executed "in parallel" takes care of memory occupied by objects which are no more referenced and recycles it automatically. Hardcoded mechanisms to avoid Garbage are available - e.g. Allocation of local variables and value types on the Stack instead of the Heap - which is maintained by the Garbage Collector.

We have decided to extend these mechansimns to allow full control by the programmer - we have added a number of MemoryManagement Strategy classes which can be used to allocate memory in a more controlled way - also an extended Collection framework is created to allow Collections of same or similar objects to be handled efficiently.

We call these technics Garbage avoidance supported by our Object memory recycling and Object lifetime management frameworks.

Isomorphic Collections

The collection framework of Smalltalk is very simple and powerful - but also inefficient. In Smalltalk everything is an Object and objects are boxed. For Isomorphic Collections - collections of objects of a known species we can make a lot of optimizations.

LSWVST-VM Primitives

Primitives are methods executed by the VM. In Smalltalk they look like

SessionStrategy>>primSaveSessionToFile: sName description: sDescription options: iOptions

<primitive: PrimitiveSaveSession>
^ self primitiveFailed

<PrimitiveSaveSession> is a constant from the Pool-Dictionary  PrimitiveNumbers ( with the value 121).

The Primitive itself knows the number of Arguments. They are pushed from lowest to highest onto the stack. In case of success the Primitive removes the Arguments from the stack. Here comes another reason for having a Smalltalk - Specific debugger environment -  to see the callstack the debugger must have knowledge which functions are smalltalk-primitives.