SMALLTALK SOLUTIONS 2005: ORLANDO

VM Primitive Maker Framework for Visualworks

Sudhakar Krishnamachari Introduction

 Personal: [email protected]  Context: Develop higher-performance C implementations of code sections for use as primitives. Dev done entirely as Smalltalk methods. Benefit from both the worlds…  Smalltalk coding is the easiest…  Compiled C code is better in performance …(for high memory swaps and multiple mathematical operations in single call) As also for:  Creating a modular Visualworks VM  Current Approach: Pure C code primitive or DLLCC wrapper

 The New Framework: Ported from

VW development by: Eliot Miranda

 Mode of Presentation: Semi-interactive to fully interactive …(refer to the slide number if required later…)

0101 Contents  Basics  Process Flow  Framework Details  Quick Examples / Demo 01  Framework Details Continued..  Interface methods  Steps to create the plugin ( a recap )  Extended Interactive Examples / Demo 02  Technical Design Notes  Other Issues  Q&A  Concluding note…

0202 VisualWorks Smalltalk An applications development platform:

Objects: Objects ( class) description Object header and data Object state Virtual Image (includes contexts as objects)

Virtual Engine ••Compiler •• Interpreter ••Memory handling aka memory reclamation •• Primitives

Operating System •• Numbered For user extensions use: •• DLLCC Hardware new •• Named 0303 HPSVM.InterpreterPlugin

Model the primitives in Smalltalk

HPSVM.InterpreterProxy

In Smalltalk: methods to create objects, access and manipulate Object values 03b The Process Flow

Calls to primitive methods that Basic VW Class/ Methods can also be part of any base VW .. class and not necessarily an with method calls to exported dll “primitive” external new class as in DLLCC functions ^Plugin01 new prim: self Func: arg1

Testing/ Debugging Plugin01 doPrimitive: … receiver:… Calls and obtains the return Use the various object/ arguments:… values within the arguments other value access sent calls as given in InterpreterProxy instance side

HPSVM.InterpreterPlugin Exported automatically as a subclass C Code file that can be Compiled Dll compiled easily The smalltalk coding of the primitive C File encapsulating the exported and helper functions “primitive” method calls. prim: rcvr Func: arg1 self export: true. Dll_export oopInt primFunc( …. oopInt arg1) { … } 0404 Framework Details

 HPSVM Parcels: Namespace and Plugin  InterpreterProxy and InterpreterPlugin Classes  The Smalltalk API subset for Plugin class methods aka Squeak Slang  C CodeGeneration classes  Simulation methods

Smalltalk.HPSVM defineClass: #VMPluginTest superclass: #{HPSVM.InterpreterPlugin} indexedType: #none private: false instanceVariableNames: '' classInstanceVariableNames: '' imports: '' category: 'VMMaker-Plugins'

returnGivenValue: anIntegerRcvr | anInt | self export: true. anInt := interpreterProxy integerValueOf: anInteger. ^interpreterProxy integerObjectOf: anInt 0505 Continued..

Smalltalk Plugin method to C conversion:

 Unary message Single argument function call  Binary or keyword sends Multiple argument function call  Instance variable Global Variable  Method temp variable Function local variable

 Declares all variables as default of oopI nt type  Coerce by specific declaration to type required  Return type defaults to oopInt, coerce if required.

DLUP_EXPORT(oopInt) returnGivenValue ( oopInt rcvr, oopInt anIntegerRcvr) { oopInt anInt;

anInt := integerValueOf (anInteger); return integerObjectOf( anInt); } 0606 Converted code…

return: rcvr GivenValue: anIntegerRcvr | anInt | self export: true. anInt := interpreterProxy integerValueOf: anInteger. ^interpreterProxy integerObjectOf: anInt

DLUP_EXPORT(oopInt) returnGivenValue ( oopInt rcvr, oop anIntegerRcvr) { oopInt anInt;

anInt := integerValueOf (anInteger); return integerObjectOf( anInt); } Quick Demo On:…

 Primitives and primitive pragma usage Numbered, DLLCC and Named…

 A HelloWorld primitive function access

 The DemoPlugin Example:

 Smalltalk Plugin class, generated C Code  Testing and linking through: * doPrimitive:receiver:arguments: * primitive call…

0707 Continued..

 Accessing and manipulating Smalltalk objects in C code or Smalltalk Plugin methods  4 distinct types of Smalltalk objects:  -2^29 < SmallIntegers > 2^29 30bits + 2 tag bits identifier…  Non-indexable ST objects : Boolean, Fraction, Object, Set objects  Indexable to oops : Array objects  Indexable to byte or word data : LargePositiveInteger, ByteArray, WordArray etc..

0808 Oops check … isIntegerValue isNilObject: , isTrueObject.. isBytes: , isPointer:… isIndexable: Is:KindOf: Instance variable access Oops ptr ( int*) Class oop fetchPointer: ofObject: fetchWord: ofObject: 32 bit integer pointer Size , hash, gcflags.. fetchIntegerValue:ofObject:

Indirection pointer Inst var1

Inst var2 Class oop Small Integers … Size , hash, … integerValueOf Inst var3…. integerObjectOf SmallInteger oop Indirection pointer

Stores the integer value Byte1, 2, 3, 4 with 1’s in the 2 low order bit 5, 6 , 7 , 8

……..

Arrays: Bytes, Integer … firstIndexableField: firstFixedField: 0909 Operators and methods supported by InterpreterProxy / InterpreterPlugin

Operators translated to C: object access • Boolean &, or , and:, or: .. ••• fetchArray:ofObject:, fetchFloat:ofObject:, • Arithmetic +, - , * , / … fetchPointer:ofObject:, fetchWord:ofObject: • Bit operators << , bitAnd:… storeInteger:ofObject:withValue: • Comparison operators < <= == … • Special mathematical: raisedTo:, min:.. Object size: ••• slotSizeOf: byteSizeOf: , stSizeOf: loop constructs (remember: no real blocks) • whileTrue: , whileFalse: to:do: … converting ••• booleanValueOf:, checkedIntegerValueOf: , conditionals positive64BitValueOf:, signed32BitValueOf: • ifTrue: , ifFalse: , ifTrue:ifFalse: special objects indexed access ••• falseObject , nilObject , trueObject , zeroObject • firstIndexableField: , firstFixedField: , at: , at:put: , basicAt: , basicAt:put: , arrayValueOf: special classes ••• classArray , classBitmap, classByteArray , integer oop conversion/testing classSemaphore • integerValueOf: , integerObjectOf:, isIntegerObject: instance creation directives ••• clone: , instantiateClass:indexableSize: • inline: , export: , returnTypeC: ,static: • makePointwithxValue:yValue:

casts miscellaneous • asFloat ,asInteger cCode: , cCode:inSmalltalk: , cCoerce:to: , preIncrement , preDecrement , primitiveFail , success: type testing isFloat , isIndexable , isIntegerOop , isIntegerValue , isWords , isWordsOrBytes , isPointers , isNil, isMemberOf: , isKindOf: , isNil , notNil 10 Create a simple plugin

Visualworks Image with HPSVM parcels loaded Create a subclass of InterpreterPlugin: Plugin01

Add instance side method: Test: primitiveReturnInteger: rcvr Plugin01 self export: true. doPrimitive: # primitiveReturnInteger: ….. self returnTypeC: ‘int’

^interpreterProxy integerObjectOf: 3 . Export to C File thru VMMaker tool or HPSVM.VMMaker new initialize… ….code execution

Create access method in any class TestPlugin>> generateInternalPlugin: generateExternallPlugin: PrimitiveReturnInteger #Plugin01 #Plugin01 Compile as vw exe Compile as dll Along with rest of vwntoeimport.lib {VW}/bin/src/include Link and Test the source code TestPlugin new PrimitiveReturnInteger 1111 The VM Maker Demo UI…

 Create plugin class

 Generate C Code + makefile

 View C code and makefile

 Simulation testing workspace

 Auto compile C code

 Create and relink through new pragma methods generated Extended Interactive Session

 Vector Math Example  Run through few methods in the VectorPlugin01 class  Debug in Smalltalk through doPrimitive: call..  Emit C Code and compare…  Link to vwntoe.lib and compile  Run the vector tests..  Profile comparison test…

 Eg: For Vector Add Internal: 154ms DLLCC: 60ms Interpreter: 373ms PluginDll: 42ms  For Vector CrossProduct Normal Internal: 469ms DLLCC: 62ms Interpreter: 399ms PluginDll: 42ms

Race through BMPReadWriter, JPEGReader, BitBlt,FFT … and others as possible… VMMaker tool…??? Hold back and discuss here…. 1212 Technical Design Notes:

Architecture Overview:

1. Utility routines to manipulate Smalltalk objects passed to external code. 2. Simulation of the primitive method in Smalltalk before exporting to C Code. 3. Automated C code generation and compilation for the specific platform. 4. Automated generation of pragma and relinking tests as required

The generated code is emitted as the following segments:

1. Header includes 2. #define declarations 3. Global Variable declarations 4. Function prototypes 5. Function declarations including return type, arguments specified,local variable declarations, C statements. 6. Final exports function pointer array Code Generation:

Code generation is primarily converting the given smalltalk methods in the subclass of InterpreterPlugin to C code through mechanism of:

1. PluggableCodeGenerator and CCodeGenerator classes to emit the C code, primarily the header, defines, function prototypes etc to the opened file stream.

2. Building a parse tree representation as in standard Smalltalk method compilation as a CompiledMethod instance with a collection of statements for transformation.

3. The statements/ nodes collection is parsed in the subclasses of TtoCParseNode to generate the equivalent C code for each statement/ node built.

Static linking and compile with Object Engine:

The code can be statically linked and compiled along with the Object Engine code. This links the named Primitives given in the module as an array of function pointers. For statically compiled code the link is maintained by a tuple of values viz:

< "functionName" memoryAddress numofArgs > a listing of all exported functions tuples can be obtained for a given named module through: UserPrimitiveMethod someInstance builtinPrimitivesFor: 'BitBltPlugin' Dynamic Loading

Using the mechanism that is given for DLLCC:

o Library loading - given a platform specific library name, which usually represents a name that is searched for by using a set of default paths. ( ExternalLibrary load ) o Library unloading - given a loaded library identifier answer by the previous step, unload the library from memory. ( ExternalLibrary unload ) o Library name mapping - given an external symbol name, answer the symbols address. (ExternalLibrary mapAddressForName: nameID ) o Library error string - answers the last error created by any one of the previous three steps.

The dynamic load primitives are accessed through ExternalLibrary methods, called in from UserPrimitive>>linkMethod

Callout Process:

Every external procedure call is routed through the primitive 394 described below.

Refer HPSNewprim.txt for the text below:

“ This primitive is a vararg primitive responsible for taking the Smalltalk arguments from the Smalltalk stack frame and building the C stack frame in preparation for an external call.

 Since this primitive is vararg, The same primitive number is used for every external user primitive invocation .

 Unlike primitive 395 this version passes the receiver along with all arguments, and passes them directly without interpretation." Callbacks: Use oeSendMessage* OE access messages to call back into Smalltalk. Remember the library this primitive maker generates is always user constructed and not preexisting, thereby C Callbacks can easily be linked through this mechanism.

The framework can be extended to translate between object perform* msgs to the OE oeSendMessage*

Threads: The possibility to branch of a function execution on a native OS thread has been tested with custom implementation of _beginthread(ex) call and an API to link this can be created.

Will be testing with current VM thread call, management of threads and semaphore mechanism in this framework though NBAPIThreadCall etc..

Connecting to pre constructed Shared Libraries This is akin to constructing the wrapper to the library functions with each exported primitive function unmarshalling the passed oopInt parameters to the reqd C parameters for the existing library function

Extensions to 64 bit.. As of VW731 the conversion to 64 bit C code is also previewed… Other Issues…

 Connecting to pre constructed Shared Libraries  Extensions to 64 bit..

Quote from Squeak Doc and needs a reality check here..

1. “reduce the size of the vm executable 2. allow ( extending or) fixing of bugs in plugins without changing the vm 3. encourage more modular thinking by primitive implementors 4. encourage modular image code to use those plugins “

1313 Q & A

I had many for Eliot to answer for… let me try answering yours…!!!

Modified pic from www.dilbert.com

1414 Epilogue…

We didn’t need primitives at all….

 Despite all the rapid progress in CPU/RAM capability the motivation to obtain max performance remains…will continue to remain in future…

References:

{1}src/doc/vm/HPSExternal.text {2}src/doc/vi/HPSExternal.text {3}hps32_64.text {4}hpsglue.text {5}hpsnewprim.text {6}THAPI.html {7}Greenberg.pdf ( Squeak guide for VMPlugin ) {8}DLLCCGuide.pdf ( VW doc ) {9} VMPrimitiveMaker.pdf ( a draft currently ) 1515 Thanks for the patient hearing of the presentation…  2004 Cincom Systems, Inc. All Rights Reserved Developed in the U.S.A. CINCOM, , and The World’s Most Experienced Software Company are trademarks or registered trademarks of Cincom Systems, Inc All other trademarks belong to their respective companies.