DoubleArray Plugin Tutorial
Last updated at 3:59 am UTC on 20 May 2009
DoubleArray is a plugin that uses 64-bit Double precision floating point data type.
Part I: Setting up the VM and plugin generation environment
Get the following three zip files and unzip it to the specified location:
- Squeak VM source release http://www.squeakvm.org/win32/release/SqueakVM-Win32-3.10.7-src.zip
- unzip it into C:\, to have C:\SqueakVM-Win32-3.10.7 created
- Tools for VM compilation http://www.squeakvm.org/win32/release/Squeak-Win32-Tools-1.2.zip
- unzip into C:\ to have C:\gnutools and C:\dx7sdk created
- Right Click on My Computers and select properties.
- Click on the advanced tab and select environmental variables.
- Under System variable ,find the path variable and choose edit.
- Insert ";C:\GNUTools\bin" in the variable value field. Make sure there is a semicolon ";" separating it from the rest.
- Sqeauk 3.10 executable and image http://ftp.squeak.org/3.10/win/Squeak3.10.2-7179-win32.zip
- unzip it into C:\, to have C:\Squeak3.10.1-7175 created
- in C:\Squeak3.10.1-7175 double click on Squeak.exe
- Drag out a package loader from the right Tools tab. Search for VMMaker() in the search field on the top left field. Right-Click on VMMaker and select install. Click on yes/proceed on messages pop up/warning prompt.
- Close the package loader object and drag a Browser from the Tool tab. Scroll to the very bottom of the first left pane and select VMMaker-Building>Win32VMMaker. Right-Click on Win32VMMaker and select remove class. Click yes on the prompt.
- Drag out a File List from the Tools tab. Click on C:, then click on SqueakVMWin32-3.10.7, then platforms, win32, build. Now we are in C:\SqueakVMWin32-3.10.7\platforms\win32\build directory, find for Win32VMMaker.st then click on file in. Close file list.
- Double check. Now Win32VMMaker as subclass of VMMaker.
Part II: Writing the DoubleArray Class and Plugin
DoubleArray Class
- On the Browser Create the DoubleArray Class as a subclass of ByteArray
ByteArray variableByteSubclass: #DoubleArray
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'ADoubleArrayPackage'
- We start of by writing the class side method.
new: n
^ self basicNew: 8 * n
- Overide the size method.
size
^ self basicSize / 8
- The accessing methods.
at: i
<primitive: 'primitiveAtDoubleArray' module: 'DoubleArrayPlugin'>
^ self
at: i put: value
<primitive: 'primitiveAtPutDoubleArray' module: 'DoubleArrayPlugin'>
^ self
.
Primitive Accessors
- In order to save execution time, primitive accessors are created for DoubleArray Plugin. primitiveAtDoubleArray and primitiveAtPutDoubleArray are created:
primitiveAtDoubleArray
| size arrayC arraySq indexC index value |
self export: true.
self inline: false.
self var: #arrayC type: 'double *'.
self var: #indexC type: 'int'.
self var: #size type: 'int'.
index := interpreterProxy stackIntegerValue: 0.
arrayC := self loadDoubleArray: (arraySq := interpreterProxy stackObjectValue: 1).
"Check for the validity of index"
(index >= 1
and: [index <= (size := (interpreterProxy stSizeOf: arraySq)
/ 8)])
ifFalse: [interpreterProxy primitiveFail.
^ nil].
interpreterProxy failed
ifTrue: [^ nil].
indexC := index - 1.
interpreterProxy pop: 2.
interpreterProxy
pushFloat: (self
cCoerce: (arrayC at: indexC)
to: 'double')
primitiveAtPutDoubleArray
| size arrayC arraySq indexC valueC |
self export: true.
self inline: false.
self var: #arrayC type: 'double *'.
self var: #indexC type: 'int'.
self var: #valueC type: 'double'.
self var: #size type: 'int'.
value := interpreterProxy stackValue: 0.
(interpreterProxy isFloatObject: value)
ifTrue: [valueC := interpreterProxy floatValueOf: value]
ifFalse: [valueC := (interpreterProxy integerValueOf: value) asFloat].
indexC := interpreterProxy stackIntegerValue: 1.
arrayC := self loadDoubleArray: (arraySq := interpreterProxy stackObjectValue: 2).
"Check for the validity of index"
(indexC >= 1
and: [indexC <= (size := (interpreterProxy stSizeOf: arraySq)
/ 8)])
ifFalse: [interpreterProxy primitiveFail.
^ nil].
interpreterProxy failed
ifTrue: [^ nil].
arrayC
at: indexC - 1
put: (self cCoerce: valueC to: 'double').
interpreterProxy pop: 3.
interpreterProxy
pushFloat: (self
cCoerce: (arrayC at: indexC - 1)
to: 'double')
- These methods will be translated to the following respectively:
EXPORT(sqInt) primitiveAtDoubleArray(void) {
int size;
double * arrayC;
sqInt arraySq;
int indexC;
sqInt indexSq;
sqInt array;
indexSq = interpreterProxy->stackIntegerValue(0);
/* begin loadDoubleArray: */
array = arraySq = interpreterProxy->stackObjectValue(1);
if (interpreterProxy->failed()) {
arrayC = null;
goto l1;
}
if (!(interpreterProxy->isBytes(array))) {
interpreterProxy->primitiveFail();
arrayC = null;
goto l1;
}
arrayC = ((double *) (interpreterProxy->firstIndexableField(array)));
l1: /* end loadDoubleArray: */;
if (!((indexSq >= 1) && (indexSq <= (size = ((sqInt) (interpreterProxy->stSizeOf(arraySq)) >> 3))))) {
interpreterProxy->primitiveFail();
return null;
}
if (interpreterProxy->failed()) {
return null;
}
indexC = indexSq - 1;
interpreterProxy->pop(2);
interpreterProxy->pushFloat(((double) (arrayC[indexC])));
}
EXPORT(sqInt) primitiveAtPutDoubleArray(void) {
double valueC;
int size;
double * arrayC;
sqInt arraySq;
int indexC;
sqInt valueSq;
sqInt array;
valueSq = interpreterProxy->stackValue(0);
if (interpreterProxy->isFloatObject(valueSq)) {
valueC = interpreterProxy->floatValueOf(valueSq);
} else {
valueC = ((double) ((valueSq >> 1)) );
}
indexC = interpreterProxy->stackIntegerValue(1);
/* begin loadDoubleArray: */
array = arraySq = interpreterProxy->stackObjectValue(2);
if (interpreterProxy->failed()) {
arrayC = null;
goto l1;
}
if (!(interpreterProxy->isBytes(array))) {
interpreterProxy->primitiveFail();
arrayC = null;
goto l1;
}
arrayC = ((double *) (interpreterProxy->firstIndexableField(array)));
l1: /* end loadDoubleArray: */;
if (!((indexC >= 1) && (indexC <= (size = ((sqInt) (interpreterProxy->stSizeOf(arraySq)) >> 3))))) {
interpreterProxy->primitiveFail();
return null;
}
if (interpreterProxy->failed()) {
return null;
}
arrayC[indexC - 1] = (((double) valueC));
interpreterProxy->pop(3);
interpreterProxy->pushFloat(((double) (arrayC[indexC - 1])));
}
DoubleArrayPlugin
- Create a plugin module that is going to store the primitive as 'DoubleArrayPlugin'
InterpreterPlugin subclass: #DoubleArrayPlugin
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'ADoubleArrayPackage'
- Now, a method has to be written to load the DoubleArray type array and cast it to a 'double *' type in the DoubleArrayPlugin Class.
loadDoubleArray: array
"Load the DoubleArray array "
self returnTypeC: 'double*'.
interpreterProxy failed
ifTrue: [^ nil].
(interpreterProxy isBytes: array )
ifFalse: [interpreterProxy primitiveFail.
^ nil].
^ self
cCoerce: (interpreterProxy firstIndexableField: array )
to: 'double *'
Part III: Some DoubleArray plugin examples
A DoubleArray addition plugin
- First create the interface of the plugin where the method adds the argument array with the receiver and store it into the answer array.
add: array into: answer
primitive: 'primitiveAddArray' module: 'DoubleArrayPlugin'
1
to: self size
do: [:i | answer at: i put: ((self at: i)
+ (array at: i)) asFloat].
^ answer
Note: first statement call the primitive, second statement will execute when primitive fail, or if primitive not found.
- The primitive for the addition plugin is written.
primitiveAddArray
| resultC secondOperandC firstOperandC result operand rcvr size |
self export: true.
self inline: false.
self var: #resultC type: 'double*'.
self var: #secondOperandC type: 'double*'.
self var: #firstOperandC type: 'double*'.
self var: #size type: 'int'.
resultC := self loadDoubleArray: (result := interpreterProxy stackObjectValue: 0).
secondOperandC := self loadDoubleArray: (operand := interpreterProxy stackObjectValue: 1).
firstOperandC := self loadDoubleArray: (rcvr := interpreterProxy stackObjectValue: 2).
"Check for size of both the operands"
(size := interpreterProxy stSizeOf: operand)
= (interpreterProxy stSizeOf: rcvr)
ifFalse: [interpreterProxy primitiveFail.
^ nil].
interpreterProxy failed
ifTrue: [^ nil].
"Computation"
0
to: size / 8 - 1
do: [:i | resultC at: i put: (firstOperandC at: i)
+ (secondOperandC at: i)].
interpreterProxy pop: 3.
interpreterProxy push: result
A positive array test
positive
primitive: 'primitiveArrayIsPositive' module: 'DoubleArrayPlugin'
1
to: self size
do: [:i | (self at: i) positive
ifFalse: [^ false]].
^ true
primitiveArrayIsPositive
| rcvrC rcvr result |
self export: true.
self inline: false.
self var: #rcvrC type: 'double*'.
rcvrC := self loadDoubleArray: (rcvr := interpreterProxy stackObjectValue: 0).
interpreterProxy failed
ifTrue: [^ nil].
result := true.
0
to: (interpreterProxy stSizeOf: rcvr)
/ 8 - 1
do: [:i | (rcvrC at: i)
>= 0
ifFalse: [result := false]].
interpreterProxy pop: 1.
interpreterProxy pushBool: result
A plugin to round the array to a factor.
- Interface:
roundTo: aFloat into: answer
<primitive: 'primitiveRoundArray' module: 'DoubleArrayPlugin'>
^ self
collect: [:arg | arg roundTo: aFloat]
- Primitive:
primitiveRoundArray
| resultC roundingFactor rcvrC result temp |
self export: true.
self inline: false.
self var: #resultC type: 'double *'.
self var: #roundingFactor type: 'double'.
self var: #rcvrC type: 'double *'.
self var: #temp type: 'double'.
resultC := self loadDoubleArray: (result := interpreterProxy stackObjectValue: 0).
roundingFactor := interpreterProxy stackFloatValue: 1.
rcvrC := self
loadDoubleArray: (interpreterProxy stackObjectValue: 2).
interpreterProxy failed
ifTrue: [^ nil].
"Computation"
0
to: (interpreterProxy stSizeOf: result)
/ 8 - 1
do: [:i |
temp := (rcvrC at: i)
/ roundingFactor.
temp > 0
ifTrue: [temp - temp floor >= 0.5
ifTrue: ["Invoking ceil() from math.h"
resultC at: i put: temp ceil * roundingFactor]
ifFalse: [resultC at: i put: temp floor * roundingFactor]]
ifFalse: [temp - temp ceil <= -0.5
ifTrue: ["Invoking floor() from math.h"
resultC at: i put: temp floor * roundingFactor]
ifFalse: [resultC at: i put: temp ceil * roundingFactor]]].
interpreterProxy pop: 3.
interpreterProxy push: result
- The corresponding C code translation would be
EXPORT(sqInt) primitiveRoundArray(void) {
double * rcvrC;
double roundingFactor;
sqInt result;
sqInt i;
double * resultC;
double temp;
sqInt array;
sqInt array1;
/* begin loadDoubleArray: */
array = result = interpreterProxy->stackObjectValue(0);
if (interpreterProxy->failed()) {
resultC = null;
goto l1;
}
if (!(interpreterProxy->isBytes(array))) {
interpreterProxy->primitiveFail();
resultC = null;
goto l1;
}
resultC = ((double *) (interpreterProxy->firstIndexableField(array)));
l1: /* end loadDoubleArray: */;
roundingFactor = interpreterProxy->stackFloatValue(1);
/* begin loadDoubleArray: */
array1 = interpreterProxy->stackObjectValue(2);
if (interpreterProxy->failed()) {
rcvrC = null;
goto l2;
}
if (!(interpreterProxy->isBytes(array1))) {
interpreterProxy->primitiveFail();
rcvrC = null;
goto l2;
}
rcvrC = ((double *) (interpreterProxy->firstIndexableField(array1)));
l2: /* end loadDoubleArray: */;
if (interpreterProxy->failed()) {
return null;
}
for (i = 0; i <= ((((sqInt) (interpreterProxy->stSizeOf(result)) >> 3)) - 1); i += 1) {
temp = (rcvrC[i]) / roundingFactor;
if (temp > 0) {
if ((temp - (floor(temp))) >= 0.5) {
resultC[i] = ((ceil(temp)) * roundingFactor);
} else {
resultC[i] = ((floor(temp)) * roundingFactor);
}
} else {
if ((temp - (ceil(temp))) <= -0.5) {
resultC[i] = ((floor(temp)) * roundingFactor);
} else {
resultC[i] = ((ceil(temp)) * roundingFactor);
}
}
}
interpreterProxy->pop(3);
interpreterProxy->push(result);
}
Part IV: Generating and test the plugin
- Drag a workspace from the Tools tab. We can now test the primitive. Type the following in the workspace.
VMMakerTool openInWorld
ctrl + d to open the VMMaker GUI and fill in the text fields with the following:Interpreter class name: Interpreter
Path to platform code: C:\SqueakVM-Win32-3.10.7\platforms
Platform name: Win32
Path to generate sources: C:\SqueakVM-Win32-3.10.7\platforms\win32\build\src
- Right click in the Plugins not built pane and select make all internal. From Internal Plugins pane, drag DoubleArrayPlugin to the External Plugins pane. Then, click on Entire to translate the files into .C equivalent for compilation.
Test Run
- In the workspace, type in the following to test for the addition plugin:
| m1 m2 dm1 |
m1 := DoubleArray new: 3.
m1 at: 1 put: 1.0.
m1 at: 2 put: 2.000000000003.
m1 at: 3 put: 1/3.
m2 := DoubleArray new: 3.
m2 at: 1 put: 1.0.
m2 at: 2 put: 1.0.
m2 at: 3 put: 1.0.
m3 := DoubleArray new: 3.
m1 add: m2 into: m3
- For the positive test, type in the following:
| m1 dm1 |
m1 := DoubleArray new: 3.
m1 at: 1 put: 1.00000000002.
m1 at: 2 put: 1.00000000003.
m1 at: 3 put: 5.5.
m1 positive.
- For the rounding test, type in the following:
| m1 dm1 |
m1 := DoubleArray new: 3.
m1 at: 1 put: 1.00000000002.
m1 at: 2 put: 4.9.
m1 at: 3 put: 5.5.
m1 roundTo: 5 into: m2.