porffor 0.16.0-a8f23d010 → 0.16.0-b099006b8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +1 -1
- package/compiler/codegen.js +165 -49
- package/compiler/cyclone.js +11 -11
- package/compiler/opt.js +3 -3
- package/compiler/parse.js +1 -1
- package/compiler/pgo.js +8 -8
- package/compiler/precompile.js +1 -3
- package/compiler/prefs.js +1 -1
- package/compiler/wrap.js +1 -1
- package/package.json +5 -3
- package/runner/index.js +3 -8
- package/compiler/allocators/chunk.js +0 -0
- package/compiler/allocators/solo.js +0 -0
- package/compiler/allocators/static.js +0 -41
package/CONTRIBUTING.md
CHANGED
@@ -26,7 +26,7 @@ You can also swap out `node` in the alias to use another runtime like Deno (`den
|
|
26
26
|
|
27
27
|
### Precompile
|
28
28
|
|
29
|
-
**If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run
|
29
|
+
**If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run `node compiler/precompile.js` to precompile. It may error during this, if so, you might have an error in your code or there could be a compiler error with Porffor (feel free to ask for help as soon as you encounter any errors with it).
|
30
30
|
|
31
31
|
<br>
|
32
32
|
|
package/compiler/codegen.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Blocktype, Opcodes, Valtype, ValtypeSize } from './wasmSpec.js';
|
1
|
+
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from './wasmSpec.js';
|
2
2
|
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } from './encoding.js';
|
3
3
|
import { operatorOpcode } from './expression.js';
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from './builtins.js';
|
@@ -181,6 +181,12 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
181
181
|
continue;
|
182
182
|
}
|
183
183
|
|
184
|
+
if (asm[0] === 'memory') {
|
185
|
+
allocPage(scope, 'asm instrinsic');
|
186
|
+
// todo: add to store/load offset insts
|
187
|
+
continue;
|
188
|
+
}
|
189
|
+
|
184
190
|
let inst = Opcodes[asm[0].replace('.', '_')];
|
185
191
|
if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
186
192
|
|
@@ -427,6 +433,57 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
427
433
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
428
434
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
429
435
|
|
436
|
+
if (assign && Prefs.aotPointerOpt) {
|
437
|
+
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
438
|
+
|
439
|
+
return [
|
440
|
+
// setup right
|
441
|
+
...right,
|
442
|
+
Opcodes.i32_to_u,
|
443
|
+
[ Opcodes.local_set, rightPointer ],
|
444
|
+
|
445
|
+
// calculate length
|
446
|
+
...number(0, Valtype.i32), // base 0 for store later
|
447
|
+
|
448
|
+
...number(pointer, Valtype.i32),
|
449
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
450
|
+
[ Opcodes.local_tee, leftLength ],
|
451
|
+
|
452
|
+
[ Opcodes.local_get, rightPointer ],
|
453
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
454
|
+
[ Opcodes.local_tee, rightLength ],
|
455
|
+
|
456
|
+
[ Opcodes.i32_add ],
|
457
|
+
|
458
|
+
// store length
|
459
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
460
|
+
|
461
|
+
// copy right
|
462
|
+
// dst = out pointer + length size + current length * sizeof valtype
|
463
|
+
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
464
|
+
|
465
|
+
[ Opcodes.local_get, leftLength ],
|
466
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
467
|
+
[ Opcodes.i32_mul ],
|
468
|
+
[ Opcodes.i32_add ],
|
469
|
+
|
470
|
+
// src = right pointer + length size
|
471
|
+
[ Opcodes.local_get, rightPointer ],
|
472
|
+
...number(ValtypeSize.i32, Valtype.i32),
|
473
|
+
[ Opcodes.i32_add ],
|
474
|
+
|
475
|
+
// size = right length * sizeof valtype
|
476
|
+
[ Opcodes.local_get, rightLength ],
|
477
|
+
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
478
|
+
[ Opcodes.i32_mul ],
|
479
|
+
|
480
|
+
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
481
|
+
|
482
|
+
// return new string (page)
|
483
|
+
...number(pointer)
|
484
|
+
];
|
485
|
+
}
|
486
|
+
|
430
487
|
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
431
488
|
|
432
489
|
// alloc/assign array
|
@@ -2058,6 +2115,7 @@ const brTable = (input, bc, returns) => {
|
|
2058
2115
|
}
|
2059
2116
|
|
2060
2117
|
for (let i = 0; i < count; i++) {
|
2118
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2061
2119
|
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2062
2120
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2063
2121
|
}
|
@@ -2091,8 +2149,10 @@ const brTable = (input, bc, returns) => {
|
|
2091
2149
|
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
2092
2150
|
);
|
2093
2151
|
|
2094
|
-
// sort the wrong way and then reverse
|
2095
|
-
//
|
2152
|
+
// if you can guess why we sort the wrong way and then reverse
|
2153
|
+
// (instead of just sorting the correct way)
|
2154
|
+
// dm me and if you are correct and the first person
|
2155
|
+
// I will somehow shout you out or something
|
2096
2156
|
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
2097
2157
|
|
2098
2158
|
br = count - 1;
|
@@ -2118,7 +2178,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2118
2178
|
return bc[known] ?? bc.default;
|
2119
2179
|
}
|
2120
2180
|
|
2121
|
-
if (Prefs.
|
2181
|
+
if (Prefs.typeswitchUseBrtable)
|
2122
2182
|
return brTable(type, bc, returns);
|
2123
2183
|
|
2124
2184
|
const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
|
@@ -2339,12 +2399,19 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2339
2399
|
|
2340
2400
|
// hack: .length setter
|
2341
2401
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
2402
|
+
const name = decl.left.object.name;
|
2403
|
+
const pointer = scope.arrays?.get(name);
|
2404
|
+
|
2405
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2406
|
+
|
2342
2407
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2343
2408
|
const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2344
2409
|
|
2345
2410
|
return [
|
2346
|
-
...
|
2347
|
-
|
2411
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
2412
|
+
...generate(scope, decl.left.object),
|
2413
|
+
Opcodes.i32_to_u
|
2414
|
+
]),
|
2348
2415
|
...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2349
2416
|
|
2350
2417
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
@@ -2355,7 +2422,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2355
2422
|
[ Opcodes.local_tee, newValueTmp ],
|
2356
2423
|
|
2357
2424
|
Opcodes.i32_to_u,
|
2358
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
2425
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
2359
2426
|
|
2360
2427
|
[ Opcodes.local_get, newValueTmp ]
|
2361
2428
|
];
|
@@ -2363,14 +2430,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2363
2430
|
|
2364
2431
|
// arr[i]
|
2365
2432
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2433
|
+
const name = decl.left.object.name;
|
2434
|
+
const pointer = scope.arrays?.get(name);
|
2435
|
+
|
2436
|
+
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2437
|
+
|
2366
2438
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2367
2439
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2368
2440
|
|
2369
2441
|
return [
|
2370
2442
|
...typeSwitch(scope, getNodeType(scope, decl.left.object), {
|
2371
2443
|
[TYPES.array]: [
|
2372
|
-
...
|
2373
|
-
|
2444
|
+
...(aotPointer ? [] : [
|
2445
|
+
...generate(scope, decl.left.object),
|
2446
|
+
Opcodes.i32_to_u
|
2447
|
+
]),
|
2374
2448
|
|
2375
2449
|
// get index as valtype
|
2376
2450
|
...generate(scope, decl.left.property),
|
@@ -2379,22 +2453,39 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2379
2453
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2380
2454
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2381
2455
|
[ Opcodes.i32_mul ],
|
2382
|
-
[ Opcodes.i32_add ],
|
2456
|
+
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2383
2457
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2384
2458
|
|
2385
2459
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2386
2460
|
[ Opcodes.local_get, pointerTmp ],
|
2387
|
-
[ Opcodes.load, 0, ValtypeSize.i32 ]
|
2461
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2388
2462
|
], generate(scope, decl.right), [
|
2389
2463
|
[ Opcodes.local_get, pointerTmp ],
|
2390
|
-
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
2464
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2391
2465
|
], getNodeType(scope, decl.right), false, name, true)),
|
2392
2466
|
[ Opcodes.local_tee, newValueTmp ],
|
2393
2467
|
|
2394
|
-
[ Opcodes.store, 0, ValtypeSize.i32 ]
|
2468
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2395
2469
|
],
|
2396
2470
|
|
2397
2471
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
2472
|
+
|
2473
|
+
// [TYPES.string]: [
|
2474
|
+
// // turn into byte offset by * sizeof i16
|
2475
|
+
// ...number(ValtypeSize.i16, Valtype.i32),
|
2476
|
+
// [ Opcodes.i32_mul ],
|
2477
|
+
// ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2478
|
+
// ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2479
|
+
|
2480
|
+
// ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2481
|
+
// [ Opcodes.local_get, pointerTmp ],
|
2482
|
+
// [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2483
|
+
// ], generate(scope, decl.right), number(TYPES.string, Valtype.i32), getNodeType(scope, decl.right))),
|
2484
|
+
// [ Opcodes.local_tee, newValueTmp ],
|
2485
|
+
|
2486
|
+
// Opcodes.i32_to_u,
|
2487
|
+
// [ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2488
|
+
// ]
|
2398
2489
|
}, Blocktype.void),
|
2399
2490
|
|
2400
2491
|
[ Opcodes.local_get, newValueTmp ]
|
@@ -3116,6 +3207,16 @@ const allocPage = (scope, reason, type) => {
|
|
3116
3207
|
return ind;
|
3117
3208
|
};
|
3118
3209
|
|
3210
|
+
// todo: add scope.pages
|
3211
|
+
const freePage = reason => {
|
3212
|
+
const { ind } = pages.get(reason);
|
3213
|
+
pages.delete(reason);
|
3214
|
+
|
3215
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
3216
|
+
|
3217
|
+
return ind;
|
3218
|
+
};
|
3219
|
+
|
3119
3220
|
const itemTypeToValtype = {
|
3120
3221
|
i32: 'i32',
|
3121
3222
|
i64: 'i64',
|
@@ -3169,7 +3270,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3169
3270
|
firstAssign = true;
|
3170
3271
|
|
3171
3272
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3172
|
-
const uniqueName = name === '$undeclared' ? name +
|
3273
|
+
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3173
3274
|
|
3174
3275
|
let page;
|
3175
3276
|
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
@@ -3190,7 +3291,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3190
3291
|
const valtype = itemTypeToValtype[itemType];
|
3191
3292
|
const length = elements.length;
|
3192
3293
|
|
3193
|
-
if (
|
3294
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
3194
3295
|
// if length is 0 memory/data will just be 0000... anyway
|
3195
3296
|
if (length !== 0) {
|
3196
3297
|
let bytes = compileBytes(length, 'i32');
|
@@ -3258,7 +3359,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3258
3359
|
return [ out, pointer ];
|
3259
3360
|
};
|
3260
3361
|
|
3261
|
-
const storeArray = (scope, array, index, element) => {
|
3362
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3262
3363
|
if (!Array.isArray(element)) element = generate(scope, element);
|
3263
3364
|
if (typeof index === 'number') index = number(index);
|
3264
3365
|
|
@@ -3270,25 +3371,26 @@ const storeArray = (scope, array, index, element) => {
|
|
3270
3371
|
Opcodes.i32_to_u,
|
3271
3372
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3272
3373
|
[ Opcodes.i32_mul ],
|
3273
|
-
|
3274
|
-
|
3275
|
-
|
3276
|
-
|
3374
|
+
...(aotPointer ? [] : [
|
3375
|
+
...array,
|
3376
|
+
Opcodes.i32_to_u,
|
3377
|
+
[ Opcodes.i32_add ],
|
3378
|
+
]),
|
3277
3379
|
[ Opcodes.local_set, offset ],
|
3278
3380
|
|
3279
3381
|
// store value
|
3280
3382
|
[ Opcodes.local_get, offset ],
|
3281
3383
|
...generate(scope, element),
|
3282
|
-
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
3384
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3283
3385
|
|
3284
3386
|
// store type
|
3285
3387
|
[ Opcodes.local_get, offset ],
|
3286
3388
|
...getNodeType(scope, element),
|
3287
|
-
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
3389
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3288
3390
|
];
|
3289
3391
|
};
|
3290
3392
|
|
3291
|
-
const loadArray = (scope, array, index) => {
|
3393
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3292
3394
|
if (typeof index === 'number') index = number(index);
|
3293
3395
|
|
3294
3396
|
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
@@ -3299,19 +3401,20 @@ const loadArray = (scope, array, index) => {
|
|
3299
3401
|
Opcodes.i32_to_u,
|
3300
3402
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3301
3403
|
[ Opcodes.i32_mul ],
|
3302
|
-
|
3303
|
-
|
3304
|
-
|
3305
|
-
|
3404
|
+
...(aotPointer ? [] : [
|
3405
|
+
...array,
|
3406
|
+
Opcodes.i32_to_u,
|
3407
|
+
[ Opcodes.i32_add ],
|
3408
|
+
]),
|
3306
3409
|
[ Opcodes.local_set, offset ],
|
3307
3410
|
|
3308
3411
|
// load value
|
3309
3412
|
[ Opcodes.local_get, offset ],
|
3310
|
-
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
3413
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3311
3414
|
|
3312
3415
|
// load type
|
3313
3416
|
[ Opcodes.local_get, offset ],
|
3314
|
-
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
3417
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3315
3418
|
];
|
3316
3419
|
};
|
3317
3420
|
|
@@ -3362,6 +3465,9 @@ const withType = (scope, wasm, type) => [
|
|
3362
3465
|
|
3363
3466
|
const generateMember = (scope, decl, _global, _name) => {
|
3364
3467
|
const name = decl.object.name;
|
3468
|
+
const pointer = scope.arrays?.get(name);
|
3469
|
+
|
3470
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3365
3471
|
|
3366
3472
|
// hack: .name
|
3367
3473
|
if (decl.property.name === 'name') {
|
@@ -3402,10 +3508,12 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3402
3508
|
if (Prefs.fastLength) {
|
3403
3509
|
// presume valid length object
|
3404
3510
|
return [
|
3405
|
-
...
|
3406
|
-
|
3511
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3512
|
+
...generate(scope, decl.object),
|
3513
|
+
Opcodes.i32_to_u
|
3514
|
+
]),
|
3407
3515
|
|
3408
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3516
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3409
3517
|
Opcodes.i32_from_u
|
3410
3518
|
];
|
3411
3519
|
}
|
@@ -3414,10 +3522,12 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3414
3522
|
const known = knownType(scope, type);
|
3415
3523
|
if (known != null) {
|
3416
3524
|
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3417
|
-
...
|
3418
|
-
|
3525
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3526
|
+
...generate(scope, decl.object),
|
3527
|
+
Opcodes.i32_to_u
|
3528
|
+
]),
|
3419
3529
|
|
3420
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3530
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3421
3531
|
Opcodes.i32_from_u
|
3422
3532
|
];
|
3423
3533
|
|
@@ -3427,10 +3537,12 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3427
3537
|
return [
|
3428
3538
|
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3429
3539
|
[ Opcodes.if, valtypeBinary ],
|
3430
|
-
...
|
3431
|
-
|
3540
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3541
|
+
...generate(scope, decl.object),
|
3542
|
+
Opcodes.i32_to_u
|
3543
|
+
]),
|
3432
3544
|
|
3433
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3545
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3434
3546
|
Opcodes.i32_from_u,
|
3435
3547
|
|
3436
3548
|
...setLastType(scope, TYPES.number),
|
@@ -3482,7 +3594,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3482
3594
|
|
3483
3595
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3484
3596
|
[TYPES.array]: [
|
3485
|
-
...loadArray(scope, object, property),
|
3597
|
+
...loadArray(scope, object, property, aotPointer),
|
3486
3598
|
...setLastType(scope)
|
3487
3599
|
],
|
3488
3600
|
|
@@ -3499,12 +3611,14 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3499
3611
|
...number(ValtypeSize.i16, Valtype.i32),
|
3500
3612
|
[ Opcodes.i32_mul ],
|
3501
3613
|
|
3502
|
-
...
|
3503
|
-
|
3504
|
-
|
3614
|
+
...(aotPointer ? [] : [
|
3615
|
+
...object,
|
3616
|
+
Opcodes.i32_to_u,
|
3617
|
+
[ Opcodes.i32_add ]
|
3618
|
+
]),
|
3505
3619
|
|
3506
3620
|
// load current string ind {arg}
|
3507
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
3621
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3508
3622
|
|
3509
3623
|
// store to new string ind 0
|
3510
3624
|
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
@@ -3523,12 +3637,14 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3523
3637
|
...property,
|
3524
3638
|
Opcodes.i32_to_u,
|
3525
3639
|
|
3526
|
-
...
|
3527
|
-
|
3528
|
-
|
3640
|
+
...(aotPointer ? [] : [
|
3641
|
+
...object,
|
3642
|
+
Opcodes.i32_to_u,
|
3643
|
+
[ Opcodes.i32_add ]
|
3644
|
+
]),
|
3529
3645
|
|
3530
3646
|
// load current string ind {arg}
|
3531
|
-
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
3647
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3532
3648
|
|
3533
3649
|
// store to new string ind 0
|
3534
3650
|
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
@@ -3542,7 +3658,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3542
3658
|
});
|
3543
3659
|
};
|
3544
3660
|
|
3545
|
-
const randId = () => Math.random().toString(16).slice(
|
3661
|
+
const randId = () => Math.random().toString(16).slice(0, -4);
|
3546
3662
|
|
3547
3663
|
const objectHack = node => {
|
3548
3664
|
if (!node) return node;
|
@@ -3594,7 +3710,7 @@ const generateFunc = (scope, decl) => {
|
|
3594
3710
|
if (decl.async) return todo(scope, 'async functions are not supported');
|
3595
3711
|
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
3596
3712
|
|
3597
|
-
const name = decl.id ? decl.id.name : `
|
3713
|
+
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3598
3714
|
const params = decl.params ?? [];
|
3599
3715
|
|
3600
3716
|
// TODO: share scope/locals between !!!
|
package/compiler/cyclone.js
CHANGED
@@ -246,7 +246,7 @@ export default wasm => {
|
|
246
246
|
const [ b, a ] = pop2();
|
247
247
|
const v = bool(a === b);
|
248
248
|
|
249
|
-
replaceVal(v, Valtype.
|
249
|
+
replaceVal(v, Valtype.f64);
|
250
250
|
push(v);
|
251
251
|
break;
|
252
252
|
}
|
@@ -255,7 +255,7 @@ export default wasm => {
|
|
255
255
|
const [ b, a ] = pop2();
|
256
256
|
const v = bool(a !== b);
|
257
257
|
|
258
|
-
replaceVal(v, Valtype.
|
258
|
+
replaceVal(v, Valtype.f64);
|
259
259
|
push(v);
|
260
260
|
break;
|
261
261
|
}
|
@@ -264,7 +264,7 @@ export default wasm => {
|
|
264
264
|
const [ b, a ] = pop2();
|
265
265
|
const v = bool(a < b);
|
266
266
|
|
267
|
-
replaceVal(v, Valtype.
|
267
|
+
replaceVal(v, Valtype.f64);
|
268
268
|
push(v);
|
269
269
|
break;
|
270
270
|
}
|
@@ -273,7 +273,7 @@ export default wasm => {
|
|
273
273
|
const [ b, a ] = pop2();
|
274
274
|
const v = bool(a <= b);
|
275
275
|
|
276
|
-
replaceVal(v, Valtype.
|
276
|
+
replaceVal(v, Valtype.f64);
|
277
277
|
push(v);
|
278
278
|
break;
|
279
279
|
}
|
@@ -282,7 +282,7 @@ export default wasm => {
|
|
282
282
|
const [ b, a ] = pop2();
|
283
283
|
const v = bool(a > b);
|
284
284
|
|
285
|
-
replaceVal(v, Valtype.
|
285
|
+
replaceVal(v, Valtype.f64);
|
286
286
|
push(v);
|
287
287
|
break;
|
288
288
|
}
|
@@ -291,7 +291,7 @@ export default wasm => {
|
|
291
291
|
const [ b, a ] = pop2();
|
292
292
|
const v = bool(a >= b);
|
293
293
|
|
294
|
-
replaceVal(v, Valtype.
|
294
|
+
replaceVal(v, Valtype.f64);
|
295
295
|
push(v);
|
296
296
|
break;
|
297
297
|
}
|
@@ -404,7 +404,7 @@ export default wasm => {
|
|
404
404
|
case Opcodes.f64_max: {
|
405
405
|
if (stack.length < 2) { empty(); break; };
|
406
406
|
const [ b, a ] = pop2();
|
407
|
-
const v =
|
407
|
+
const v = a + b;
|
408
408
|
|
409
409
|
replaceVal(v, Valtype.f64);
|
410
410
|
push(v);
|
@@ -467,9 +467,9 @@ export default wasm => {
|
|
467
467
|
// i32.const 1
|
468
468
|
// i32.add
|
469
469
|
// local.set 7 ;; $i (i32)
|
470
|
-
if (
|
471
|
-
(
|
472
|
-
(opcode >= 0x61 && opcode <= 0x66)
|
470
|
+
if (
|
471
|
+
(opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
|
472
|
+
(opcode >= 0x61 && opcode <= 0x66) // main f64 eq op
|
473
473
|
) {
|
474
474
|
const o2 = wasm[i - 1][0];
|
475
475
|
if (o2 === Opcodes.f64_const) { // f64.const
|
@@ -500,7 +500,7 @@ export default wasm => {
|
|
500
500
|
}
|
501
501
|
}
|
502
502
|
|
503
|
-
if ((opcode === 0xfc02 || opcode === 0xfc03) && i
|
503
|
+
if ((opcode === 0xfc02 || opcode === 0xfc03) && i > 3) { // i32.trunc_sat_f64_s/u
|
504
504
|
const o2 = wasm[i - 1][0];
|
505
505
|
if (
|
506
506
|
(o2 >= 0xa0 && o2 <= 0xa3) || // main f64 math op
|
package/compiler/opt.js
CHANGED
@@ -172,14 +172,14 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
172
172
|
}
|
173
173
|
}
|
174
174
|
|
175
|
-
if (inst[inst.length - 1] === 'string_only' && !pages.hasAnyString && Prefs.
|
175
|
+
if (inst[inst.length - 1] === 'string_only' && !pages.hasAnyString && !Prefs.noRmUnusedTypes) {
|
176
176
|
// remove this inst
|
177
177
|
wasm.splice(i, 1);
|
178
178
|
if (i > 0) i--;
|
179
179
|
inst = wasm[i];
|
180
180
|
}
|
181
181
|
|
182
|
-
if (inst[inst.length - 1] === 'string_only|start' && !pages.hasAnyString
|
182
|
+
if (inst[inst.length - 1] === 'string_only|start' && !pages.hasAnyString&& !Prefs.noRmUnusedTypes) {
|
183
183
|
let j = i;
|
184
184
|
for (; j < wasm.length; j++) {
|
185
185
|
const op = wasm[j];
|
@@ -193,7 +193,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
193
193
|
inst = wasm[i];
|
194
194
|
}
|
195
195
|
|
196
|
-
if (inst[0] === Opcodes.if && typeof inst[2] === 'string' && Prefs.
|
196
|
+
if (inst[0] === Opcodes.if && typeof inst[2] === 'string' && !Prefs.noRmUnusedTypes) {
|
197
197
|
// remove unneeded typeswitch checks
|
198
198
|
|
199
199
|
const type = inst[2].split('|')[1];
|
package/compiler/parse.js
CHANGED
@@ -43,7 +43,7 @@ export default (input, flags) => {
|
|
43
43
|
webcompat: true,
|
44
44
|
|
45
45
|
// babel
|
46
|
-
plugins: types
|
46
|
+
plugins: types ? ['estree', 'typescript'] : ['estree'],
|
47
47
|
|
48
48
|
// multiple
|
49
49
|
sourceType: flags.includes('module') ? 'module' : 'script',
|
package/compiler/pgo.js
CHANGED
@@ -11,7 +11,7 @@ export const setup = () => {
|
|
11
11
|
|
12
12
|
// enable these prefs by default for pgo
|
13
13
|
Prefs.typeswitchUniqueTmp = Prefs.typeswitchUniqueTmp === false ? false : true;
|
14
|
-
Prefs.cyclone = Prefs.cyclone === false ? false : true
|
14
|
+
Prefs.cyclone = Prefs.cyclone === false ? false : true;;
|
15
15
|
};
|
16
16
|
|
17
17
|
export const run = obj => {
|
@@ -131,18 +131,16 @@ export const run = obj => {
|
|
131
131
|
func.localValues = localValues;
|
132
132
|
|
133
133
|
let counts = new Array(10).fill(0);
|
134
|
-
const consistents = localData[i].map(
|
135
|
-
if (
|
136
|
-
if (x.length === 0 || !x.every((y, i) => i < 1 ? true : y === x[i - 1])) return false; // not consistent
|
134
|
+
const consistents = localData[i].map(x => {
|
135
|
+
if (x.length === 0 || !x.every((y, i) => i < 1 ? true : y === x[i - 1])) return false;
|
137
136
|
|
138
137
|
counts[0]++;
|
139
138
|
return x[0];
|
140
139
|
});
|
141
140
|
|
142
141
|
const integerOnlyF64s = localData[i].map((x, j) => {
|
143
|
-
if (j < func.params.length) return false; // param
|
144
142
|
if (localValues[j].type === Valtype.i32) return false; // already i32
|
145
|
-
if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false;
|
143
|
+
if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false;
|
146
144
|
|
147
145
|
counts[1]++;
|
148
146
|
return true;
|
@@ -153,14 +151,14 @@ export const run = obj => {
|
|
153
151
|
|
154
152
|
log += ` ${func.name}: identified ${counts[0]}/${total} locals as consistent${Prefs.verbosePgo ? ':' : ''}\n`;
|
155
153
|
if (Prefs.verbosePgo) {
|
156
|
-
for (let j =
|
154
|
+
for (let j = 0; j < localData[i].length; j++) {
|
157
155
|
log += ` ${consistents[j] !== false ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m: ${new Set(localData[i][j]).size} unique values set\n`;
|
158
156
|
}
|
159
157
|
}
|
160
158
|
|
161
159
|
log += ` ${func.name}: identified ${counts[1]}/${localValues.reduce((acc, x) => acc + (x.type === Valtype.f64 ? 1 : 0), 0)} f64 locals as integer usage only${Prefs.verbosePgo ? ':' : ''}\n`;
|
162
160
|
if (Prefs.verbosePgo) {
|
163
|
-
for (let j =
|
161
|
+
for (let j = 0; j < localData[i].length; j++) {
|
164
162
|
if (localValues[j].type !== Valtype.f64) continue;
|
165
163
|
log += ` ${integerOnlyF64s[j] ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m\n`;
|
166
164
|
}
|
@@ -180,6 +178,7 @@ export const run = obj => {
|
|
180
178
|
for (let i = 0; i < x.integerOnlyF64s.length; i++) {
|
181
179
|
const c = x.integerOnlyF64s[i];
|
182
180
|
if (c === false) continue;
|
181
|
+
if (i < x.params.length) continue;
|
183
182
|
|
184
183
|
targets.push(i);
|
185
184
|
}
|
@@ -192,6 +191,7 @@ export const run = obj => {
|
|
192
191
|
for (let i = 0; i < x.consistents.length; i++) {
|
193
192
|
const c = x.consistents[i];
|
194
193
|
if (c === false) continue;
|
194
|
+
if (i < x.params.length) continue;
|
195
195
|
|
196
196
|
targets.push(i);
|
197
197
|
|
package/compiler/precompile.js
CHANGED
@@ -26,7 +26,7 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
26
26
|
|
27
27
|
const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
|
28
28
|
|
29
|
-
let { funcs, globals, data, exceptions } = porfCompile(source, ['module'
|
29
|
+
let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
|
30
30
|
|
31
31
|
const allocated = new Set();
|
32
32
|
|
@@ -87,8 +87,6 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
87
87
|
};
|
88
88
|
|
89
89
|
const precompile = async () => {
|
90
|
-
if (globalThis._porf_loadParser) await globalThis._porf_loadParser('@babel/parser');
|
91
|
-
|
92
90
|
const dir = join(__dirname, 'builtins');
|
93
91
|
|
94
92
|
let funcs = [], globals = [];
|
package/compiler/prefs.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
const onByDefault = [ 'bytestring', 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused'
|
1
|
+
const onByDefault = [ 'bytestring', 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused' ];
|
2
2
|
|
3
3
|
let cache = {};
|
4
4
|
const obj = new Proxy({}, {
|
package/compiler/wrap.js
CHANGED
@@ -128,7 +128,7 @@ export default (source, flags = [ 'module' ], customImports = {}, print = str =>
|
|
128
128
|
|
129
129
|
if (source.includes?.('export ')) flags.push('module');
|
130
130
|
|
131
|
-
|
131
|
+
fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
132
132
|
|
133
133
|
times.push(performance.now() - t1);
|
134
134
|
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
package/package.json
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "porffor",
|
3
3
|
"description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
|
4
|
-
"version": "0.16.0-
|
4
|
+
"version": "0.16.0-b099006b8",
|
5
5
|
"author": "CanadaHonk",
|
6
6
|
"license": "MIT",
|
7
|
-
"scripts": {
|
7
|
+
"scripts": {
|
8
|
+
"precompile": "node ./compiler/precompile.js"
|
9
|
+
},
|
8
10
|
"dependencies": {
|
9
11
|
"acorn": "^8.11.3",
|
10
12
|
"node-repl-polyfill": "^0.1.1"
|
@@ -26,5 +28,5 @@
|
|
26
28
|
"bugs": {
|
27
29
|
"url": "https://github.com/CanadaHonk/porffor/issues"
|
28
30
|
},
|
29
|
-
"homepage": "https://porffor.
|
31
|
+
"homepage": "https://porffor.goose.icu"
|
30
32
|
}
|
package/runner/index.js
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import compile from '../compiler/wrap.js';
|
2
4
|
import fs from 'node:fs';
|
3
5
|
|
4
6
|
const start = performance.now();
|
@@ -57,15 +59,10 @@ if (process.argv.includes('--help')) {
|
|
57
59
|
}
|
58
60
|
|
59
61
|
let file = process.argv.slice(2).find(x => x[0] !== '-');
|
60
|
-
if (['
|
62
|
+
if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
61
63
|
// remove this arg
|
62
64
|
process.argv.splice(process.argv.indexOf(file), 1);
|
63
65
|
|
64
|
-
if (file === 'precompile') {
|
65
|
-
await import('../compiler/precompile.js');
|
66
|
-
await new Promise(() => {}); // do nothing for the rest of this file
|
67
|
-
}
|
68
|
-
|
69
66
|
if (file === 'profile') {
|
70
67
|
await import('./profile.js');
|
71
68
|
await new Promise(() => {}); // do nothing for the rest of this file
|
@@ -108,8 +105,6 @@ if (!file) {
|
|
108
105
|
|
109
106
|
const source = fs.readFileSync(file, 'utf8');
|
110
107
|
|
111
|
-
const compile = (await import('../compiler/wrap.js')).default;
|
112
|
-
|
113
108
|
let cache = '';
|
114
109
|
const print = str => {
|
115
110
|
/* cache += str;
|
File without changes
|
File without changes
|
@@ -1,41 +0,0 @@
|
|
1
|
-
import { Valtype } from '../wasmSpec.js';
|
2
|
-
import { number } from './embedding.js';
|
3
|
-
import { log } from './log.js';
|
4
|
-
import Prefs from './prefs.js';
|
5
|
-
|
6
|
-
let pages = new Set();
|
7
|
-
export const setup = () => {
|
8
|
-
pages = new Set();
|
9
|
-
};
|
10
|
-
|
11
|
-
const getAllocType = itemType => {
|
12
|
-
switch (itemType) {
|
13
|
-
case 'i8': return 'bytestring';
|
14
|
-
case 'i16': return 'string';
|
15
|
-
|
16
|
-
default: return 'array';
|
17
|
-
}
|
18
|
-
};
|
19
|
-
|
20
|
-
export const alloc = ({ scope }, name, itemType) => {
|
21
|
-
const reason = Prefs.scopedPageNames ? `${getAllocType(itemType)}: ${scope.name}/${name}` : `${getAllocType(itemType)}: ${name}`;
|
22
|
-
|
23
|
-
if (pages.has(reason)) return number(pages.get(reason).ind, Valtype.i32);
|
24
|
-
|
25
|
-
if (reason.startsWith('array:')) pages.hasArray = true;
|
26
|
-
if (reason.startsWith('string:')) pages.hasString = true;
|
27
|
-
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
28
|
-
if (reason.includes('string:')) pages.hasAnyString = true;
|
29
|
-
|
30
|
-
const ind = pages.size;
|
31
|
-
pages.set(reason, { ind, type });
|
32
|
-
|
33
|
-
scope.pages ??= new Map();
|
34
|
-
scope.pages.set(reason, { ind, type });
|
35
|
-
|
36
|
-
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
37
|
-
|
38
|
-
return number(ind, Valtype.i32);
|
39
|
-
};
|
40
|
-
|
41
|
-
export const getStaticPages = () => pages;
|