porffor 0.55.13 → 0.55.15
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/README.md +0 -1
- package/compiler/builtins/_internal_object.ts +10 -2
- package/compiler/builtins/math.ts +86 -2
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/porffor.d.ts +4 -0
- package/compiler/builtins.js +40 -89
- package/compiler/builtins_objects.js +1 -1
- package/compiler/builtins_precompiled.js +361 -354
- package/compiler/codegen.js +2 -3
- package/compiler/cyclone.js +1 -2
- package/compiler/encoding.js +10 -0
- package/compiler/opt.js +1 -2
- package/compiler/pgo.js +2 -2
- package/compiler/precompile.js +1 -1
- package/compiler/prototype.js +1 -1
- package/compiler/types.js +5 -19
- package/package.json +1 -1
- package/r.cjs +2 -9
- package/rhemyn/compile.js +2 -2
- package/runner/flamegraph.js +1 -1
- package/runner/index.js +1 -1
- package/.fails.cjs +0 -20
- package/all.json +0 -1
- package/compiler/cache.js +0 -44
- package/compiler/embedding.js +0 -11
package/README.md
CHANGED
@@ -108,7 +108,6 @@ Porffor can run Test262 via some hacks/transforms which remove unsupported featu
|
|
108
108
|
- `codegen.js`: code (wasm) generation, ast -> wasm. The bulk of the effort
|
109
109
|
- `cyclone.js`: wasm partial constant evaluator (it is fast and dangerous hence "cyclone")
|
110
110
|
- `disassemble.js`: wasm disassembler using internal debug info
|
111
|
-
- `embedding.js`: utils for embedding consts
|
112
111
|
- `encoding.js`: utils for encoding things as bytes as wasm expects
|
113
112
|
- `expression.js`: mapping most operators to an opcode (advanced are as built-ins eg `f64_%`)
|
114
113
|
- `havoc.js`: wasm rewrite library (it wreaks havoc upon wasm bytecode hence "havoc")
|
@@ -135,11 +135,16 @@ export const __Porffor_object_lookup = (obj: any, target: any): i32 => {
|
|
135
135
|
const size: i32 = Porffor.wasm.i32.load(obj, 0, 0);
|
136
136
|
const endPtr: i32 = ptr + size * 14;
|
137
137
|
|
138
|
+
let out: boolean = false;
|
138
139
|
if (targetType == Porffor.TYPES.symbol) {
|
139
140
|
const targetSym: symbol = target;
|
140
141
|
for (; ptr < endPtr; ptr += 14) {
|
141
142
|
const keyRaw: i32 = Porffor.wasm.i32.load(ptr, 0, 0);
|
142
|
-
if (keyRaw == 0)
|
143
|
+
if (keyRaw == 0) {
|
144
|
+
if (out) break; // ran out of keys
|
145
|
+
out = true;
|
146
|
+
}
|
147
|
+
|
143
148
|
if (keyRaw >>> 30 == 3) { // MSB 1 and 2 set, symbol
|
144
149
|
const keySym: symbol = keyRaw & 0x3FFFFFFF; // unset MSB
|
145
150
|
if (keySym == targetSym) return ptr;
|
@@ -148,7 +153,10 @@ export const __Porffor_object_lookup = (obj: any, target: any): i32 => {
|
|
148
153
|
} else {
|
149
154
|
for (; ptr < endPtr; ptr += 14) {
|
150
155
|
const keyRaw: i32 = Porffor.wasm.i32.load(ptr, 0, 0);
|
151
|
-
if (keyRaw == 0)
|
156
|
+
if (keyRaw == 0) {
|
157
|
+
if (out) break; // ran out of keys
|
158
|
+
out = true;
|
159
|
+
}
|
152
160
|
|
153
161
|
const msb: i32 = keyRaw >>> 30;
|
154
162
|
if (msb == 0) {
|
@@ -137,6 +137,11 @@ export const __Math_pow = (base: number, exponent: number): number => {
|
|
137
137
|
// 2. If exponent is either +0𝔽 or -0𝔽, return 1𝔽.
|
138
138
|
if (exponent == 0) return 1;
|
139
139
|
|
140
|
+
// opt: use bit shift for base 2
|
141
|
+
if (base == 2) {
|
142
|
+
if (Porffor.fastAnd(Number.isInteger(exponent), exponent > 0, exponent < 31)) return 2 << (exponent - 1);
|
143
|
+
}
|
144
|
+
|
140
145
|
if (!Number.isFinite(base)) {
|
141
146
|
// 3. If base is NaN, return NaN.
|
142
147
|
if (Number.isNaN(base)) return base;
|
@@ -259,7 +264,7 @@ export const __Math_expm1 = (x: number): number => {
|
|
259
264
|
return x;
|
260
265
|
}
|
261
266
|
|
262
|
-
// use exp(x) - 1 for large x
|
267
|
+
// opt: use exp(x) - 1 for large x
|
263
268
|
if (Math.abs(x) > 1e-5) return Math.exp(x) - 1;
|
264
269
|
|
265
270
|
// Taylor series
|
@@ -280,7 +285,7 @@ export const __Math_log1p = (x: number): number => {
|
|
280
285
|
if (x == -1) return -Infinity; // log(0) = -inf
|
281
286
|
if (!Number.isFinite(x)) return x;
|
282
287
|
|
283
|
-
// use exp(x) - 1 for large x
|
288
|
+
// opt: use exp(x) - 1 for large x
|
284
289
|
if (Math.abs(x) > 1e-5) return Math.log(1 + x);
|
285
290
|
|
286
291
|
// Taylor series
|
@@ -454,4 +459,83 @@ export const __Math_atan2 = (y: number, x: number): number => {
|
|
454
459
|
|
455
460
|
if (y >= 0) return Math.atan(ratio) + Math.PI;
|
456
461
|
return Math.atan(ratio) - Math.PI;
|
462
|
+
};
|
463
|
+
|
464
|
+
export const __Math_sumPrecise = (values: any[]): number => {
|
465
|
+
// based on "Fast exact summation using small and large superaccumulators" by Radford M. Neal
|
466
|
+
// https://arxiv.org/abs/1505.05571
|
467
|
+
// accuracy is top priority, it is fine for this to be slow(er)
|
468
|
+
|
469
|
+
// small superaccumulator uses 67 chunks: (1 << (11 - 5)) + 3
|
470
|
+
// 11 is the number of exponent bits in IEEE-754 double precision
|
471
|
+
// 5 is the number of low-order exponent bits stored per chunk
|
472
|
+
const SMALL_SLOTS: number = 67;
|
473
|
+
const SMALL_MIN: number = -970;
|
474
|
+
const small: Float64Array = new Float64Array(SMALL_SLOTS);
|
475
|
+
|
476
|
+
// large superaccumulator uses 4096 chunks: 1 << (11 + 1)
|
477
|
+
// 11 is the number of exponent bits in IEEE-754 double precision
|
478
|
+
const LARGE_SLOTS: number = 4096;
|
479
|
+
const LARGE_MIN: number = -1074;
|
480
|
+
const large: Float64Array = new Float64Array(LARGE_SLOTS);
|
481
|
+
|
482
|
+
for (const _ of values) {
|
483
|
+
if (Porffor.rawType(_) != Porffor.TYPES.number) throw new TypeError('Math.sumPrecise must have only numbers in values');
|
484
|
+
|
485
|
+
const v: number = _;
|
486
|
+
if (v == 0) continue;
|
487
|
+
|
488
|
+
const exp: number = Porffor.number.getExponent(v);
|
489
|
+
|
490
|
+
// check if value fits in small superaccumulator
|
491
|
+
if (exp >= SMALL_MIN && exp < SMALL_MIN + SMALL_SLOTS) {
|
492
|
+
// map the exponent to an array index (-970 -> 0, -969 -> 1, etc)
|
493
|
+
const slot: number = exp - SMALL_MIN;
|
494
|
+
let y: number = v;
|
495
|
+
|
496
|
+
// cascade up through slots, similar to carrying digits in decimal
|
497
|
+
// but operating in binary and handling floating point carefully
|
498
|
+
for (let i: number = slot; i < SMALL_SLOTS - 1; i++) {
|
499
|
+
const sum: number = small[i] + y;
|
500
|
+
y = sum;
|
501
|
+
|
502
|
+
// a number fits in slot i if its magnitude is less than 2^(i+SMALL_MIN+1)
|
503
|
+
const slotLimit: number = Math.pow(2, i + SMALL_MIN + 1);
|
504
|
+
if (y >= -slotLimit && y < slotLimit) {
|
505
|
+
small[i] = y;
|
506
|
+
y = 0;
|
507
|
+
break;
|
508
|
+
}
|
509
|
+
|
510
|
+
// doesn't fit, clear this slot and continue cascading
|
511
|
+
small[i] = 0;
|
512
|
+
}
|
513
|
+
|
514
|
+
// if we still have a non-zero value after cascading through small,
|
515
|
+
// it needs to go into the large superaccumulator
|
516
|
+
if (y != 0) {
|
517
|
+
large[Porffor.number.getExponent(y) - LARGE_MIN] += y;
|
518
|
+
}
|
519
|
+
} else {
|
520
|
+
// exponent is outside small superaccumulator range,
|
521
|
+
// put it directly in the large superaccumulator
|
522
|
+
large[Porffor.number.getExponent(v) - LARGE_MIN] += v;
|
523
|
+
}
|
524
|
+
}
|
525
|
+
|
526
|
+
// combine results from both superaccumulators,
|
527
|
+
// process from highest to lowest to maintain precision
|
528
|
+
// todo: handle -0 (see test262 test)
|
529
|
+
let sum: number = -0;
|
530
|
+
for (let i: number = LARGE_SLOTS - 1; i >= 0; i--) {
|
531
|
+
sum += large[i];
|
532
|
+
}
|
533
|
+
|
534
|
+
for (let i: number = SMALL_SLOTS - 1; i >= 0; i--) {
|
535
|
+
sum += small[i];
|
536
|
+
}
|
537
|
+
|
538
|
+
// todo: free large and small
|
539
|
+
|
540
|
+
return sum;
|
457
541
|
};
|
@@ -559,6 +559,8 @@ export const parseInt = (input: any, radix: any): f64 => {
|
|
559
559
|
|
560
560
|
let defaultRadix: boolean = false;
|
561
561
|
radix = ecma262.ToIntegerOrInfinity(radix);
|
562
|
+
if (!Number.isFinite(radix)) radix = 0; // infinity/NaN -> default
|
563
|
+
|
562
564
|
if (radix == 0) {
|
563
565
|
defaultRadix = true;
|
564
566
|
radix = 10;
|
package/compiler/builtins.js
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
import * as PrecompiledBuiltins from './builtins_precompiled.js';
|
2
2
|
import ObjectBuiltins from './builtins_objects.js';
|
3
3
|
import { Blocktype, Opcodes, Valtype, ValtypeSize } from './wasmSpec.js';
|
4
|
-
import { number } from './embedding.js';
|
5
4
|
import { TYPES, TYPE_NAMES } from './types.js';
|
5
|
+
import { number, unsignedLEB128 } from './encoding.js';
|
6
6
|
import './prefs.js';
|
7
|
-
import { unsignedLEB128 } from './encoding.js';
|
8
7
|
|
9
8
|
export const importedFuncs = [
|
10
9
|
{
|
@@ -230,90 +229,26 @@ export const BuiltinFuncs = function() {
|
|
230
229
|
};
|
231
230
|
|
232
231
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
]
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
[ Opcodes.f64_abs ]
|
254
|
-
]
|
255
|
-
};
|
256
|
-
|
257
|
-
this.__Math_sign = {
|
258
|
-
floatOnly: true,
|
259
|
-
params: [ valtypeBinary ],
|
260
|
-
locals: [],
|
261
|
-
returns: [ valtypeBinary ],
|
262
|
-
returnType: TYPES.number,
|
263
|
-
wasm: [
|
264
|
-
number(1),
|
265
|
-
[ Opcodes.local_get, 0 ],
|
266
|
-
[ Opcodes.f64_copysign ]
|
267
|
-
]
|
268
|
-
};
|
269
|
-
|
270
|
-
this.__Math_floor = {
|
271
|
-
floatOnly: true,
|
272
|
-
params: [ valtypeBinary ],
|
273
|
-
locals: [],
|
274
|
-
returns: [ valtypeBinary ],
|
275
|
-
returnType: TYPES.number,
|
276
|
-
wasm: [
|
277
|
-
[ Opcodes.local_get, 0 ],
|
278
|
-
[ Opcodes.f64_floor ]
|
279
|
-
]
|
280
|
-
};
|
281
|
-
|
282
|
-
this.__Math_ceil = {
|
283
|
-
floatOnly: true,
|
284
|
-
params: [ valtypeBinary ],
|
285
|
-
locals: [],
|
286
|
-
returns: [ valtypeBinary ],
|
287
|
-
returnType: TYPES.number,
|
288
|
-
wasm: [
|
289
|
-
[ Opcodes.local_get, 0 ],
|
290
|
-
[ Opcodes.f64_ceil ]
|
291
|
-
]
|
292
|
-
};
|
293
|
-
|
294
|
-
this.__Math_round = {
|
295
|
-
floatOnly: true,
|
296
|
-
params: [ valtypeBinary ],
|
297
|
-
locals: [],
|
298
|
-
returns: [ valtypeBinary ],
|
299
|
-
returnType: TYPES.number,
|
300
|
-
wasm: [
|
301
|
-
[ Opcodes.local_get, 0 ],
|
302
|
-
[ Opcodes.f64_nearest ]
|
303
|
-
]
|
304
|
-
};
|
305
|
-
|
306
|
-
this.__Math_trunc = {
|
307
|
-
floatOnly: true,
|
308
|
-
params: [ valtypeBinary ],
|
309
|
-
locals: [],
|
310
|
-
returns: [ valtypeBinary ],
|
311
|
-
returnType: TYPES.number,
|
312
|
-
wasm: [
|
313
|
-
[ Opcodes.local_get, 0 ],
|
314
|
-
[ Opcodes.f64_trunc ]
|
315
|
-
]
|
316
|
-
};
|
232
|
+
for (const [ name, op, prefix = [ [ Opcodes.local_get, 0 ] ] ] of [
|
233
|
+
[ 'sqrt', Opcodes.f64_sqrt ],
|
234
|
+
[ 'abs', Opcodes.f64_abs ],
|
235
|
+
[ 'sign', Opcodes.f64_copysign, [ number(1), [ Opcodes.local_get, 0 ] ] ],
|
236
|
+
[ 'floor', Opcodes.f64_floor ],
|
237
|
+
[ 'ceil', Opcodes.f64_ceil ],
|
238
|
+
[ 'round', Opcodes.f64_nearest ],
|
239
|
+
[ 'trunc', Opcodes.f64_trunc ]
|
240
|
+
]) {
|
241
|
+
this[`__Math_${name}`] = {
|
242
|
+
params: [ Valtype.f64 ],
|
243
|
+
locals: [],
|
244
|
+
returns: [ Valtype.f64 ],
|
245
|
+
returnType: TYPES.number,
|
246
|
+
wasm: [
|
247
|
+
...prefix,
|
248
|
+
[ op ]
|
249
|
+
]
|
250
|
+
};
|
251
|
+
}
|
317
252
|
|
318
253
|
// todo: does not follow spec with +-Infinity and values >2**32
|
319
254
|
this.__Math_clz32 = {
|
@@ -359,10 +294,7 @@ export const BuiltinFuncs = function() {
|
|
359
294
|
]
|
360
295
|
};
|
361
296
|
|
362
|
-
// this is an implementation of xorshift128+ (in wasm bytecode)
|
363
|
-
// fun fact: v8, SM, JSC also use this (you will need this fun fact to maintain your sanity reading this code)
|
364
297
|
const prngSeed0 = (Math.random() * (2 ** 30)) | 0, prngSeed1 = (Math.random() * (2 ** 30)) | 0;
|
365
|
-
|
366
298
|
const prng = ({
|
367
299
|
'lcg32': {
|
368
300
|
globals: [ Valtype.i32 ],
|
@@ -1025,5 +957,24 @@ export const BuiltinFuncs = function() {
|
|
1025
957
|
table: true
|
1026
958
|
};
|
1027
959
|
|
960
|
+
this.__Porffor_number_getExponent = {
|
961
|
+
params: [ Valtype.f64 ],
|
962
|
+
returns: [ Valtype.i32 ],
|
963
|
+
returnType: TYPES.number,
|
964
|
+
wasm: [
|
965
|
+
// extract exponent bits from f64 with bit manipulation
|
966
|
+
[ Opcodes.local_get, 0 ],
|
967
|
+
[ Opcodes.i64_reinterpret_f64 ],
|
968
|
+
number(52, Valtype.i64),
|
969
|
+
[ Opcodes.i64_shr_u ],
|
970
|
+
number(0x7FF, Valtype.i64),
|
971
|
+
[ Opcodes.i64_and ],
|
972
|
+
[ Opcodes.i32_wrap_i64 ],
|
973
|
+
number(1023, Valtype.i32),
|
974
|
+
[ Opcodes.i32_sub ]
|
975
|
+
]
|
976
|
+
};
|
977
|
+
|
978
|
+
|
1028
979
|
PrecompiledBuiltins.BuiltinFuncs.call(this);
|
1029
980
|
};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Blocktype, Opcodes, PageSize, Valtype } from './wasmSpec.js';
|
2
2
|
import { TYPES } from './types.js';
|
3
|
-
import { number } from './
|
3
|
+
import { number } from './encoding.js';
|
4
4
|
|
5
5
|
export default function({ builtinFuncs }, Prefs) {
|
6
6
|
const makePrefix = name => (name.startsWith('__') ? '' : '__') + name + '_';
|