porffor 0.16.0-0e931a1fa → 0.16.0-1a6e16aae
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/allocators/chunk.js +0 -0
- package/compiler/allocators/solo.js +0 -0
- package/compiler/allocators/static.js +41 -0
- package/compiler/codegen.js +49 -165
- package/compiler/cyclone.js +11 -11
- package/compiler/havoc.js +1 -1
- package/compiler/index.js +16 -3
- package/compiler/parse.js +1 -1
- package/compiler/pgo.js +11 -10
- package/compiler/precompile.js +3 -1
- package/compiler/prefs.js +1 -1
- package/compiler/wrap.js +1 -1
- package/package.json +3 -5
- package/runner/index.js +8 -3
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 `./porf precompile` 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
|
|
File without changes
|
File without changes
|
@@ -0,0 +1,41 @@
|
|
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;
|
package/compiler/codegen.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Blocktype, Opcodes, Valtype,
|
1
|
+
import { Blocktype, Opcodes, Valtype, 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,12 +181,6 @@ 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
|
-
|
190
184
|
let inst = Opcodes[asm[0].replace('.', '_')];
|
191
185
|
if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
192
186
|
|
@@ -433,57 +427,6 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
433
427
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
434
428
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
435
429
|
|
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
|
-
|
487
430
|
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
488
431
|
|
489
432
|
// alloc/assign array
|
@@ -2115,7 +2058,6 @@ const brTable = (input, bc, returns) => {
|
|
2115
2058
|
}
|
2116
2059
|
|
2117
2060
|
for (let i = 0; i < count; i++) {
|
2118
|
-
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2119
2061
|
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2120
2062
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2121
2063
|
}
|
@@ -2149,10 +2091,8 @@ const brTable = (input, bc, returns) => {
|
|
2149
2091
|
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
2150
2092
|
);
|
2151
2093
|
|
2152
|
-
//
|
2153
|
-
// (
|
2154
|
-
// dm me and if you are correct and the first person
|
2155
|
-
// I will somehow shout you out or something
|
2094
|
+
// sort the wrong way and then reverse
|
2095
|
+
// so strings ('default') are at the start before any numbers
|
2156
2096
|
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
2157
2097
|
|
2158
2098
|
br = count - 1;
|
@@ -2178,7 +2118,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2178
2118
|
return bc[known] ?? bc.default;
|
2179
2119
|
}
|
2180
2120
|
|
2181
|
-
if (Prefs.
|
2121
|
+
if (Prefs.typeswitchBrtable)
|
2182
2122
|
return brTable(type, bc, returns);
|
2183
2123
|
|
2184
2124
|
const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
|
@@ -2399,19 +2339,12 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2399
2339
|
|
2400
2340
|
// hack: .length setter
|
2401
2341
|
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
|
-
|
2407
2342
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2408
2343
|
const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2409
2344
|
|
2410
2345
|
return [
|
2411
|
-
...(
|
2412
|
-
|
2413
|
-
Opcodes.i32_to_u
|
2414
|
-
]),
|
2346
|
+
...generate(scope, decl.left.object),
|
2347
|
+
Opcodes.i32_to_u,
|
2415
2348
|
...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2416
2349
|
|
2417
2350
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
@@ -2422,7 +2355,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2422
2355
|
[ Opcodes.local_tee, newValueTmp ],
|
2423
2356
|
|
2424
2357
|
Opcodes.i32_to_u,
|
2425
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1,
|
2358
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
2426
2359
|
|
2427
2360
|
[ Opcodes.local_get, newValueTmp ]
|
2428
2361
|
];
|
@@ -2430,21 +2363,14 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2430
2363
|
|
2431
2364
|
// arr[i]
|
2432
2365
|
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
|
-
|
2438
2366
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2439
2367
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2440
2368
|
|
2441
2369
|
return [
|
2442
2370
|
...typeSwitch(scope, getNodeType(scope, decl.left.object), {
|
2443
2371
|
[TYPES.array]: [
|
2444
|
-
...(
|
2445
|
-
|
2446
|
-
Opcodes.i32_to_u
|
2447
|
-
]),
|
2372
|
+
...generate(scope, decl.left.object),
|
2373
|
+
Opcodes.i32_to_u,
|
2448
2374
|
|
2449
2375
|
// get index as valtype
|
2450
2376
|
...generate(scope, decl.left.property),
|
@@ -2453,39 +2379,22 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2453
2379
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2454
2380
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2455
2381
|
[ Opcodes.i32_mul ],
|
2456
|
-
|
2382
|
+
[ Opcodes.i32_add ],
|
2457
2383
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2458
2384
|
|
2459
2385
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2460
2386
|
[ Opcodes.local_get, pointerTmp ],
|
2461
|
-
[ Opcodes.load, 0,
|
2387
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ]
|
2462
2388
|
], generate(scope, decl.right), [
|
2463
2389
|
[ Opcodes.local_get, pointerTmp ],
|
2464
|
-
[ Opcodes.i32_load8_u, 0,
|
2390
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
2465
2391
|
], getNodeType(scope, decl.right), false, name, true)),
|
2466
2392
|
[ Opcodes.local_tee, newValueTmp ],
|
2467
2393
|
|
2468
|
-
[ Opcodes.store, 0,
|
2394
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ]
|
2469
2395
|
],
|
2470
2396
|
|
2471
2397
|
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
|
-
// ]
|
2489
2398
|
}, Blocktype.void),
|
2490
2399
|
|
2491
2400
|
[ Opcodes.local_get, newValueTmp ]
|
@@ -3207,16 +3116,6 @@ const allocPage = (scope, reason, type) => {
|
|
3207
3116
|
return ind;
|
3208
3117
|
};
|
3209
3118
|
|
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
|
-
|
3220
3119
|
const itemTypeToValtype = {
|
3221
3120
|
i32: 'i32',
|
3222
3121
|
i64: 'i64',
|
@@ -3270,7 +3169,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3270
3169
|
firstAssign = true;
|
3271
3170
|
|
3272
3171
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3273
|
-
const uniqueName = name === '$undeclared' ? name +
|
3172
|
+
const uniqueName = name === '$undeclared' ? name + randId() : name;
|
3274
3173
|
|
3275
3174
|
let page;
|
3276
3175
|
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
@@ -3291,7 +3190,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3291
3190
|
const valtype = itemTypeToValtype[itemType];
|
3292
3191
|
const length = elements.length;
|
3293
3192
|
|
3294
|
-
if (
|
3193
|
+
if (Prefs.data && firstAssign && useRawElements) {
|
3295
3194
|
// if length is 0 memory/data will just be 0000... anyway
|
3296
3195
|
if (length !== 0) {
|
3297
3196
|
let bytes = compileBytes(length, 'i32');
|
@@ -3359,7 +3258,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3359
3258
|
return [ out, pointer ];
|
3360
3259
|
};
|
3361
3260
|
|
3362
|
-
const storeArray = (scope, array, index, element
|
3261
|
+
const storeArray = (scope, array, index, element) => {
|
3363
3262
|
if (!Array.isArray(element)) element = generate(scope, element);
|
3364
3263
|
if (typeof index === 'number') index = number(index);
|
3365
3264
|
|
@@ -3371,26 +3270,25 @@ const storeArray = (scope, array, index, element, aotPointer = null) => {
|
|
3371
3270
|
Opcodes.i32_to_u,
|
3372
3271
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3373
3272
|
[ Opcodes.i32_mul ],
|
3374
|
-
|
3375
|
-
|
3376
|
-
|
3377
|
-
|
3378
|
-
]),
|
3273
|
+
|
3274
|
+
...array,
|
3275
|
+
Opcodes.i32_to_u,
|
3276
|
+
[ Opcodes.i32_add ],
|
3379
3277
|
[ Opcodes.local_set, offset ],
|
3380
3278
|
|
3381
3279
|
// store value
|
3382
3280
|
[ Opcodes.local_get, offset ],
|
3383
3281
|
...generate(scope, element),
|
3384
|
-
[ Opcodes.store, 0,
|
3282
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
3385
3283
|
|
3386
3284
|
// store type
|
3387
3285
|
[ Opcodes.local_get, offset ],
|
3388
3286
|
...getNodeType(scope, element),
|
3389
|
-
[ Opcodes.i32_store8, 0,
|
3287
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
3390
3288
|
];
|
3391
3289
|
};
|
3392
3290
|
|
3393
|
-
const loadArray = (scope, array, index
|
3291
|
+
const loadArray = (scope, array, index) => {
|
3394
3292
|
if (typeof index === 'number') index = number(index);
|
3395
3293
|
|
3396
3294
|
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
@@ -3401,20 +3299,19 @@ const loadArray = (scope, array, index, aotPointer = null) => {
|
|
3401
3299
|
Opcodes.i32_to_u,
|
3402
3300
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3403
3301
|
[ Opcodes.i32_mul ],
|
3404
|
-
|
3405
|
-
|
3406
|
-
|
3407
|
-
|
3408
|
-
]),
|
3302
|
+
|
3303
|
+
...array,
|
3304
|
+
Opcodes.i32_to_u,
|
3305
|
+
[ Opcodes.i32_add ],
|
3409
3306
|
[ Opcodes.local_set, offset ],
|
3410
3307
|
|
3411
3308
|
// load value
|
3412
3309
|
[ Opcodes.local_get, offset ],
|
3413
|
-
[ Opcodes.load, 0,
|
3310
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
3414
3311
|
|
3415
3312
|
// load type
|
3416
3313
|
[ Opcodes.local_get, offset ],
|
3417
|
-
[ Opcodes.i32_load8_u, 0,
|
3314
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
3418
3315
|
];
|
3419
3316
|
};
|
3420
3317
|
|
@@ -3465,9 +3362,6 @@ const withType = (scope, wasm, type) => [
|
|
3465
3362
|
|
3466
3363
|
const generateMember = (scope, decl, _global, _name) => {
|
3467
3364
|
const name = decl.object.name;
|
3468
|
-
const pointer = scope.arrays?.get(name);
|
3469
|
-
|
3470
|
-
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3471
3365
|
|
3472
3366
|
// hack: .name
|
3473
3367
|
if (decl.property.name === 'name') {
|
@@ -3508,12 +3402,10 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3508
3402
|
if (Prefs.fastLength) {
|
3509
3403
|
// presume valid length object
|
3510
3404
|
return [
|
3511
|
-
...(
|
3512
|
-
|
3513
|
-
Opcodes.i32_to_u
|
3514
|
-
]),
|
3405
|
+
...generate(scope, decl.object),
|
3406
|
+
Opcodes.i32_to_u,
|
3515
3407
|
|
3516
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1,
|
3408
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3517
3409
|
Opcodes.i32_from_u
|
3518
3410
|
];
|
3519
3411
|
}
|
@@ -3522,12 +3414,10 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3522
3414
|
const known = knownType(scope, type);
|
3523
3415
|
if (known != null) {
|
3524
3416
|
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3525
|
-
...(
|
3526
|
-
|
3527
|
-
Opcodes.i32_to_u
|
3528
|
-
]),
|
3417
|
+
...generate(scope, decl.object),
|
3418
|
+
Opcodes.i32_to_u,
|
3529
3419
|
|
3530
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1,
|
3420
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3531
3421
|
Opcodes.i32_from_u
|
3532
3422
|
];
|
3533
3423
|
|
@@ -3537,12 +3427,10 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3537
3427
|
return [
|
3538
3428
|
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3539
3429
|
[ Opcodes.if, valtypeBinary ],
|
3540
|
-
...(
|
3541
|
-
|
3542
|
-
Opcodes.i32_to_u
|
3543
|
-
]),
|
3430
|
+
...generate(scope, decl.object),
|
3431
|
+
Opcodes.i32_to_u,
|
3544
3432
|
|
3545
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1,
|
3433
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3546
3434
|
Opcodes.i32_from_u,
|
3547
3435
|
|
3548
3436
|
...setLastType(scope, TYPES.number),
|
@@ -3594,7 +3482,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3594
3482
|
|
3595
3483
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3596
3484
|
[TYPES.array]: [
|
3597
|
-
...loadArray(scope, object, property
|
3485
|
+
...loadArray(scope, object, property),
|
3598
3486
|
...setLastType(scope)
|
3599
3487
|
],
|
3600
3488
|
|
@@ -3611,14 +3499,12 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3611
3499
|
...number(ValtypeSize.i16, Valtype.i32),
|
3612
3500
|
[ Opcodes.i32_mul ],
|
3613
3501
|
|
3614
|
-
...
|
3615
|
-
|
3616
|
-
|
3617
|
-
[ Opcodes.i32_add ]
|
3618
|
-
]),
|
3502
|
+
...object,
|
3503
|
+
Opcodes.i32_to_u,
|
3504
|
+
[ Opcodes.i32_add ],
|
3619
3505
|
|
3620
3506
|
// load current string ind {arg}
|
3621
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
3507
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
3622
3508
|
|
3623
3509
|
// store to new string ind 0
|
3624
3510
|
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
@@ -3637,14 +3523,12 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3637
3523
|
...property,
|
3638
3524
|
Opcodes.i32_to_u,
|
3639
3525
|
|
3640
|
-
...
|
3641
|
-
|
3642
|
-
|
3643
|
-
[ Opcodes.i32_add ]
|
3644
|
-
]),
|
3526
|
+
...object,
|
3527
|
+
Opcodes.i32_to_u,
|
3528
|
+
[ Opcodes.i32_add ],
|
3645
3529
|
|
3646
3530
|
// load current string ind {arg}
|
3647
|
-
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(
|
3531
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
3648
3532
|
|
3649
3533
|
// store to new string ind 0
|
3650
3534
|
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
@@ -3658,7 +3542,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3658
3542
|
});
|
3659
3543
|
};
|
3660
3544
|
|
3661
|
-
const randId = () => Math.random().toString(16).slice(
|
3545
|
+
const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
|
3662
3546
|
|
3663
3547
|
const objectHack = node => {
|
3664
3548
|
if (!node) return node;
|
@@ -3710,7 +3594,7 @@ const generateFunc = (scope, decl) => {
|
|
3710
3594
|
if (decl.async) return todo(scope, 'async functions are not supported');
|
3711
3595
|
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
3712
3596
|
|
3713
|
-
const name = decl.id ? decl.id.name : `
|
3597
|
+
const name = decl.id ? decl.id.name : `anonymous${randId()}`;
|
3714
3598
|
const params = decl.params ?? [];
|
3715
3599
|
|
3716
3600
|
// 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.i32);
|
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.i32);
|
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.i32);
|
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.i32);
|
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.i32);
|
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.i32);
|
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 = a
|
407
|
+
const v = Math.max(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
|
-
(opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
|
472
|
-
(opcode >= 0x61 && opcode <= 0x66) // main f64 eq op
|
470
|
+
if (i >= 2 &&
|
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/havoc.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
// havoc: wasm rewrite library (it wreaks havoc upon wasm bytecode)
|
1
|
+
// havoc: wasm rewrite library (it wreaks havoc upon wasm bytecode hence "havoc")
|
2
2
|
import { Opcodes, Valtype } from './wasmSpec.js';
|
3
3
|
|
4
4
|
export const localsToConsts = (func, targets, consts, { localKeys }) => {
|
package/compiler/index.js
CHANGED
@@ -158,10 +158,23 @@ export default (code, flags) => {
|
|
158
158
|
|
159
159
|
if (process.version) {
|
160
160
|
if (Prefs.native) {
|
161
|
-
const
|
162
|
-
|
161
|
+
const cleanup = () => {
|
162
|
+
try {
|
163
|
+
fs.unlinkSync(outFile);
|
164
|
+
} catch {}
|
165
|
+
};
|
166
|
+
|
167
|
+
process.on('exit', cleanup);
|
168
|
+
process.on('beforeExit', cleanup);
|
169
|
+
process.on('SIGINT', () => {
|
170
|
+
cleanup();
|
171
|
+
process.exit();
|
172
|
+
});
|
163
173
|
|
164
|
-
|
174
|
+
const runArgs = process.argv.slice(2).filter(x => !x.startsWith('-'));
|
175
|
+
try {
|
176
|
+
execSync([ outFile, ...runArgs.slice(1) ].join(' '), { stdio: 'inherit' });
|
177
|
+
} catch {}
|
165
178
|
}
|
166
179
|
|
167
180
|
process.exit();
|
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 ? ['estree', 'typescript'] : ['estree'],
|
46
|
+
plugins: types || flags.includes('typed') ? ['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 => {
|
@@ -87,9 +87,10 @@ export const run = obj => {
|
|
87
87
|
localData[activeFunc][i].push(n);
|
88
88
|
},
|
89
89
|
w: (ind, outPtr) => { // readArgv
|
90
|
-
const
|
90
|
+
const pgoInd = process.argv.indexOf('--pgo');
|
91
|
+
const args = process.argv.slice(pgoInd).filter(x => !x.startsWith('-'));
|
91
92
|
const str = args[ind - 1];
|
92
|
-
if (!str) {
|
93
|
+
if (pgoInd === -1 || !str) {
|
93
94
|
if (Prefs.pgoLog) console.log('\nPGO warning: script was expecting arguments, please specify args to use for PGO after --pgo arg');
|
94
95
|
return -1;
|
95
96
|
}
|
@@ -130,16 +131,18 @@ export const run = obj => {
|
|
130
131
|
func.localValues = localValues;
|
131
132
|
|
132
133
|
let counts = new Array(10).fill(0);
|
133
|
-
const consistents = localData[i].map(x => {
|
134
|
-
if (
|
134
|
+
const consistents = localData[i].map((x, j) => {
|
135
|
+
if (j < func.params.length) return false; // param
|
136
|
+
if (x.length === 0 || !x.every((y, i) => i < 1 ? true : y === x[i - 1])) return false; // not consistent
|
135
137
|
|
136
138
|
counts[0]++;
|
137
139
|
return x[0];
|
138
140
|
});
|
139
141
|
|
140
142
|
const integerOnlyF64s = localData[i].map((x, j) => {
|
143
|
+
if (j < func.params.length) return false; // param
|
141
144
|
if (localValues[j].type === Valtype.i32) return false; // already i32
|
142
|
-
if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false;
|
145
|
+
if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false; // not all integer values
|
143
146
|
|
144
147
|
counts[1]++;
|
145
148
|
return true;
|
@@ -150,14 +153,14 @@ export const run = obj => {
|
|
150
153
|
|
151
154
|
log += ` ${func.name}: identified ${counts[0]}/${total} locals as consistent${Prefs.verbosePgo ? ':' : ''}\n`;
|
152
155
|
if (Prefs.verbosePgo) {
|
153
|
-
for (let j =
|
156
|
+
for (let j = func.params.length; j < localData[i].length; j++) {
|
154
157
|
log += ` ${consistents[j] !== false ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m: ${new Set(localData[i][j]).size} unique values set\n`;
|
155
158
|
}
|
156
159
|
}
|
157
160
|
|
158
161
|
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`;
|
159
162
|
if (Prefs.verbosePgo) {
|
160
|
-
for (let j =
|
163
|
+
for (let j = func.params.length; j < localData[i].length; j++) {
|
161
164
|
if (localValues[j].type !== Valtype.f64) continue;
|
162
165
|
log += ` ${integerOnlyF64s[j] ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m\n`;
|
163
166
|
}
|
@@ -177,7 +180,6 @@ export const run = obj => {
|
|
177
180
|
for (let i = 0; i < x.integerOnlyF64s.length; i++) {
|
178
181
|
const c = x.integerOnlyF64s[i];
|
179
182
|
if (c === false) continue;
|
180
|
-
if (i < x.params.length) continue;
|
181
183
|
|
182
184
|
targets.push(i);
|
183
185
|
}
|
@@ -190,7 +192,6 @@ export const run = obj => {
|
|
190
192
|
for (let i = 0; i < x.consistents.length; i++) {
|
191
193
|
const c = x.consistents[i];
|
192
194
|
if (c === false) continue;
|
193
|
-
if (i < x.params.length) continue;
|
194
195
|
|
195
196
|
targets.push(i);
|
196
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', 'typed']);
|
30
30
|
|
31
31
|
const allocated = new Set();
|
32
32
|
|
@@ -87,6 +87,8 @@ 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
|
+
|
90
92
|
const dir = join(__dirname, 'builtins');
|
91
93
|
|
92
94
|
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', 'data', 'rmUnusedTypes' ];
|
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
|
-
fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
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,12 +1,10 @@
|
|
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-1a6e16aae",
|
5
5
|
"author": "CanadaHonk",
|
6
6
|
"license": "MIT",
|
7
|
-
"scripts": {
|
8
|
-
"precompile": "node ./compiler/precompile.js"
|
9
|
-
},
|
7
|
+
"scripts": {},
|
10
8
|
"dependencies": {
|
11
9
|
"acorn": "^8.11.3",
|
12
10
|
"node-repl-polyfill": "^0.1.1"
|
@@ -28,5 +26,5 @@
|
|
28
26
|
"bugs": {
|
29
27
|
"url": "https://github.com/CanadaHonk/porffor/issues"
|
30
28
|
},
|
31
|
-
"homepage": "https://porffor.
|
29
|
+
"homepage": "https://porffor.dev"
|
32
30
|
}
|
package/runner/index.js
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
-
|
3
|
-
import compile from '../compiler/wrap.js';
|
4
2
|
import fs from 'node:fs';
|
5
3
|
|
6
4
|
const start = performance.now();
|
@@ -59,10 +57,15 @@ if (process.argv.includes('--help')) {
|
|
59
57
|
}
|
60
58
|
|
61
59
|
let file = process.argv.slice(2).find(x => x[0] !== '-');
|
62
|
-
if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
60
|
+
if (['precompile', 'run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
63
61
|
// remove this arg
|
64
62
|
process.argv.splice(process.argv.indexOf(file), 1);
|
65
63
|
|
64
|
+
if (file === 'precompile') {
|
65
|
+
await import('../compiler/precompile.js');
|
66
|
+
await new Promise(() => {}); // do nothing for the rest of this file
|
67
|
+
}
|
68
|
+
|
66
69
|
if (file === 'profile') {
|
67
70
|
await import('./profile.js');
|
68
71
|
await new Promise(() => {}); // do nothing for the rest of this file
|
@@ -105,6 +108,8 @@ if (!file) {
|
|
105
108
|
|
106
109
|
const source = fs.readFileSync(file, 'utf8');
|
107
110
|
|
111
|
+
const compile = (await import('../compiler/wrap.js')).default;
|
112
|
+
|
108
113
|
let cache = '';
|
109
114
|
const print = str => {
|
110
115
|
/* cache += str;
|