porffor 0.16.0-6572d1c74 → 0.16.0-688a50c13

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