porffor 0.55.20 → 0.55.21
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/compiler/builtins/bigint.ts +4 -8
- package/compiler/codegen.js +45 -127
- package/package.json +1 -1
- package/runner/index.js +1 -1
@@ -5,11 +5,11 @@ export const __Porffor_bigint_fromDigits = (negative: boolean, digits: i32[]): b
|
|
5
5
|
const len: i32 = digits.length;
|
6
6
|
if (len > 16383) throw new RangeError('Maximum BigInt size exceeded'); // (65536 - 4) / 4
|
7
7
|
|
8
|
-
//
|
8
|
+
// use digits pointer as bigint pointer, as only used here
|
9
9
|
let ptr: i32 = Porffor.wasm`local.get ${digits}`;
|
10
10
|
|
11
|
-
Porffor.wasm.i32.store8(ptr, negative ? 1 : 0, 0, 0);
|
12
|
-
Porffor.wasm.i32.store16(ptr, len, 0, 2);
|
11
|
+
Porffor.wasm.i32.store8(ptr, negative ? 1 : 0, 0, 0); // sign
|
12
|
+
Porffor.wasm.i32.store16(ptr, len, 0, 2); // digit count
|
13
13
|
|
14
14
|
let allZero: boolean = true;
|
15
15
|
for (let i: i32 = 0; i < len; i++) {
|
@@ -84,14 +84,10 @@ export const __Porffor_bigint_fromString = (n: string|bytestring): bigint => {
|
|
84
84
|
offset = 1;
|
85
85
|
}
|
86
86
|
|
87
|
-
// n ->
|
88
|
-
// 4294967294 -> [ 4294967294 ]
|
87
|
+
// n -> base 2^32 digits (most to least significant)
|
89
88
|
// 4294967295 -> [ 4294967295 ]
|
90
89
|
// 4294967296 -> [ 1, 0 ]
|
91
90
|
// 4294967297 -> [ 1, 1 ]
|
92
|
-
// 9007199254740992 -> [ 2097152, 0 ]
|
93
|
-
// 9007199254740993 -> [ 2097152, 1 ]
|
94
|
-
// 9007199254740994 -> [ 2097152, 2 ]
|
95
91
|
|
96
92
|
const BASE: i32 = 0x100000000; // 2^32
|
97
93
|
const digits: i32[] = Porffor.allocate(); // todo: free later
|
package/compiler/codegen.js
CHANGED
@@ -462,7 +462,7 @@ const lookup = (scope, name, failEarly = false) => {
|
|
462
462
|
}
|
463
463
|
|
464
464
|
if (local?.idx === undefined) {
|
465
|
-
if (name === 'arguments' &&
|
465
|
+
if (name === 'arguments' && !scope.arrow) {
|
466
466
|
// todo: not compliant
|
467
467
|
let len = countLength(scope);
|
468
468
|
const names = new Array(len);
|
@@ -1386,29 +1386,6 @@ const generateLogicExp = (scope, decl) => {
|
|
1386
1386
|
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
1387
1387
|
};
|
1388
1388
|
|
1389
|
-
// potential future ideas for nan boxing (unused):
|
1390
|
-
// T = JS type, V = value/pointer
|
1391
|
-
// 0bTTT
|
1392
|
-
// qNAN: 0 11111111111 1000000000000000000000000000000000000000000000000001
|
1393
|
-
// 50 bits usable: 0 11111111111 11??????????????????????????????????????????????????
|
1394
|
-
// js type: 4 bits
|
1395
|
-
// internal type: ? bits
|
1396
|
-
// pointer: 32 bits
|
1397
|
-
// https://piotrduperas.com/posts/nan-boxing
|
1398
|
-
// 0x7ffc000000000000
|
1399
|
-
// budget: 50 bits
|
1400
|
-
// js type: 4 bits
|
1401
|
-
// internal type: ? bits
|
1402
|
-
// pointer: 32 bits
|
1403
|
-
// generic
|
1404
|
-
// 1 23 4 5
|
1405
|
-
// 0 11111111111 11TTTTIIII??????????PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
|
1406
|
-
// 1: regular iEEE 754 double NaN
|
1407
|
-
// 2: extra 1 bit to identify NaN box
|
1408
|
-
// 3: js type
|
1409
|
-
// 4: internal type
|
1410
|
-
// 5: pointer
|
1411
|
-
|
1412
1389
|
const isExistingProtoFunc = name => {
|
1413
1390
|
if (name.startsWith('__Array_prototype')) return !!prototypeFuncs[TYPES.array][name.slice(18)];
|
1414
1391
|
if (name.startsWith('__String_prototype_')) return !!prototypeFuncs[TYPES.string][name.slice(19)];
|
@@ -1470,7 +1447,7 @@ const getType = (scope, name, failEarly = false) => {
|
|
1470
1447
|
global = true;
|
1471
1448
|
}
|
1472
1449
|
|
1473
|
-
if (global !== false && name === 'arguments' &&
|
1450
|
+
if (global !== false && name === 'arguments' && !scope.arrow) {
|
1474
1451
|
return [ number(TYPES.array, Valtype.i32) ];
|
1475
1452
|
}
|
1476
1453
|
|
@@ -2750,18 +2727,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2750
2727
|
}
|
2751
2728
|
|
2752
2729
|
out.push([ Opcodes.call, idx ]);
|
2753
|
-
|
2754
|
-
if (!typedReturns) {
|
2755
|
-
// let type;
|
2756
|
-
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
2757
|
-
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
2758
|
-
// if (importedFuncs[name] && importedFuncs[]) type =
|
2759
|
-
|
2760
|
-
// if (type) out.push(
|
2761
|
-
// number(type, Valtype.i32),
|
2762
|
-
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
2763
|
-
// );
|
2764
|
-
} else out.push(...setLastType(scope));
|
2730
|
+
if (typedReturns) out.push(...setLastType(scope));
|
2765
2731
|
|
2766
2732
|
if (
|
2767
2733
|
func?.returns?.length === 0 ||
|
@@ -3571,9 +3537,8 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
|
3571
3537
|
const generateVar = (scope, decl) => {
|
3572
3538
|
let out = [];
|
3573
3539
|
|
3574
|
-
const topLevel = scope.name === '#main';
|
3575
|
-
|
3576
3540
|
// global variable if in top scope (main) or if internally wanted
|
3541
|
+
const topLevel = scope.name === '#main';
|
3577
3542
|
const global = decl._global ?? (topLevel || decl._bare);
|
3578
3543
|
|
3579
3544
|
for (const x of decl.declarations) {
|
@@ -3637,6 +3602,15 @@ const memberTmpNames = scope => {
|
|
3637
3602
|
// COCTC: cross-object compile-time cache
|
3638
3603
|
let coctc = new Map();
|
3639
3604
|
const coctcOffset = prop => {
|
3605
|
+
if (typeof prop === 'object') {
|
3606
|
+
if (
|
3607
|
+
prop.computed || prop.optional ||
|
3608
|
+
['prototype', 'size', 'description', 'byteLength', 'byteOffset', 'buffer', 'detached', 'resizable', 'growable', 'maxByteLength', 'length', '__proto__'].includes(prop.property.name)
|
3609
|
+
) return 0;
|
3610
|
+
|
3611
|
+
prop = prop.property.name;
|
3612
|
+
}
|
3613
|
+
|
3640
3614
|
let offset = coctc.get(prop);
|
3641
3615
|
if (offset == null) {
|
3642
3616
|
offset = (coctc.lastOffset ?? 60000) - 9;
|
@@ -3768,7 +3742,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
3768
3742
|
// todo/perf: use i32 object (and prop?) locals
|
3769
3743
|
const { objectTmp, propertyTmp, objectGet, propertyGet } = memberTmpNames(scope);
|
3770
3744
|
|
3771
|
-
const useCoctc = Prefs.coctc &&
|
3745
|
+
const useCoctc = Prefs.coctc && coctcOffset(decl.left) > 0;
|
3772
3746
|
if (useCoctc) valueUnused = false;
|
3773
3747
|
|
3774
3748
|
// opt: do not mark prototype funcs as referenced to optimize this in them
|
@@ -4214,7 +4188,7 @@ const generateUnary = (scope, decl) => {
|
|
4214
4188
|
const property = getProperty(decl.argument);
|
4215
4189
|
if (property.value === 'length' || property.value === 'name') scope.noFastFuncMembers = true;
|
4216
4190
|
|
4217
|
-
const useCoctc = Prefs.coctc &&
|
4191
|
+
const useCoctc = Prefs.coctc && coctcOffset(decl.argument) > 0;
|
4218
4192
|
const objectTmp = useCoctc && localTmp(scope, '#coctc_object', Valtype.i32);
|
4219
4193
|
|
4220
4194
|
const out = [
|
@@ -5410,7 +5384,7 @@ const makeData = (scope, elements, page = null, itemType = 'i8') => {
|
|
5410
5384
|
|
5411
5385
|
const length = elements.length;
|
5412
5386
|
|
5413
|
-
// if length is 0 memory/data will just be 0000... anyway
|
5387
|
+
// if length is 0, memory/data will just be 0000... anyway
|
5414
5388
|
if (length === 0) return false;
|
5415
5389
|
|
5416
5390
|
let bytes = compileBytes(length, 'i32');
|
@@ -5424,7 +5398,6 @@ const makeData = (scope, elements, page = null, itemType = 'i8') => {
|
|
5424
5398
|
}
|
5425
5399
|
|
5426
5400
|
const obj = { bytes, page };
|
5427
|
-
|
5428
5401
|
const idx = data.push(obj) - 1;
|
5429
5402
|
|
5430
5403
|
scope.data ??= [];
|
@@ -5448,63 +5421,6 @@ const printStaticStr = str => {
|
|
5448
5421
|
return out;
|
5449
5422
|
};
|
5450
5423
|
|
5451
|
-
const storeArray = (scope, array, index, element) => {
|
5452
|
-
if (!Array.isArray(element)) element = generate(scope, element);
|
5453
|
-
if (typeof index === 'number') index = [ number(index) ];
|
5454
|
-
|
5455
|
-
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
5456
|
-
|
5457
|
-
return [
|
5458
|
-
// calculate offset
|
5459
|
-
...index,
|
5460
|
-
Opcodes.i32_to_u,
|
5461
|
-
number(ValtypeSize[valtype] + 1, Valtype.i32),
|
5462
|
-
[ Opcodes.i32_mul ],
|
5463
|
-
|
5464
|
-
...array,
|
5465
|
-
Opcodes.i32_to_u,
|
5466
|
-
[ Opcodes.i32_add ],
|
5467
|
-
[ Opcodes.local_set, offset ],
|
5468
|
-
|
5469
|
-
// store value
|
5470
|
-
[ Opcodes.local_get, offset ],
|
5471
|
-
...generate(scope, element),
|
5472
|
-
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
5473
|
-
|
5474
|
-
// store type
|
5475
|
-
[ Opcodes.local_get, offset ],
|
5476
|
-
...getNodeType(scope, element),
|
5477
|
-
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
5478
|
-
];
|
5479
|
-
};
|
5480
|
-
|
5481
|
-
const loadArray = (scope, array, index) => {
|
5482
|
-
if (typeof index === 'number') index = [ number(index) ];
|
5483
|
-
|
5484
|
-
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
5485
|
-
|
5486
|
-
return [
|
5487
|
-
// calculate offset
|
5488
|
-
...index,
|
5489
|
-
Opcodes.i32_to_u,
|
5490
|
-
number(ValtypeSize[valtype] + 1, Valtype.i32),
|
5491
|
-
[ Opcodes.i32_mul ],
|
5492
|
-
|
5493
|
-
...array,
|
5494
|
-
Opcodes.i32_to_u,
|
5495
|
-
[ Opcodes.i32_add ],
|
5496
|
-
[ Opcodes.local_set, offset ],
|
5497
|
-
|
5498
|
-
// load value
|
5499
|
-
[ Opcodes.local_get, offset ],
|
5500
|
-
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
5501
|
-
|
5502
|
-
// load type
|
5503
|
-
[ Opcodes.local_get, offset ],
|
5504
|
-
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
5505
|
-
];
|
5506
|
-
};
|
5507
|
-
|
5508
5424
|
const byteStringable = str => {
|
5509
5425
|
for (let i = 0; i < str.length; i++) {
|
5510
5426
|
if (str.charCodeAt(i) > 0xFF) return false;
|
@@ -5943,14 +5859,27 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
5943
5859
|
}
|
5944
5860
|
}
|
5945
5861
|
|
5946
|
-
const useCoctc = Prefs.coctc &&
|
5862
|
+
const useCoctc = Prefs.coctc && coctcOffset(decl) > 0;
|
5947
5863
|
const coctcObjTmp = useCoctc && localTmp(scope, '#coctc_obj' + uniqId(), Valtype.i32);
|
5948
5864
|
|
5949
5865
|
const out = typeSwitch(scope, getNodeType(scope, object), {
|
5950
5866
|
...(decl.computed ? {
|
5951
5867
|
[TYPES.array]: () => [
|
5952
|
-
|
5953
|
-
|
5868
|
+
propertyGet,
|
5869
|
+
Opcodes.i32_to_u,
|
5870
|
+
number(ValtypeSize[valtype] + 1, Valtype.i32),
|
5871
|
+
[ Opcodes.i32_mul ],
|
5872
|
+
|
5873
|
+
objectGet,
|
5874
|
+
Opcodes.i32_to_u,
|
5875
|
+
[ Opcodes.i32_add ],
|
5876
|
+
[ Opcodes.local_tee, localTmp(scope, '#loadArray_offset', Valtype.i32) ],
|
5877
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
5878
|
+
|
5879
|
+
...setLastType(scope, [
|
5880
|
+
[ Opcodes.local_get, localTmp(scope, '#loadArray_offset', Valtype.i32) ],
|
5881
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
|
5882
|
+
])
|
5954
5883
|
],
|
5955
5884
|
|
5956
5885
|
[TYPES.string]: () => [
|
@@ -7072,28 +7001,24 @@ export default program => {
|
|
7072
7001
|
const getObjectName = x => x.startsWith('__') && x.slice(2, x.indexOf('_', 2));
|
7073
7002
|
objectHackers = ['assert', 'compareArray', 'Test262Error', ...new Set(Object.keys(builtinFuncs).map(getObjectName).concat(Object.keys(builtinVars).map(getObjectName)).filter(x => x))];
|
7074
7003
|
|
7075
|
-
|
7076
|
-
|
7077
|
-
|
7078
|
-
|
7079
|
-
|
7080
|
-
|
7081
|
-
|
7082
|
-
|
7083
|
-
|
7084
|
-
const [ main ] = generateFunc({}, program);
|
7004
|
+
const [ main ] = generateFunc({}, {
|
7005
|
+
type: 'Program',
|
7006
|
+
id: { name: '#main' },
|
7007
|
+
body: {
|
7008
|
+
type: 'BlockStatement',
|
7009
|
+
body: program.body
|
7010
|
+
}
|
7011
|
+
});
|
7085
7012
|
|
7086
7013
|
// if wanted and blank main func and other exports, remove it
|
7087
7014
|
if (Prefs.rmBlankMain && main.wasm.length === 0 && funcs.some(x => x.export)) funcs.splice(main.index - importedFuncs.length, 1);
|
7088
7015
|
|
7089
|
-
// make ~empty funcs for never generated funcs
|
7090
|
-
// todo: these should just be deleted once able
|
7091
7016
|
for (let i = 0; i < funcs.length; i++) {
|
7092
7017
|
const f = funcs[i];
|
7093
7018
|
|
7094
|
-
|
7095
|
-
|
7096
|
-
|
7019
|
+
const wasm = f.wasm;
|
7020
|
+
if (wasm) {
|
7021
|
+
// func was generated, run callback ops
|
7097
7022
|
for (let j = 0; j < wasm.length; j++) {
|
7098
7023
|
const o = wasm[j];
|
7099
7024
|
if (o[0] === null && typeof o[1] === 'function') {
|
@@ -7104,14 +7029,8 @@ export default program => {
|
|
7104
7029
|
continue;
|
7105
7030
|
}
|
7106
7031
|
|
7107
|
-
// make wasm just return 0s for expected returns
|
7032
|
+
// func was never generated, make wasm just return 0s for expected returns
|
7108
7033
|
f.wasm = f.returns.map(x => number(0, x));
|
7109
|
-
|
7110
|
-
// alternative: make func empty, may break some indirect calls
|
7111
|
-
// f.wasm = [];
|
7112
|
-
// f.returns = [];
|
7113
|
-
// f.params = [];
|
7114
|
-
// f.locals = {};
|
7115
7034
|
}
|
7116
7035
|
|
7117
7036
|
// // remove never generated functions
|
@@ -7151,10 +7070,9 @@ export default program => {
|
|
7151
7070
|
for (let i = 0; i < indirectFuncs.length; i++) {
|
7152
7071
|
const f = indirectFuncs[i];
|
7153
7072
|
f.index = currentFuncIndex++;
|
7073
|
+
funcs.push(f);
|
7154
7074
|
}
|
7155
7075
|
|
7156
|
-
funcs.push(...indirectFuncs);
|
7157
|
-
|
7158
7076
|
delete globals['#ind'];
|
7159
7077
|
|
7160
7078
|
return { funcs, globals, tags, exceptions, pages, data };
|
package/package.json
CHANGED