porffor 0.16.0-688a50c13 → 0.16.0-79cd8c0c8

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.
@@ -1,5 +1,5 @@
1
- import { Blocktype, Opcodes, Valtype, ValtypeSize } from './wasmSpec.js';
2
- import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector, read_signedLEB128 } from './encoding.js';
1
+ import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from './wasmSpec.js';
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';
5
5
  import { PrototypeFuncs } from './prototype.js';
@@ -9,16 +9,15 @@ import * as Rhemyn from '../rhemyn/compile.js';
9
9
  import parse from './parse.js';
10
10
  import { log } from './log.js';
11
11
  import Prefs from './prefs.js';
12
- import Allocator from './allocators/index.js';
13
12
 
14
13
  let globals = {};
14
+ let globalInd = 0;
15
15
  let tags = [];
16
16
  let funcs = [];
17
17
  let exceptions = [];
18
18
  let funcIndex = {};
19
19
  let currentFuncIndex = importedFuncs.length;
20
20
  let builtinFuncs = {}, builtinVars = {}, prototypeFuncs = {};
21
- let allocator;
22
21
 
23
22
  class TodoError extends Error {
24
23
  constructor(message) {
@@ -182,6 +181,12 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
182
181
  continue;
183
182
  }
184
183
 
184
+ if (asm[0] === 'memory') {
185
+ allocPage(scope, 'asm instrinsic');
186
+ // todo: add to store/load offset insts
187
+ continue;
188
+ }
189
+
185
190
  let inst = Opcodes[asm[0].replace('.', '_')];
186
191
  if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
187
192
 
@@ -428,12 +433,63 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
428
433
  const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
429
434
  const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
430
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
+
431
487
  const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
432
488
 
433
489
  // alloc/assign array
434
490
  const [ , pointer ] = makeArray(scope, {
435
491
  rawElements: new Array(0)
436
- }, global, name, true, 'i16', true);
492
+ }, global, name, true, 'i16');
437
493
 
438
494
  return [
439
495
  // setup left
@@ -447,7 +503,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
447
503
  [ Opcodes.local_set, rightPointer ],
448
504
 
449
505
  // calculate length
450
- ...pointer, // base 0 for store later
506
+ ...number(0, Valtype.i32), // base 0 for store later
451
507
 
452
508
  [ Opcodes.local_get, leftPointer ],
453
509
  [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
@@ -460,13 +516,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
460
516
  [ Opcodes.i32_add ],
461
517
 
462
518
  // store length
463
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
519
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
464
520
 
465
521
  // copy left
466
522
  // dst = out pointer + length size
467
- ...pointer,
468
- ...number(ValtypeSize.i32, Valtype.i32),
469
- [ Opcodes.i32_add ],
523
+ ...number(pointer + ValtypeSize.i32, Valtype.i32),
470
524
 
471
525
  // src = left pointer + length size
472
526
  [ Opcodes.local_get, leftPointer ],
@@ -479,9 +533,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
479
533
 
480
534
  // copy right
481
535
  // dst = out pointer + length size + left length * sizeof valtype
482
- ...pointer,
483
- ...number(ValtypeSize.i32, Valtype.i32),
484
- [ Opcodes.i32_add ],
536
+ ...number(pointer + ValtypeSize.i32, Valtype.i32),
485
537
 
486
538
  [ Opcodes.local_get, leftLength ],
487
539
  ...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
@@ -501,8 +553,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
501
553
  [ ...Opcodes.memory_copy, 0x00, 0x00 ],
502
554
 
503
555
  // return new string (page)
504
- ...pointer,
505
- Opcodes.i32_from_u
556
+ ...number(pointer)
506
557
  ];
507
558
  };
508
559
 
@@ -618,10 +669,10 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
618
669
  };
619
670
 
620
671
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
621
- // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
622
- // ...wasm,
623
- // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
624
- // ];
672
+ if (isIntToFloatOp(wasm[wasm.length - 1])) return [
673
+ ...wasm,
674
+ ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
675
+ ];
625
676
  // if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
626
677
 
627
678
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
@@ -1040,9 +1091,9 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1040
1091
 
1041
1092
  let baseGlobalIdx, i = 0;
1042
1093
  for (const type of globalTypes) {
1043
- if (baseGlobalIdx === undefined) baseGlobalIdx = globals['#ind'];
1094
+ if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
1044
1095
 
1045
- globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
1096
+ globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globalInd++, type, init: globalInits[i] ?? 0 };
1046
1097
  i++;
1047
1098
  }
1048
1099
 
@@ -1055,15 +1106,11 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1055
1106
  }
1056
1107
  }
1057
1108
 
1058
- if (table) {
1059
- for (const inst of wasm) {
1060
- if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1061
- inst.splice(2, 99);
1062
- inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1063
- }
1109
+ if (table) for (const inst of wasm) {
1110
+ if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1111
+ inst.splice(2, 99);
1112
+ inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1064
1113
  }
1065
-
1066
- funcs.table = true;
1067
1114
  }
1068
1115
 
1069
1116
  func.wasm = wasm;
@@ -1678,7 +1725,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1678
1725
  }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1679
1726
  return makeArray(scope, {
1680
1727
  rawElements: new Array(length)
1681
- }, _global, _name, true, itemType, true);
1728
+ }, _global, _name, true, itemType);
1682
1729
  }, () => {
1683
1730
  optUnused = true;
1684
1731
  return unusedValue;
@@ -2068,6 +2115,7 @@ const brTable = (input, bc, returns) => {
2068
2115
  }
2069
2116
 
2070
2117
  for (let i = 0; i < count; i++) {
2118
+ // if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
2071
2119
  if (i === 0) out.push([ Opcodes.block, returns ]);
2072
2120
  else out.push([ Opcodes.block, Blocktype.void ]);
2073
2121
  }
@@ -2101,8 +2149,10 @@ const brTable = (input, bc, returns) => {
2101
2149
  [ Opcodes.br_table, ...encodeVector(table), 0 ]
2102
2150
  );
2103
2151
 
2104
- // sort the wrong way and then reverse
2105
- // 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
2106
2156
  const orderedBc = keys.sort((a, b) => b - a).reverse();
2107
2157
 
2108
2158
  br = count - 1;
@@ -2128,7 +2178,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2128
2178
  return bc[known] ?? bc.default;
2129
2179
  }
2130
2180
 
2131
- if (Prefs.typeswitchBrtable)
2181
+ if (Prefs.typeswitchUseBrtable)
2132
2182
  return brTable(type, bc, returns);
2133
2183
 
2134
2184
  const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
@@ -2183,11 +2233,11 @@ const allocVar = (scope, name, global = false, type = true) => {
2183
2233
  return target[name].idx;
2184
2234
  }
2185
2235
 
2186
- let idx = global ? globals['#ind']++ : scope.localInd++;
2236
+ let idx = global ? globalInd++ : scope.localInd++;
2187
2237
  target[name] = { idx, type: valtypeBinary };
2188
2238
 
2189
2239
  if (type) {
2190
- let typeIdx = global ? globals['#ind']++ : scope.localInd++;
2240
+ let typeIdx = global ? globalInd++ : scope.localInd++;
2191
2241
  target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
2192
2242
  }
2193
2243
 
@@ -2280,10 +2330,24 @@ const generateVar = (scope, decl) => {
2280
2330
  }
2281
2331
 
2282
2332
  if (x.init) {
2283
- const alreadyArray = scope.arrays?.get(name) != null;
2333
+ // if (isFuncType(x.init.type)) {
2334
+ // // let a = function () { ... }
2335
+ // x.init.id = { name };
2336
+
2337
+ // const func = generateFunc(scope, x.init);
2338
+
2339
+ // out.push(
2340
+ // ...number(func.index - importedFuncs.length),
2341
+ // [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
2342
+
2343
+ // ...setType(scope, name, TYPES.function)
2344
+ // );
2345
+
2346
+ // continue;
2347
+ // }
2284
2348
 
2285
2349
  const generated = generate(scope, x.init, global, name);
2286
- if (!alreadyArray && scope.arrays?.get(name) != null) {
2350
+ if (scope.arrays?.get(name) != null) {
2287
2351
  // hack to set local as pointer before
2288
2352
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2289
2353
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
@@ -2335,12 +2399,19 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2335
2399
 
2336
2400
  // hack: .length setter
2337
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
+
2338
2407
  const newValueTmp = localTmp(scope, '__length_setter_tmp');
2339
2408
  const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2340
2409
 
2341
2410
  return [
2342
- ...generate(scope, decl.left.object),
2343
- Opcodes.i32_to_u,
2411
+ ...(aotPointer ? number(0, Valtype.i32) : [
2412
+ ...generate(scope, decl.left.object),
2413
+ Opcodes.i32_to_u
2414
+ ]),
2344
2415
  ...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2345
2416
 
2346
2417
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
@@ -2351,7 +2422,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2351
2422
  [ Opcodes.local_tee, newValueTmp ],
2352
2423
 
2353
2424
  Opcodes.i32_to_u,
2354
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
2425
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
2355
2426
 
2356
2427
  [ Opcodes.local_get, newValueTmp ]
2357
2428
  ];
@@ -2359,14 +2430,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2359
2430
 
2360
2431
  // arr[i]
2361
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
+
2362
2438
  const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2363
2439
  const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2364
2440
 
2365
2441
  return [
2366
2442
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2367
2443
  [TYPES.array]: [
2368
- ...generate(scope, decl.left.object),
2369
- Opcodes.i32_to_u,
2444
+ ...(aotPointer ? [] : [
2445
+ ...generate(scope, decl.left.object),
2446
+ Opcodes.i32_to_u
2447
+ ]),
2370
2448
 
2371
2449
  // get index as valtype
2372
2450
  ...generate(scope, decl.left.property),
@@ -2375,22 +2453,39 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2375
2453
  // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2376
2454
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2377
2455
  [ Opcodes.i32_mul ],
2378
- [ Opcodes.i32_add ],
2456
+ ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2379
2457
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2380
2458
 
2381
2459
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2382
2460
  [ Opcodes.local_get, pointerTmp ],
2383
- [ Opcodes.load, 0, ValtypeSize.i32 ]
2461
+ [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2384
2462
  ], generate(scope, decl.right), [
2385
2463
  [ Opcodes.local_get, pointerTmp ],
2386
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2464
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
2387
2465
  ], getNodeType(scope, decl.right), false, name, true)),
2388
2466
  [ Opcodes.local_tee, newValueTmp ],
2389
2467
 
2390
- [ Opcodes.store, 0, ValtypeSize.i32 ]
2468
+ [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2391
2469
  ],
2392
2470
 
2393
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
+ // ]
2394
2489
  }, Blocktype.void),
2395
2490
 
2396
2491
  [ Opcodes.local_get, newValueTmp ]
@@ -2774,12 +2869,12 @@ const generateForOf = (scope, decl) => {
2774
2869
 
2775
2870
  // // todo: we should only do this for strings but we don't know at compile-time :(
2776
2871
  // hack: this is naughty and will break things!
2777
- let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2872
+ let newOut = number(0, Valtype.f64), newPointer = -1;
2778
2873
  if (pages.hasAnyString) {
2779
2874
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2780
2875
  0, [ newOut, newPointer ] = makeArray(scope, {
2781
- rawElements: new Array(0)
2782
- }, isGlobal, leftName, true, 'i16', true);
2876
+ rawElements: new Array(1)
2877
+ }, isGlobal, leftName, true, 'i16');
2783
2878
  }
2784
2879
 
2785
2880
  // set type for local
@@ -2826,28 +2921,23 @@ const generateForOf = (scope, decl) => {
2826
2921
  [TYPES.string]: [
2827
2922
  ...setType(scope, leftName, TYPES.string),
2828
2923
 
2924
+ [ Opcodes.loop, Blocktype.void ],
2925
+
2829
2926
  // setup new/out array
2830
2927
  ...newOut,
2928
+ [ Opcodes.drop ],
2831
2929
 
2832
- // set length to 1
2833
- ...number(1, Valtype.i32),
2834
- [ Opcodes.i32_store, 0, 0 ],
2835
-
2836
- [ Opcodes.loop, Blocktype.void ],
2837
-
2838
- // use as pointer for store later
2839
- ...newPointer,
2930
+ ...number(0, Valtype.i32), // base 0 for store after
2840
2931
 
2841
2932
  // load current string ind {arg}
2842
2933
  [ Opcodes.local_get, pointer ],
2843
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2934
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
2844
2935
 
2845
2936
  // store to new string ind 0
2846
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2937
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2847
2938
 
2848
2939
  // return new string (page)
2849
- ...newPointer,
2850
- Opcodes.i32_from_u,
2940
+ ...number(newPointer),
2851
2941
 
2852
2942
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2853
2943
 
@@ -2879,30 +2969,25 @@ const generateForOf = (scope, decl) => {
2879
2969
  [TYPES.bytestring]: [
2880
2970
  ...setType(scope, leftName, TYPES.bytestring),
2881
2971
 
2972
+ [ Opcodes.loop, Blocktype.void ],
2973
+
2882
2974
  // setup new/out array
2883
2975
  ...newOut,
2976
+ [ Opcodes.drop ],
2884
2977
 
2885
- // set length to 1
2886
- ...number(1, Valtype.i32),
2887
- [ Opcodes.i32_store, 0, 0 ],
2888
-
2889
- [ Opcodes.loop, Blocktype.void ],
2890
-
2891
- // use as pointer for store later
2892
- ...newPointer,
2978
+ ...number(0, Valtype.i32), // base 0 for store after
2893
2979
 
2894
2980
  // load current string ind {arg}
2895
2981
  [ Opcodes.local_get, pointer ],
2896
2982
  [ Opcodes.local_get, counter ],
2897
2983
  [ Opcodes.i32_add ],
2898
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
2984
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
2899
2985
 
2900
2986
  // store to new string ind 0
2901
- [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
2987
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2902
2988
 
2903
2989
  // return new string (page)
2904
- ...newPointer,
2905
- Opcodes.i32_from_u,
2990
+ ...number(newPointer),
2906
2991
 
2907
2992
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2908
2993
 
@@ -3122,6 +3207,16 @@ const allocPage = (scope, reason, type) => {
3122
3207
  return ind;
3123
3208
  };
3124
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
+
3125
3220
  const itemTypeToValtype = {
3126
3221
  i32: 'i32',
3127
3222
  i64: 'i64',
@@ -3156,84 +3251,86 @@ const compileBytes = (val, itemType) => {
3156
3251
  }
3157
3252
  };
3158
3253
 
3159
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
3160
- if (itemType !== 'i16' && itemType !== 'i8') {
3161
- pages.hasArray = true;
3162
- } else {
3163
- pages.hasAnyString = true;
3164
- if (itemType === 'i8') pages.hasByteString = true;
3165
- else pages.hasString = true;
3254
+ const getAllocType = itemType => {
3255
+ switch (itemType) {
3256
+ case 'i8': return 'bytestring';
3257
+ case 'i16': return 'string';
3258
+
3259
+ default: return 'array';
3166
3260
  }
3261
+ };
3167
3262
 
3263
+ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
3168
3264
  const out = [];
3169
3265
 
3170
- const uniqueName = name === '$undeclared' ? name + randId() : name;
3266
+ scope.arrays ??= new Map();
3171
3267
 
3172
- const useRawElements = !!decl.rawElements;
3173
- const elements = useRawElements ? decl.rawElements : decl.elements;
3268
+ let firstAssign = false;
3269
+ if (!scope.arrays.has(name) || name === '$undeclared') {
3270
+ firstAssign = true;
3174
3271
 
3175
- const valtype = itemTypeToValtype[itemType];
3176
- const length = elements.length;
3272
+ // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3273
+ const uniqueName = name === '$undeclared' ? name + randId() : name;
3177
3274
 
3178
- const allocated = allocator.alloc({ scope, pages, globals }, uniqueName, { itemType });
3275
+ let page;
3276
+ if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
3277
+ else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
3179
3278
 
3180
- let pointer = allocated;
3181
- if (allocator.constructor.name !== 'StaticAllocator') {
3182
- const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3183
- out.push(
3184
- ...allocated,
3185
- [ Opcodes.local_set, tmp ]
3186
- );
3279
+ // hack: use 1 for page 0 pointer for fast truthiness
3280
+ const ptr = page === 0 ? 1 : (page * pageSize);
3281
+ scope.arrays.set(name, ptr);
3282
+ }
3187
3283
 
3188
- pointer = [ [ Opcodes.local_get, tmp ] ];
3189
- } else {
3190
- const rawPtr = read_signedLEB128(pointer[0].slice(1));
3284
+ const pointer = scope.arrays.get(name);
3191
3285
 
3192
- scope.arrays ??= new Map();
3193
- const firstAssign = !scope.arrays.has(uniqueName);
3194
- if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3286
+ const local = global ? globals[name] : scope.locals[name];
3195
3287
 
3196
- if (Prefs.data && firstAssign && useRawElements) {
3197
- // if length is 0 memory/data will just be 0000... anyway
3198
- if (length !== 0) {
3199
- let bytes = compileBytes(length, 'i32');
3288
+ const useRawElements = !!decl.rawElements;
3289
+ const elements = useRawElements ? decl.rawElements : decl.elements;
3200
3290
 
3201
- if (!initEmpty) for (let i = 0; i < length; i++) {
3202
- if (elements[i] == null) continue;
3291
+ const valtype = itemTypeToValtype[itemType];
3292
+ const length = elements.length;
3203
3293
 
3204
- bytes.push(...compileBytes(elements[i], itemType));
3205
- }
3294
+ if (firstAssign && useRawElements && !Prefs.noData) {
3295
+ // if length is 0 memory/data will just be 0000... anyway
3296
+ if (length !== 0) {
3297
+ let bytes = compileBytes(length, 'i32');
3206
3298
 
3207
- const ind = data.push({
3208
- offset: rawPtr,
3209
- bytes
3210
- }) - 1;
3299
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3300
+ if (elements[i] == null) continue;
3211
3301
 
3212
- scope.data ??= [];
3213
- scope.data.push(ind);
3302
+ bytes.push(...compileBytes(elements[i], itemType));
3214
3303
  }
3215
3304
 
3216
- // local value as pointer
3217
- return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
3305
+ const ind = data.push({
3306
+ offset: pointer,
3307
+ bytes
3308
+ }) - 1;
3309
+
3310
+ scope.data ??= [];
3311
+ scope.data.push(ind);
3218
3312
  }
3219
3313
 
3220
- const local = global ? globals[name] : scope.locals[name];
3221
- const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3222
- if (pointerTmp != null) {
3223
- out.push(
3224
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3225
- Opcodes.i32_to_u,
3226
- [ Opcodes.local_set, pointerTmp ]
3227
- );
3314
+ // local value as pointer
3315
+ out.push(...number(pointer));
3228
3316
 
3229
- pointer = [ [ Opcodes.local_get, pointerTmp ] ];
3230
- }
3317
+ return [ out, pointer ];
3231
3318
  }
3232
3319
 
3320
+ const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3321
+ if (pointerTmp != null) {
3322
+ out.push(
3323
+ [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3324
+ Opcodes.i32_to_u,
3325
+ [ Opcodes.local_set, pointerTmp ]
3326
+ );
3327
+ }
3328
+
3329
+ const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
3233
3330
 
3234
3331
  // store length
3235
3332
  out.push(
3236
- ...pointer,
3333
+ ...pointerWasm,
3237
3334
  ...number(length, Valtype.i32),
3238
3335
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
3239
3336
  );
@@ -3245,11 +3342,11 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3245
3342
 
3246
3343
  const offset = ValtypeSize.i32 + i * sizePerEl;
3247
3344
  out.push(
3248
- ...pointer,
3345
+ ...pointerWasm,
3249
3346
  ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
3250
3347
  [ storeOp, 0, ...unsignedLEB128(offset) ],
3251
3348
  ...(!typed ? [] : [ // typed presumes !useRawElements
3252
- ...pointer,
3349
+ ...pointerWasm,
3253
3350
  ...getNodeType(scope, elements[i]),
3254
3351
  [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
3255
3352
  ])
@@ -3257,13 +3354,12 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3257
3354
  }
3258
3355
 
3259
3356
  // local value as pointer
3260
- out.push(...pointer);
3261
- if (!intOut) out.push(Opcodes.i32_from_u);
3357
+ out.push(...pointerWasm, Opcodes.i32_from_u);
3262
3358
 
3263
3359
  return [ out, pointer ];
3264
3360
  };
3265
3361
 
3266
- const storeArray = (scope, array, index, element) => {
3362
+ const storeArray = (scope, array, index, element, aotPointer = null) => {
3267
3363
  if (!Array.isArray(element)) element = generate(scope, element);
3268
3364
  if (typeof index === 'number') index = number(index);
3269
3365
 
@@ -3275,25 +3371,26 @@ const storeArray = (scope, array, index, element) => {
3275
3371
  Opcodes.i32_to_u,
3276
3372
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3277
3373
  [ Opcodes.i32_mul ],
3278
-
3279
- ...array,
3280
- Opcodes.i32_to_u,
3281
- [ Opcodes.i32_add ],
3374
+ ...(aotPointer ? [] : [
3375
+ ...array,
3376
+ Opcodes.i32_to_u,
3377
+ [ Opcodes.i32_add ],
3378
+ ]),
3282
3379
  [ Opcodes.local_set, offset ],
3283
3380
 
3284
3381
  // store value
3285
3382
  [ Opcodes.local_get, offset ],
3286
3383
  ...generate(scope, element),
3287
- [ Opcodes.store, 0, ValtypeSize.i32 ],
3384
+ [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3288
3385
 
3289
3386
  // store type
3290
3387
  [ Opcodes.local_get, offset ],
3291
3388
  ...getNodeType(scope, element),
3292
- [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3389
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3293
3390
  ];
3294
3391
  };
3295
3392
 
3296
- const loadArray = (scope, array, index) => {
3393
+ const loadArray = (scope, array, index, aotPointer = null) => {
3297
3394
  if (typeof index === 'number') index = number(index);
3298
3395
 
3299
3396
  const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
@@ -3304,19 +3401,20 @@ const loadArray = (scope, array, index) => {
3304
3401
  Opcodes.i32_to_u,
3305
3402
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3306
3403
  [ Opcodes.i32_mul ],
3307
-
3308
- ...array,
3309
- Opcodes.i32_to_u,
3310
- [ Opcodes.i32_add ],
3404
+ ...(aotPointer ? [] : [
3405
+ ...array,
3406
+ Opcodes.i32_to_u,
3407
+ [ Opcodes.i32_add ],
3408
+ ]),
3311
3409
  [ Opcodes.local_set, offset ],
3312
3410
 
3313
3411
  // load value
3314
3412
  [ Opcodes.local_get, offset ],
3315
- [ Opcodes.load, 0, ValtypeSize.i32 ],
3413
+ [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3316
3414
 
3317
3415
  // load type
3318
3416
  [ Opcodes.local_get, offset ],
3319
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3417
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3320
3418
  ];
3321
3419
  };
3322
3420
 
@@ -3348,7 +3446,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
3348
3446
  };
3349
3447
 
3350
3448
  const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
3351
- return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
3449
+ return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
3352
3450
  };
3353
3451
 
3354
3452
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
@@ -3367,6 +3465,9 @@ const withType = (scope, wasm, type) => [
3367
3465
 
3368
3466
  const generateMember = (scope, decl, _global, _name) => {
3369
3467
  const name = decl.object.name;
3468
+ const pointer = scope.arrays?.get(name);
3469
+
3470
+ const aotPointer = Prefs.aotPointerOpt && pointer;
3370
3471
 
3371
3472
  // hack: .name
3372
3473
  if (decl.property.name === 'name') {
@@ -3407,10 +3508,12 @@ const generateMember = (scope, decl, _global, _name) => {
3407
3508
  if (Prefs.fastLength) {
3408
3509
  // presume valid length object
3409
3510
  return [
3410
- ...generate(scope, decl.object),
3411
- Opcodes.i32_to_u,
3511
+ ...(aotPointer ? number(0, Valtype.i32) : [
3512
+ ...generate(scope, decl.object),
3513
+ Opcodes.i32_to_u
3514
+ ]),
3412
3515
 
3413
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3516
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3414
3517
  Opcodes.i32_from_u
3415
3518
  ];
3416
3519
  }
@@ -3419,10 +3522,12 @@ const generateMember = (scope, decl, _global, _name) => {
3419
3522
  const known = knownType(scope, type);
3420
3523
  if (known != null) {
3421
3524
  if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3422
- ...generate(scope, decl.object),
3423
- Opcodes.i32_to_u,
3525
+ ...(aotPointer ? number(0, Valtype.i32) : [
3526
+ ...generate(scope, decl.object),
3527
+ Opcodes.i32_to_u
3528
+ ]),
3424
3529
 
3425
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3530
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3426
3531
  Opcodes.i32_from_u
3427
3532
  ];
3428
3533
 
@@ -3432,10 +3537,12 @@ const generateMember = (scope, decl, _global, _name) => {
3432
3537
  return [
3433
3538
  ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3434
3539
  [ Opcodes.if, valtypeBinary ],
3435
- ...generate(scope, decl.object),
3436
- Opcodes.i32_to_u,
3540
+ ...(aotPointer ? number(0, Valtype.i32) : [
3541
+ ...generate(scope, decl.object),
3542
+ Opcodes.i32_to_u
3543
+ ]),
3437
3544
 
3438
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3545
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3439
3546
  Opcodes.i32_from_u,
3440
3547
 
3441
3548
  ...setLastType(scope, TYPES.number),
@@ -3478,29 +3585,25 @@ const generateMember = (scope, decl, _global, _name) => {
3478
3585
 
3479
3586
  // // todo: we should only do this for strings but we don't know at compile-time :(
3480
3587
  // hack: this is naughty and will break things!
3481
- let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3588
+ let newOut = number(0, valtypeBinary), newPointer = -1;
3482
3589
  if (pages.hasAnyString) {
3483
- // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3484
3590
  0, [ newOut, newPointer ] = makeArray(scope, {
3485
- rawElements: new Array(0)
3486
- }, _global, _name, true, 'i16', true);
3591
+ rawElements: new Array(1)
3592
+ }, _global, _name, true, 'i16');
3487
3593
  }
3488
3594
 
3489
3595
  return typeSwitch(scope, getNodeType(scope, decl.object), {
3490
3596
  [TYPES.array]: [
3491
- ...loadArray(scope, object, property),
3597
+ ...loadArray(scope, object, property, aotPointer),
3492
3598
  ...setLastType(scope)
3493
3599
  ],
3600
+
3494
3601
  [TYPES.string]: [
3495
3602
  // setup new/out array
3496
3603
  ...newOut,
3604
+ [ Opcodes.drop ],
3497
3605
 
3498
- // set length to 1
3499
- ...number(1, Valtype.i32),
3500
- [ Opcodes.i32_store, 0, 0 ],
3501
-
3502
- // use as pointer for store later
3503
- ...newPointer,
3606
+ ...number(0, Valtype.i32), // base 0 for store later
3504
3607
 
3505
3608
  ...property,
3506
3609
  Opcodes.i32_to_u,
@@ -3508,48 +3611,46 @@ const generateMember = (scope, decl, _global, _name) => {
3508
3611
  ...number(ValtypeSize.i16, Valtype.i32),
3509
3612
  [ Opcodes.i32_mul ],
3510
3613
 
3511
- ...object,
3512
- Opcodes.i32_to_u,
3513
- [ Opcodes.i32_add ],
3614
+ ...(aotPointer ? [] : [
3615
+ ...object,
3616
+ Opcodes.i32_to_u,
3617
+ [ Opcodes.i32_add ]
3618
+ ]),
3514
3619
 
3515
3620
  // load current string ind {arg}
3516
- [ 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) ],
3517
3622
 
3518
3623
  // store to new string ind 0
3519
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3624
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3520
3625
 
3521
3626
  // return new string (page)
3522
- ...newPointer,
3523
- Opcodes.i32_from_u,
3627
+ ...number(newPointer),
3524
3628
  ...setLastType(scope, TYPES.string)
3525
3629
  ],
3526
3630
  [TYPES.bytestring]: [
3527
3631
  // setup new/out array
3528
3632
  ...newOut,
3633
+ [ Opcodes.drop ],
3529
3634
 
3530
- // set length to 1
3531
- ...number(1, Valtype.i32),
3532
- [ Opcodes.i32_store, 0, 0 ],
3533
-
3534
- // use as pointer for store later
3535
- ...newPointer,
3635
+ ...number(0, Valtype.i32), // base 0 for store later
3536
3636
 
3537
3637
  ...property,
3538
3638
  Opcodes.i32_to_u,
3539
3639
 
3540
- ...object,
3541
- Opcodes.i32_to_u,
3542
- [ Opcodes.i32_add ],
3640
+ ...(aotPointer ? [] : [
3641
+ ...object,
3642
+ Opcodes.i32_to_u,
3643
+ [ Opcodes.i32_add ]
3644
+ ]),
3543
3645
 
3544
3646
  // load current string ind {arg}
3545
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
3647
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3546
3648
 
3547
3649
  // store to new string ind 0
3548
- [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
3650
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3549
3651
 
3550
3652
  // return new string (page)
3551
- ...newPointer,
3552
- Opcodes.i32_from_u,
3653
+ ...number(newPointer),
3553
3654
  ...setLastType(scope, TYPES.bytestring)
3554
3655
  ],
3555
3656
 
@@ -3705,13 +3806,12 @@ const internalConstrs = {
3705
3806
  if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
3706
3807
 
3707
3808
  return [
3708
- ...pointer,
3809
+ ...number(0, Valtype.i32),
3709
3810
  ...generate(scope, arg, global, name),
3710
3811
  Opcodes.i32_to_u,
3711
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
3812
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
3712
3813
 
3713
- ...pointer,
3714
- Opcodes.i32_from_u
3814
+ ...number(pointer)
3715
3815
  ];
3716
3816
  },
3717
3817
  type: TYPES.array,
@@ -3860,9 +3960,8 @@ const internalConstrs = {
3860
3960
  };
3861
3961
 
3862
3962
  export default program => {
3863
- globals = {
3864
- ['#ind']: 0
3865
- };
3963
+ globals = {};
3964
+ globalInd = 0;
3866
3965
  tags = [];
3867
3966
  exceptions = [];
3868
3967
  funcs = [];
@@ -3895,7 +3994,6 @@ export default program => {
3895
3994
  builtinFuncs = new BuiltinFuncs();
3896
3995
  builtinVars = new BuiltinVars();
3897
3996
  prototypeFuncs = new PrototypeFuncs();
3898
- allocator = Allocator(Prefs.allocator ?? 'static');
3899
3997
 
3900
3998
  program.id = { name: 'main' };
3901
3999
 
@@ -3938,8 +4036,6 @@ export default program => {
3938
4036
  else main.returns = [];
3939
4037
  }
3940
4038
 
3941
- delete globals['#ind'];
3942
-
3943
4039
  // if blank main func and other exports, remove it
3944
4040
  if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
3945
4041