Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SqueakJS V2 (WIP) #168

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
V2 BRANCH
=========
This is the work-in-progress branch for SqueakJS 2.0. Things I want to change:

* each fixed inst var gets its own property instead for direct access instead of being indexed in `pointers[]`. There will be a compatibility accessor for primitives that use indexed access.

Still need to decide between named inst vars (using inst var names from image) or suffixed (like `p0`, `p1`, ...)

The goal is faster access than via the `pointers[]` array.
Also, nicer debuggability if we use actual names.

* new high-performance JIT without per-frame context allocation, but instead using direct function calls, function temps as stack, args passed directly via function parameters, and direct instance var access (see above). Contexts would only be allocated if needed (also see the existing [discussion](https://github.com/codefrau/SqueakJS/issues/121) and my [JIT experiments](https://squeak.js.org/docs/jit.md.html))

The goal is to make the jitted methods look as close to "normal" JavaScript functions as possible, so that the JS JIT can optimize them, even with inlining etc.

* (maybe) use `WeakRef` and `WeakMap`? All JS runtimes now support weak objects (`WeakRef` is still pretty new, since 2021).

The goal would be to have faster GCs while still supporting object enumeration.

* (maybe) use WASM for BitBlt etc. To avoid copying in and out of the WASM heap, we could use binary arrays allocated via WASM (but would need to implement GC for that)

Goal: speed

* (maybe) `BigInt` for large integer primitives? Supported in browsers since 2020 (and allowed to fail if not available). Need to measure fastest way to convert from/to `Uint8Array` representation. (this is actually independent of v2, see the existing [discussion](https://github.com/codefrau/SqueakJS/issues/37))

The goal is faster LargeInteger calculations.

* (maybe) no more `.oop` property: it's not needed at runtime, and updating it makes the GC slower.

Goal: faster GC

* (maybe) export as 64 bit image: on load, 64-bit images are converted to 32 bits. On export, we could store them as 64 bits (and if we get rid of the `.oop` as mentioned above it may actually be almost as fast as snapshotting in 32 bits)

The goal here is compatibility with other VMs, which on some systems only run 64 bit images

Feedback and ideas: please comment on the [Pull Request](https://github.com/codefrau/SqueakJS/pull/168) or use the [vm-dev](http://lists.squeak.org/mailman/listinfo/vm-dev) mailing list and `#squeakjs` channel on the [Squeak Slack](https://join.slack.com/t/squeak/shared_invite/zt-2ahdbewgl-56nPdkf1hYACBmc8xCOXRQ).

SqueakJS: A Squeak VM for the Web and Node.js
=============================================

Expand Down
46 changes: 33 additions & 13 deletions vm.image.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Object.subclass('Squeak.Image',
hash = (header>>>17) & 4095,
bits = readBits(nWords, format < 5);
var object = new Squeak.Object();
object.initFromImage(oop, classInt, format, hash);
object.initFromBits(oop, classInt, format, hash);
if (classInt < 32) object.hash |= 0x10000000; // see fixCompactOops()
if (prevObj) prevObj.nextObject = object;
this.oldSpaceCount++;
Expand Down Expand Up @@ -236,7 +236,7 @@ Object.subclass('Squeak.Image',
// low class ids are internal to Spur
if (classID >= 32) {
var object = new Squeak.ObjectSpur();
object.initFromImage(oop, classID, format, hash);
object.initFromBits(oop, classID, format, hash);
if (prevObj) prevObj.nextObject = object;
this.oldSpaceCount++;
prevObj = object;
Expand Down Expand Up @@ -296,7 +296,7 @@ Object.subclass('Squeak.Image',
prevObj = null;
while (object) {
prevObj = renamedObj;
renamedObj = object.renameFromImage(oopMap, rawBits, cc);
renamedObj = object.renameFromBits(oopMap, rawBits, cc);
if (prevObj) prevObj.nextObject = renamedObj;
else this.firstOldObject = renamedObj;
oopMap[oldBaseAddr + object.oop] = renamedObj;
Expand All @@ -310,26 +310,25 @@ Object.subclass('Squeak.Image',
var splObs = oopMap[specialObjectsOopInt];
var compactClasses = rawBits[oopMap[rawBits[splObs.oop][Squeak.splOb_CompactClasses]].oop];
var floatClass = oopMap[rawBits[splObs.oop][Squeak.splOb_ClassFloat]];
// Spur needs different arguments for installFromImage()
// Spur needs different arguments for installFromBits()
if (this.isSpur) {
this.initImmediateClasses(oopMap, rawBits, splObs);
compactClasses = this.spurClassTable(oopMap, rawBits, classPages, splObs);
nativeFloats = this.getCharacter.bind(this);
this.initSpurOverrides();
}
// figure out if Class_instVars is 3 or 4 or unknown
Squeak.Class_instVars = this.detectClassInstVarIndex(splObs, oopMap, rawBits);
// now "install" the objects, i.e. decode the bits into proper references, etc.
var obj = this.firstOldObject,
done = 0;
var mapSomeObjects = function() {
if (obj) {
var stop = done + (this.oldSpaceCount / 20 | 0); // do it in 20 chunks
while (obj && done < stop) {
obj.installFromImage(oopMap, rawBits, compactClasses, floatClass, littleEndian, nativeFloats, is64Bit && {
makeFloat: function makeFloat(bits) {
return this.instantiateFloat(bits);
}.bind(this),
makeLargeFromSmall: function makeLargeFromSmall(hi, lo) {
return this.instantiateLargeFromSmall(hi, lo);
}.bind(this),
obj.installFromBits(oopMap, rawBits, compactClasses, floatClass, littleEndian, nativeFloats, is64Bit && {
makeFloat: bits => this.instantiateFloat(bits),
makeLargeFromSmall: (hi, lo) => this.instantiateLargeFromSmall(hi, lo),
});
obj = obj.nextObject;
done++;
Expand Down Expand Up @@ -364,6 +363,27 @@ Object.subclass('Squeak.Image',
self.setTimeout(mapSomeObjectsAsync, 0);
}
},
detectClassInstVarIndex: function(splObs, oopMap, rawBits) {
// the VM really should only make assumptions about inst vars 0-2
// but we want to use the actual instance variable names
// which are at index 3 or 4 in the class
var classPoint = oopMap[rawBits[splObs.oop][Squeak.splOb_ClassPoint]];
var classBits = rawBits[classPoint.oop];
// we check if the array #(x y) is anywhere in the Point class
// starting at index 3 (indices 0-2 are known to the VM)
for (var index = 3; index < classBits.length; index++) {
var names = oopMap[classBits[index]]; if (!names) continue;
var namesBits = rawBits[names.oop]; if (namesBits.length !== 2) continue;
var x = oopMap[namesBits[0]];
var xBits = rawBits[x.oop];
if (String.fromCharCode(xBits[0]) !== 'x') continue;
var y = oopMap[namesBits[1]];
var yBits = rawBits[y.oop];
if (String.fromCharCode(yBits[0]) !== 'y') continue;
return index;
}
return 0; // unknown
},
decorateKnownObjects: function() {
var splObjs = this.specialObjectsArray.pointers;
splObjs[Squeak.splOb_NilObject].isNil = true;
Expand Down Expand Up @@ -1140,7 +1160,7 @@ Object.subclass('Squeak.Image',
bits = readBits(nWords, format);

var object = new Squeak.Object();
object.initFromImage(oop + oopOffset, classInt, format, hash);
object.initFromBits(oop + oopOffset, classInt, format, hash);
prevObj.nextObject = object;
this.oldSpaceCount++;
prevObj = object;
Expand All @@ -1164,7 +1184,7 @@ Object.subclass('Squeak.Image',
floatClass = this.specialObjectsArray.pointers[Squeak.splOb_ClassFloat],
obj = roots;
do {
obj.installFromImage(oopMap, rawBits, compactClassOops, floatClass, littleEndian, nativeFloats);
obj.installFromBits(oopMap, rawBits, compactClassOops, floatClass, littleEndian, nativeFloats);
obj = obj.nextObject;
} while (obj !== endMarker);
return roots;
Expand Down
2 changes: 1 addition & 1 deletion vm.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Object.extend(Squeak,
Class_superclass: 0,
Class_mdict: 1,
Class_format: 2,
Class_instVars: null, // 3 or 4 depending on image, see instVarNames()
Class_instVars: null, // 3 or 4 or unknown depending on image, see detectClassInstVarIndex()
Class_name: 6,
// ClassBinding layout:
ClassBinding_value: 1,
Expand Down
Loading