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 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 `./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).
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
 
@@ -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
- // so strings ('default') are at the start before any numbers
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.typeswitchBrtable)
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
- ...generate(scope, decl.left.object),
2347
- Opcodes.i32_to_u,
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
- ...generate(scope, decl.left.object),
2373
- Opcodes.i32_to_u,
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 + randId() : 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 (Prefs.data && firstAssign && useRawElements) {
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
- ...array,
3275
- Opcodes.i32_to_u,
3276
- [ Opcodes.i32_add ],
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
- ...array,
3304
- Opcodes.i32_to_u,
3305
- [ Opcodes.i32_add ],
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
- ...generate(scope, decl.object),
3406
- Opcodes.i32_to_u,
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
- ...generate(scope, decl.object),
3418
- Opcodes.i32_to_u,
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
- ...generate(scope, decl.object),
3431
- Opcodes.i32_to_u,
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
- ...object,
3503
- Opcodes.i32_to_u,
3504
- [ Opcodes.i32_add ],
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
- ...object,
3527
- Opcodes.i32_to_u,
3528
- [ Opcodes.i32_add ],
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(1, -2).padEnd(12, '0');
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 : `anonymous${randId()}`;
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 !!!
@@ -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.i32);
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.i32);
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.i32);
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.i32);
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.i32);
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.i32);
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 = Math.max(a, b);
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 (i >= 2 &&
471
- ((opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
472
- (opcode >= 0x61 && opcode <= 0x66)) // main f64 eq op
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 >= 3) { // i32.trunc_sat_f64_s/u
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.rmUnusedTypes) {
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 && Prefs.rmUnusedTypes) {
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.rmUnusedTypes) {
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 || flags.includes('typed') ? ['estree', 'typescript'] : ['estree'],
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((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
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; // not all integer values
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 = func.params.length; j < localData[i].length; 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 = func.params.length; j < localData[i].length; 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
 
@@ -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', 'typed']);
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', 'data', 'rmUnusedTypes' ];
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
- // 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,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-a8f23d010",
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.dev"
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 (['precompile', 'run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
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;