@markw65/monkeyc-optimizer 1.1.21 → 1.1.22

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.
@@ -26,8 +26,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  mod
27
27
  ));
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var chunk_ZDTW2QRS_exports = {};
30
- __export(chunk_ZDTW2QRS_exports, {
29
+ var chunk_563GVBVT_exports = {};
30
+ __export(chunk_563GVBVT_exports, {
31
31
  EnumTagsConst: () => EnumTagsConst,
32
32
  LastTypeTag: () => LastTypeTag,
33
33
  ObjectLikeTagsConst: () => ObjectLikeTagsConst,
@@ -125,7 +125,7 @@ __export(chunk_ZDTW2QRS_exports, {
125
125
  visitorNode: () => visitorNode,
126
126
  xml_util_exports: () => xml_util_exports
127
127
  });
128
- module.exports = __toCommonJS(chunk_ZDTW2QRS_exports);
128
+ module.exports = __toCommonJS(chunk_563GVBVT_exports);
129
129
  var import_chunk_HHQDDCTP = require("./chunk-HHQDDCTP.cjs");
130
130
  var import_prettier_plugin_monkeyc = require("@markw65/prettier-plugin-monkeyc");
131
131
  var import_node_assert = __toESM(require("node:assert"));
@@ -13663,6 +13663,23 @@ function findDeadStores(func, graph, nodeEquivs, findCopyPropCandidates, logThis
13663
13663
  );
13664
13664
  const deadStores = /* @__PURE__ */ new Set();
13665
13665
  const copyPropStores = /* @__PURE__ */ new Map();
13666
+ const copyPropRef = (curState, key, node) => {
13667
+ if (findCopyPropCandidates && declIsLocal(key)) {
13668
+ if (!curState.anticipated) {
13669
+ curState.anticipated = /* @__PURE__ */ new Map();
13670
+ }
13671
+ addAnt(curState.anticipated, key, node);
13672
+ if (!curState.partiallyAnticipated) {
13673
+ curState.partiallyAnticipated = /* @__PURE__ */ new Map();
13674
+ }
13675
+ addAnt(curState.partiallyAnticipated, key, node);
13676
+ if (logThisRun) {
13677
+ console.log(
13678
+ ` antrefs: ${curState.partiallyAnticipated.get(key) !== false} ${curState.anticipated.get(key) !== false}`
13679
+ );
13680
+ }
13681
+ }
13682
+ };
13666
13683
  order.forEach((block) => {
13667
13684
  if (!block.succs) {
13668
13685
  queue.enqueue(block);
@@ -13694,31 +13711,21 @@ function findDeadStores(func, graph, nodeEquivs, findCopyPropCandidates, logThis
13694
13711
  case "ref":
13695
13712
  if (isTypeStateKey(event.decl)) {
13696
13713
  if (logThisRun) {
13697
- console.log(describeEvent(event));
13714
+ console.log(
13715
+ `${describeEvent(event)} (${sourceLocation(event.node.loc)})`
13716
+ );
13698
13717
  console.log(` kill => ${tsKey(event.decl)}`);
13699
13718
  }
13700
- if (findCopyPropCandidates && declIsLocal(event.decl)) {
13701
- if (!curState.anticipated) {
13702
- curState.anticipated = /* @__PURE__ */ new Map();
13703
- }
13704
- addAnt(curState.anticipated, event.decl, event.node);
13705
- if (!curState.partiallyAnticipated) {
13706
- curState.partiallyAnticipated = /* @__PURE__ */ new Map();
13707
- }
13708
- addAnt(curState.partiallyAnticipated, event.decl, event.node);
13709
- if (logThisRun) {
13710
- console.log(
13711
- ` antrefs: ${curState.partiallyAnticipated.get(event.decl) !== false} ${curState.anticipated.get(event.decl) !== false}`
13712
- );
13713
- }
13714
- }
13719
+ copyPropRef(curState, event.decl, event.node);
13715
13720
  curState.dead.delete(event.decl);
13716
13721
  }
13717
13722
  break;
13718
13723
  case "def":
13719
13724
  if (isTypeStateKey(event.decl) && (event.node.type !== "VariableDeclarator" || event.node.init)) {
13720
13725
  if (logThisRun) {
13721
- console.log(describeEvent(event));
13726
+ console.log(
13727
+ `${describeEvent(event)} (${sourceLocation(event.node.loc)})`
13728
+ );
13722
13729
  }
13723
13730
  const assignNode = event.node.type === "AssignmentExpression" && event.node.operator === "=" && event.node.right || event.node.type === "VariableDeclarator" && event.node.init;
13724
13731
  if (curState.dead.has(event.decl)) {
@@ -13764,6 +13771,8 @@ function findDeadStores(func, graph, nodeEquivs, findCopyPropCandidates, logThis
13764
13771
  if (logThisRun) {
13765
13772
  console.log(` anticipated => ${tsKey(event.decl)}`);
13766
13773
  }
13774
+ } else if (event.node.type === "UpdateExpression") {
13775
+ copyPropRef(curState, event.decl, event.node.argument);
13767
13776
  }
13768
13777
  }
13769
13778
  break;
@@ -21382,178 +21391,6 @@ var init_opcodes = (0, import_chunk_HHQDDCTP.__esm)({
21382
21391
  })(Opcodes || {});
21383
21392
  }
21384
21393
  });
21385
- function optimizeArrayInit(func, block, index, stackPreserving, context) {
21386
- (0, import_node_assert6.default)(
21387
- block.bytecodes[index].op === 20
21388
- /* newa */
21389
- );
21390
- const putvStarts = [];
21391
- let i;
21392
- let initVal = null;
21393
- for (i = index; ++i < block.bytecodes.length - 1; ) {
21394
- const dup = block.bytecodes[i];
21395
- if (dup.op !== 46 || dup.arg !== 0) {
21396
- break;
21397
- }
21398
- const ipush = block.bytecodes[i + 1];
21399
- if (ipush.op !== 37 || ipush.arg !== putvStarts.length) {
21400
- break;
21401
- }
21402
- let found = i;
21403
- for (let k = i + 1, depth = 0; ++k < block.bytecodes.length; ) {
21404
- const bc = block.bytecodes[k];
21405
- if (bc.op === 17 && depth === 1) {
21406
- found = k;
21407
- break;
21408
- }
21409
- const { pop, push } = getOpInfo(bc);
21410
- depth += push - pop;
21411
- if (depth < 0 || bc.op === 46 && bc.arg >= depth) {
21412
- break;
21413
- }
21414
- }
21415
- if (found === i)
21416
- break;
21417
- if (initVal !== false) {
21418
- if (found === i + 3) {
21419
- const bc = block.bytecodes[i + 2];
21420
- if (initVal === null) {
21421
- initVal = bc;
21422
- } else if (initVal.op !== bc.op || initVal.arg !== bc.arg) {
21423
- initVal = false;
21424
- }
21425
- } else {
21426
- initVal = false;
21427
- }
21428
- }
21429
- putvStarts.push(i);
21430
- i = found;
21431
- }
21432
- if (block.bytecodes[i]?.op === 2) {
21433
- const convertAputv = (i2) => {
21434
- const bc = block.bytecodes[i2];
21435
- const op = bc.op;
21436
- bc.op = 2;
21437
- bc.size = 1;
21438
- delete bc.arg;
21439
- (0, import_node_assert6.default)(
21440
- op === 17
21441
- /* aputv */
21442
- );
21443
- };
21444
- convertAputv(i - 1);
21445
- for (i = putvStarts.length; i--; ) {
21446
- const offset = putvStarts[i];
21447
- block.bytecodes.splice(offset, 2);
21448
- if (i) {
21449
- convertAputv(offset - 1);
21450
- }
21451
- }
21452
- (0, import_chunk_HHQDDCTP.logger)(
21453
- "array-init",
21454
- 1,
21455
- `Optimizing unused ${putvStarts.length} element array init at block ${offsetToString(
21456
- block.offset
21457
- )}, starting at index ${index}, at offset ${offsetToString(
21458
- block.bytecodes[index].offset
21459
- )}`
21460
- );
21461
- if ((0, import_chunk_HHQDDCTP.wouldLog)("array-init", 5)) {
21462
- (0, import_chunk_HHQDDCTP.log)(blockToString(block, context));
21463
- }
21464
- return true;
21465
- }
21466
- const bytecode = (op, arg) => {
21467
- const bc = { op, arg, size: opcodeSize(op), offset: context.nextOffset++ };
21468
- if (arg == null)
21469
- delete bc.arg;
21470
- return bc;
21471
- };
21472
- if (initVal) {
21473
- if (putvStarts.length < 3)
21474
- return false;
21475
- (0, import_chunk_HHQDDCTP.logger)(
21476
- "array-init",
21477
- 1,
21478
- `Optimizing ${putvStarts.length} element array init with constant initializer ${bytecodeToString(
21479
- initVal,
21480
- null
21481
- )} at block ${offsetToString(
21482
- block.offset
21483
- )}, starting at index ${index}, at offset ${offsetToString(
21484
- block.bytecodes[index].offset
21485
- )}`
21486
- );
21487
- block.bytecodes.splice(
21488
- putvStarts[1],
21489
- putvStarts[putvStarts.length - 1] - putvStarts[0]
21490
- );
21491
- block.bytecodes.splice(putvStarts[0], 1);
21492
- block.bytecodes[putvStarts[0]].arg = putvStarts.length;
21493
- const loopOffset2 = context.nextOffset;
21494
- block.bytecodes.splice(
21495
- index + 2,
21496
- 0,
21497
- bytecode(37, 1),
21498
- bytecode(4, void 0),
21499
- bytecode(46, 1),
21500
- bytecode(46, 1)
21501
- );
21502
- block.bytecodes.splice(
21503
- index + 8,
21504
- 0,
21505
- bytecode(46, 0),
21506
- bytecode(40, loopOffset2),
21507
- bytecode(2, void 0)
21508
- );
21509
- splitBlock(func, block, index + 10);
21510
- splitBlock(func, block, index + 2);
21511
- const loop2 = func.blocks.get(loopOffset2);
21512
- loop2.preds.add(loopOffset2);
21513
- loop2.taken = loopOffset2;
21514
- return true;
21515
- }
21516
- if (stackPreserving || putvStarts.length < 4)
21517
- return false;
21518
- for (i = putvStarts.length; i-- > 1; ) {
21519
- block.bytecodes.splice(putvStarts[i], 2);
21520
- }
21521
- (0, import_chunk_HHQDDCTP.logger)(
21522
- "array-init",
21523
- 1,
21524
- `Optimizing ${putvStarts.length} element array init at block ${offsetToString(
21525
- block.offset
21526
- )}, starting at index ${index}, at offset ${offsetToString(
21527
- block.bytecodes[index].offset
21528
- )}`
21529
- );
21530
- block.bytecodes[index + 2].arg = putvStarts.length - 1;
21531
- const loopOffset = context.nextOffset;
21532
- block.bytecodes.splice(
21533
- index + 3,
21534
- 0,
21535
- bytecode(46, 1),
21536
- bytecode(46, 1),
21537
- bytecode(37, 1),
21538
- bytecode(4, void 0),
21539
- bytecode(46, 0),
21540
- bytecode(40, loopOffset)
21541
- );
21542
- splitBlock(func, block, index + 9);
21543
- splitBlock(func, block, index + 3);
21544
- const loop = func.blocks.get(loopOffset);
21545
- loop.preds.add(loopOffset);
21546
- loop.taken = loopOffset;
21547
- return true;
21548
- }
21549
- var init_array_init = (0, import_chunk_HHQDDCTP.__esm)({
21550
- "src/readprg/array-init.ts"() {
21551
- "use strict";
21552
- (0, import_chunk_HHQDDCTP.init_util)();
21553
- init_bytecode();
21554
- init_opcodes();
21555
- }
21556
- });
21557
21394
  function postOrderTraverse2(func, visitor) {
21558
21395
  const visited = /* @__PURE__ */ new Set();
21559
21396
  const helper = (offset) => {
@@ -21621,7 +21458,7 @@ function postOrderPropagate(func, preBlock, processBc, postBlock, merge) {
21621
21458
  top.preds?.forEach((pred) => {
21622
21459
  const predBlock = func.blocks.get(pred);
21623
21460
  const isExPred = predBlock.next !== top.offset && predBlock.taken !== top.offset;
21624
- (0, import_node_assert7.default)(!isExPred || predBlock.exsucc === top.offset);
21461
+ (0, import_node_assert6.default)(!isExPred || predBlock.exsucc === top.offset);
21625
21462
  merge(localState, predBlock, isExPred) && queue.enqueue(predBlock);
21626
21463
  const jsrPreds = jsrMap.get(pred);
21627
21464
  if (jsrPreds) {
@@ -21640,7 +21477,7 @@ function computeRetMap(func) {
21640
21477
  const retMap = /* @__PURE__ */ new Map();
21641
21478
  jsrMap.forEach((retBlocks, jsrBlock) => {
21642
21479
  const jsrNext = func.blocks.get(jsrBlock).next;
21643
- (0, import_node_assert7.default)(jsrNext);
21480
+ (0, import_node_assert6.default)(jsrNext);
21644
21481
  retBlocks.forEach((retBlock) => {
21645
21482
  const retSuccs = retMap.get(retBlock);
21646
21483
  if (retSuccs) {
@@ -21670,20 +21507,40 @@ function rpoPropagate(func, preBlock, processBc, postBlock, merge) {
21670
21507
  const top = queue.dequeue();
21671
21508
  const localState = preBlock(top);
21672
21509
  const doMerge = (to, isExSucc) => {
21673
- if (to == null)
21674
- return;
21675
21510
  const toBlock = func.blocks.get(to);
21676
21511
  merge(top, localState, toBlock, isExSucc) && queue.enqueue(toBlock);
21677
21512
  };
21678
- top.bytecodes.forEach((bc) => {
21679
- processBc(top, bc, localState);
21513
+ let flags;
21514
+ for (let i = 0; i < top.bytecodes.length; i++) {
21515
+ const bc = top.bytecodes[i];
21516
+ flags = processBc(top, bc, localState);
21517
+ if (flags != null && flags & 16) {
21518
+ i = -1;
21519
+ continue;
21520
+ }
21680
21521
  if (top.exsucc && (bc.op === 15 || bc.op === 51)) {
21681
21522
  doMerge(top.exsucc, true);
21682
21523
  }
21683
- });
21524
+ }
21684
21525
  postBlock(top, localState);
21685
- doMerge(top.next, false);
21686
- doMerge(top.taken, false);
21526
+ if (flags == null)
21527
+ flags = 0;
21528
+ if (top.next != null) {
21529
+ if (!(flags & 8)) {
21530
+ doMerge(top.next, false);
21531
+ }
21532
+ if (flags & 2) {
21533
+ queue.enqueue(func.blocks.get(top.next));
21534
+ }
21535
+ }
21536
+ if (top.taken) {
21537
+ if (!(flags & 4)) {
21538
+ doMerge(top.taken, false);
21539
+ }
21540
+ if (flags & 1) {
21541
+ queue.enqueue(func.blocks.get(top.taken));
21542
+ }
21543
+ }
21687
21544
  retMap.get(top.offset)?.forEach((retSucc) => doMerge(retSucc, false));
21688
21545
  }
21689
21546
  }
@@ -21694,257 +21551,10 @@ var init_cflow = (0, import_chunk_HHQDDCTP.__esm)({
21694
21551
  init_opcodes();
21695
21552
  }
21696
21553
  });
21697
- function localDCE(func, context) {
21698
- if ((0, import_chunk_HHQDDCTP.wouldLog)("dce", 5)) {
21699
- (0, import_chunk_HHQDDCTP.setBanner)(
21700
- functionBanner(
21701
- func,
21702
- context,
21703
- "local-dce-start",
21704
- (block, footer) => footer ? `liveOutLocals: ${Array.from(
21705
- liveOutLocals.get(block.offset) ?? []
21706
- ).join(" ")}
21707
- ` : ""
21708
- )
21709
- );
21710
- }
21711
- const { liveInLocals, liveOutLocals } = computeLiveLocals(func);
21712
- let anyChanges = false;
21713
- let changes = false;
21714
- const makeNop = (bc) => {
21715
- changes = true;
21716
- makeArgless(
21717
- bc,
21718
- 0
21719
- /* nop */
21720
- );
21721
- };
21722
- const makePopv = (bc) => {
21723
- changes = true;
21724
- makeArgless(
21725
- bc,
21726
- 2
21727
- /* popv */
21728
- );
21729
- };
21730
- func.blocks.forEach((block) => {
21731
- const reportPopv = (i, item, kill) => {
21732
- (0, import_chunk_HHQDDCTP.logger)(
21733
- "dce",
21734
- 2,
21735
- `${func.name}: Convert ${i}:${bytecodeToString(
21736
- block.bytecodes[i],
21737
- context.symbolTable
21738
- )} to popv for ${item.deps.map(
21739
- (i2) => `${i2}:${bytecodeToString(
21740
- block.bytecodes[i2],
21741
- context.symbolTable
21742
- )}${kill ? "=>nop" : ""}`
21743
- ).join(", ")} }`
21744
- );
21745
- };
21746
- const reportNop = (item) => {
21747
- (0, import_chunk_HHQDDCTP.logger)(
21748
- "dce",
21749
- 2,
21750
- `${func.name}: Kill ${item.deps.map((i) => bytecodeToString(block.bytecodes[i], context.symbolTable)).join(", ")}`
21751
- );
21752
- };
21753
- changes = false;
21754
- const dceInfo = {
21755
- stack: [],
21756
- locals: new Set(liveOutLocals.get(block.offset))
21757
- };
21758
- for (let i = block.bytecodes.length; i--; ) {
21759
- const bytecode = block.bytecodes[i];
21760
- switch (bytecode.op) {
21761
- case 19: {
21762
- const liveLocal = dceInfo.locals.has(bytecode.arg);
21763
- if (!liveLocal) {
21764
- (0, import_chunk_HHQDDCTP.logger)(
21765
- "dce",
21766
- 2,
21767
- `${func.name}: Killing store to unused local ${bytecode.arg} at ${offsetToString(block.offset)}:${i}`
21768
- );
21769
- makePopv(bytecode);
21770
- dceInfo.stack.push({ dead: true, deps: [i] });
21771
- } else {
21772
- dceInfo.stack.push({ dead: false });
21773
- }
21774
- dceInfo.locals.delete(bytecode.arg);
21775
- break;
21776
- }
21777
- case 2:
21778
- dceInfo.stack.push({ dead: true, deps: [i] });
21779
- break;
21780
- case 46: {
21781
- const item = dceInfo.stack.pop();
21782
- if (item?.dead) {
21783
- item.deps.push(i);
21784
- reportNop(item);
21785
- item.deps.forEach((index) => makeNop(block.bytecodes[index]));
21786
- } else {
21787
- if (dceInfo.stack.length > bytecode.arg) {
21788
- dceInfo.stack[dceInfo.stack.length - 1 - bytecode.arg].dead = false;
21789
- }
21790
- }
21791
- break;
21792
- }
21793
- case 18:
21794
- case 44:
21795
- case 43:
21796
- case 24:
21797
- case 37:
21798
- case 38:
21799
- case 39:
21800
- case 52:
21801
- case 49:
21802
- case 50: {
21803
- const item = dceInfo.stack.pop();
21804
- if (item?.dead) {
21805
- item.deps.push(i);
21806
- reportNop(item);
21807
- item.deps.forEach((index) => makeNop(block.bytecodes[index]));
21808
- } else if (bytecode.op === 18) {
21809
- dceInfo.locals.add(bytecode.arg);
21810
- }
21811
- break;
21812
- }
21813
- case 3:
21814
- case 4:
21815
- case 5:
21816
- case 6:
21817
- case 7:
21818
- case 8:
21819
- case 9:
21820
- case 10:
21821
- case 11:
21822
- case 12:
21823
- case 26:
21824
- case 27:
21825
- case 28:
21826
- case 29:
21827
- case 30:
21828
- case 31:
21829
- case 34:
21830
- case 33:
21831
- case 16:
21832
- case 13: {
21833
- const item = dceInfo.stack.pop();
21834
- if (item?.dead) {
21835
- reportPopv(i, item, false);
21836
- makePopv(bytecode);
21837
- dceInfo.stack.push({ dead: true, deps: item.deps.slice() });
21838
- dceInfo.stack.push({ dead: true, deps: [i] });
21839
- } else {
21840
- dceInfo.stack.push({ dead: false });
21841
- dceInfo.stack.push({ dead: false });
21842
- }
21843
- break;
21844
- }
21845
- case 21:
21846
- case 32:
21847
- case 45:
21848
- case 48:
21849
- case 20:
21850
- case 54:
21851
- case 47: {
21852
- const item = dceInfo.stack.pop();
21853
- if (item?.dead) {
21854
- reportPopv(i, item, true);
21855
- makePopv(bytecode);
21856
- item.deps.forEach((index) => makeNop(block.bytecodes[index]));
21857
- dceInfo.stack.push({ dead: true, deps: [i] });
21858
- } else {
21859
- dceInfo.stack.push({ dead: false });
21860
- }
21861
- break;
21862
- }
21863
- case 51:
21864
- case 15:
21865
- if (block.exsucc) {
21866
- liveInLocals.get(block.exsucc)?.forEach((local) => dceInfo.locals.add(local));
21867
- }
21868
- default: {
21869
- let { push, pop } = getOpInfo(bytecode);
21870
- while (push-- > 0) {
21871
- dceInfo.stack.pop();
21872
- }
21873
- while (pop-- > 0) {
21874
- dceInfo.stack.push({ dead: false });
21875
- }
21876
- }
21877
- }
21878
- }
21879
- if (changes) {
21880
- anyChanges = true;
21881
- block.bytecodes = block.bytecodes.filter(
21882
- (bc) => bc.op !== 0
21883
- /* nop */
21884
- );
21885
- if ((0, import_chunk_HHQDDCTP.wouldLog)("dce", 3)) {
21886
- (0, import_chunk_HHQDDCTP.log)(functionBanner(func, context, "local-dce-end")());
21887
- }
21888
- }
21889
- });
21890
- (0, import_chunk_HHQDDCTP.setBanner)(null);
21891
- return anyChanges;
21892
- }
21893
- function computeLiveLocals(func) {
21894
- const liveOutLocals = /* @__PURE__ */ new Map();
21895
- const liveInLocals = /* @__PURE__ */ new Map();
21896
- postOrderPropagate(
21897
- func,
21898
- (block) => new Set(liveOutLocals.get(block.offset)),
21899
- (block, bc, locals) => {
21900
- switch (bc.op) {
21901
- case 18:
21902
- locals.add(bc.arg);
21903
- break;
21904
- case 19:
21905
- locals.delete(bc.arg);
21906
- break;
21907
- case 51:
21908
- case 15:
21909
- if (block.exsucc) {
21910
- liveInLocals.get(block.exsucc)?.forEach((local) => locals.add(local));
21911
- }
21912
- break;
21913
- }
21914
- },
21915
- (block, locals) => {
21916
- liveInLocals.set(block.offset, locals);
21917
- },
21918
- (locals, predBlock, isExPred) => {
21919
- if (isExPred)
21920
- return false;
21921
- const predLocals = liveOutLocals.get(predBlock.offset);
21922
- if (!predLocals) {
21923
- liveOutLocals.set(predBlock.offset, new Set(locals));
21924
- return true;
21925
- }
21926
- const size = predLocals.size;
21927
- locals.forEach((local) => predLocals.add(local));
21928
- return size !== predLocals.size;
21929
- }
21930
- );
21931
- return { liveInLocals, liveOutLocals };
21932
- }
21933
- var init_dce = (0, import_chunk_HHQDDCTP.__esm)({
21934
- "src/readprg/dce.ts"() {
21935
- "use strict";
21936
- (0, import_chunk_HHQDDCTP.init_util)();
21937
- init_bytecode();
21938
- init_cflow();
21939
- init_opcodes();
21940
- }
21941
- });
21942
- function interpItemToString(item) {
21943
- let str = display(item.type);
21944
- if (item.dup != null)
21945
- str += ` dup<${item.dup}>`;
21946
- if (item.equivs) {
21947
- str += ` equivs: ${Array.from(item.equivs).join(", ")}`;
21554
+ function interpItemToString(item) {
21555
+ let str = display(item.type);
21556
+ if (item.equivs) {
21557
+ str += ` equivs: ${Array.from(item.equivs).join(", ")}`;
21948
21558
  }
21949
21559
  return str;
21950
21560
  }
@@ -21961,7 +21571,7 @@ function checkState(state) {
21961
21571
  const checkArray = (items) => items.forEach((item) => {
21962
21572
  item.equivs?.forEach((e) => {
21963
21573
  if (getEquivs(state, e) !== item.equivs) {
21964
- (0, import_node_assert8.default)(getEquivs(state, e) === item.equivs);
21574
+ (0, import_node_assert7.default)(getEquivs(state, e) === item.equivs);
21965
21575
  }
21966
21576
  });
21967
21577
  });
@@ -22055,10 +21665,6 @@ function mergeElems(fromElem, toElem) {
22055
21665
  if (unionInto(toElem.type, fromElem.type)) {
22056
21666
  changes = true;
22057
21667
  }
22058
- if (toElem.dup !== fromElem.dup) {
22059
- changes = true;
22060
- delete toElem.dup;
22061
- }
22062
21668
  return changes;
22063
21669
  }
22064
21670
  function mergeInto(from, to) {
@@ -22134,9 +21740,9 @@ function mergeInto(from, to) {
22134
21740
  return changes;
22135
21741
  }
22136
21742
  function findEquivalent(localState, type, value2) {
22137
- const find = (elems) => {
21743
+ const find = (elems, d) => {
22138
21744
  let alternate = null;
22139
- for (let i = elems.length; i--; ) {
21745
+ for (let i = elems.length - d; i--; ) {
22140
21746
  const t = elems[i];
22141
21747
  if (t && t.type.type === type) {
22142
21748
  if (t.type.value === value2) {
@@ -22148,310 +21754,1044 @@ function findEquivalent(localState, type, value2) {
22148
21754
  }
22149
21755
  return alternate;
22150
21756
  };
22151
- const stackIndex = find(localState.stack);
21757
+ const stackIndex = find(localState.stack, 1);
22152
21758
  if (stackIndex != null && stackIndex < localState.stack.length) {
22153
21759
  return ~stackIndex;
22154
21760
  }
22155
- const localIndex = find(localState.locals);
21761
+ const localIndex = find(localState.locals, 0);
22156
21762
  if (localIndex != null && localIndex < localState.locals.length) {
22157
21763
  return localIndex;
22158
21764
  }
22159
21765
  return stackIndex != null ? ~stackIndex : localIndex;
22160
21766
  }
22161
- function interpFunc(func, context) {
22162
- const { symbolTable } = context;
22163
- const equivSets = /* @__PURE__ */ new Map();
22164
- const selfStores = /* @__PURE__ */ new Set();
22165
- const liveInState = /* @__PURE__ */ new Map();
22166
- const replacements = /* @__PURE__ */ new Map();
22167
- const interpLogging = (0, import_chunk_HHQDDCTP.wouldLog)("interp", 1);
22168
- if (interpLogging) {
22169
- if ((0, import_chunk_HHQDDCTP.wouldLog)("interp", 7)) {
22170
- (0, import_chunk_HHQDDCTP.setBanner)(functionBanner(func, context, "interp"));
22171
- } else if ((0, import_chunk_HHQDDCTP.wouldLog)("interp", 3)) {
22172
- (0, import_chunk_HHQDDCTP.setBanner)(
22173
- () => `+++++++++++++ interp-prepare ${func.name} ++++++++++++++`
22174
- );
22175
- }
22176
- }
22177
- const xpush = (localState, block, bc, type, value2) => {
21767
+ function interpBytecode(bc, localState, context) {
21768
+ const xpush = (type, value2) => {
22178
21769
  const tt = { type };
22179
21770
  if (value2 != null) {
22180
21771
  tt.value = value2;
22181
21772
  }
22182
- if (bc.size > 2) {
22183
- const index = findEquivalent(localState, type, value2);
22184
- let blockReps = replacements.get(block);
22185
- if (index != null) {
22186
- if (!blockReps) {
22187
- blockReps = /* @__PURE__ */ new Map();
22188
- replacements.set(block, blockReps);
22189
- }
22190
- if (index < 0) {
22191
- const arg = localState.stack.length - ~index % localState.stack.length - 1;
22192
- blockReps.set(bc, {
22193
- op: 46,
22194
- arg,
22195
- offset: bc.offset,
22196
- size: 2,
22197
- invert: localState.stack.length <= ~index
22198
- });
22199
- } else {
22200
- const arg = index % localState.locals.length;
22201
- blockReps.set(bc, {
22202
- op: 18,
22203
- arg,
22204
- offset: bc.offset,
22205
- size: 2,
22206
- invert: localState.locals.length <= index
22207
- });
22208
- }
22209
- } else if (blockReps) {
22210
- blockReps.delete(bc);
22211
- if (!blockReps.size) {
22212
- replacements.delete(block);
22213
- }
22214
- }
22215
- }
22216
21773
  localState.stack.push({
22217
21774
  type: tt
22218
21775
  });
22219
21776
  };
22220
- rpoPropagate(
22221
- func,
22222
- (block) => {
22223
- if (interpLogging) {
22224
- (0, import_chunk_HHQDDCTP.logger)(
22225
- "interp",
22226
- 3,
22227
- `${offsetToString(block.offset)}: ${block.bytecodes[0]?.lineNum ? lineInfoToString(block.bytecodes[0]?.lineNum, context) : ""}
22228
- ${interpStateToString(liveInState.get(block.offset))}`
22229
- );
22230
- (0, import_chunk_HHQDDCTP.logger)("interp", 9, blockToString(block, context));
21777
+ const binary = (op) => {
21778
+ const args = localState.stack.slice(-2);
21779
+ if (args.length !== 2) {
21780
+ args.splice(
21781
+ 0,
21782
+ 2,
21783
+ { type: {
21784
+ type: 524287
21785
+ /* Any */
21786
+ } },
21787
+ { type: {
21788
+ type: 524287
21789
+ /* Any */
21790
+ } }
21791
+ );
21792
+ }
21793
+ const type = evaluateBinaryTypes(op, args[0].type, args[1].type);
21794
+ if (args[0].equivs) {
21795
+ removeEquiv2(localState, 1 - localState.stack.length);
21796
+ }
21797
+ if (args[1].equivs) {
21798
+ removeEquiv2(localState, 0 - localState.stack.length);
21799
+ }
21800
+ localState.stack.splice(-2, 2, { type });
21801
+ };
21802
+ switch (bc.op) {
21803
+ case 18: {
21804
+ let local = localState.locals[bc.arg];
21805
+ if (local) {
21806
+ local = { ...local };
21807
+ delete local.equivs;
21808
+ } else {
21809
+ local = { type: {
21810
+ type: 524287
21811
+ /* Any */
21812
+ } };
22231
21813
  }
22232
- return cloneState2(liveInState.get(block.offset));
22233
- },
22234
- (block, bc, localState) => {
22235
- switch (bc.op) {
22236
- case 18: {
22237
- let local = localState.locals[bc.arg];
22238
- if (local) {
22239
- local = { ...local };
22240
- delete local.equivs;
21814
+ localState.stack.push(local);
21815
+ addEquiv2(localState, -localState.stack.length, bc.arg);
21816
+ break;
21817
+ }
21818
+ case 46: {
21819
+ const dup = localState.stack.length - bc.arg - 1;
21820
+ let other = localState.stack[dup];
21821
+ if (other) {
21822
+ other = { ...other };
21823
+ delete other.equivs;
21824
+ } else {
21825
+ other = { type: {
21826
+ type: 524287
21827
+ /* Any */
21828
+ } };
21829
+ }
21830
+ localState.stack.push(other);
21831
+ if (dup >= 0) {
21832
+ addEquiv2(localState, -localState.stack.length, ~dup);
21833
+ }
21834
+ break;
21835
+ }
21836
+ case 19: {
21837
+ let curItem = localState.locals[bc.arg];
21838
+ if (!curItem) {
21839
+ curItem = localState.locals[bc.arg] = {
21840
+ type: {
21841
+ type: 524287
21842
+ /* Any */
21843
+ }
21844
+ };
21845
+ }
21846
+ const value2 = localState.stack[localState.stack.length - 1];
21847
+ if (value2.equivs) {
21848
+ if (curItem.equivs?.has(-localState.stack.length)) {
21849
+ removeEquiv2(localState, -localState.stack.length);
21850
+ localState.stack.pop();
21851
+ break;
21852
+ }
21853
+ removeEquiv2(localState, bc.arg);
21854
+ addEquiv2(localState, bc.arg, -localState.stack.length);
21855
+ removeEquiv2(localState, -localState.stack.length);
21856
+ } else if (curItem.equivs) {
21857
+ removeEquiv2(localState, bc.arg);
21858
+ }
21859
+ localState.stack.pop();
21860
+ localState.locals[bc.arg].type = value2.type;
21861
+ break;
21862
+ }
21863
+ case 37:
21864
+ xpush(8, bc.arg);
21865
+ break;
21866
+ case 49:
21867
+ xpush(16, bc.arg);
21868
+ break;
21869
+ case 38:
21870
+ xpush(32, roundToFloat(bc.arg));
21871
+ break;
21872
+ case 50:
21873
+ xpush(64, bc.arg);
21874
+ break;
21875
+ case 52:
21876
+ xpush(128, String.fromCharCode(bc.arg));
21877
+ break;
21878
+ case 43:
21879
+ xpush(
21880
+ bc.arg ? 4 : 2
21881
+ /* False */
21882
+ );
21883
+ break;
21884
+ case 44:
21885
+ xpush(
21886
+ 1
21887
+ /* Null */
21888
+ );
21889
+ break;
21890
+ case 39: {
21891
+ const argSym = context.symbolTable.symbolToLabelMap.get(bc.arg);
21892
+ const name = argSym && context.symbolTable.symbols.get(argSym)?.str;
21893
+ const value2 = `${name ?? "symbol"}<${bc.arg}>`;
21894
+ xpush(131072, value2);
21895
+ break;
21896
+ }
21897
+ case 24: {
21898
+ const symbol = context.symbolTable?.symbols.get(bc.arg);
21899
+ xpush(131072, symbol?.str);
21900
+ break;
21901
+ }
21902
+ case 3:
21903
+ binary("+");
21904
+ break;
21905
+ case 4:
21906
+ binary("-");
21907
+ break;
21908
+ case 5:
21909
+ binary("*");
21910
+ break;
21911
+ case 6:
21912
+ binary("/");
21913
+ break;
21914
+ case 9:
21915
+ binary("%");
21916
+ break;
21917
+ case 10:
21918
+ binary("<<");
21919
+ break;
21920
+ case 11:
21921
+ binary(">>");
21922
+ break;
21923
+ case 7:
21924
+ binary("&");
21925
+ break;
21926
+ case 8:
21927
+ binary("|");
21928
+ break;
21929
+ case 12:
21930
+ binary("^");
21931
+ break;
21932
+ case 26:
21933
+ binary("==");
21934
+ break;
21935
+ case 31:
21936
+ binary("!=");
21937
+ break;
21938
+ case 27:
21939
+ binary("<");
21940
+ break;
21941
+ case 28:
21942
+ binary("<=");
21943
+ break;
21944
+ case 29:
21945
+ binary(">");
21946
+ break;
21947
+ case 30:
21948
+ binary(">=");
21949
+ break;
21950
+ default: {
21951
+ const { pop, push } = getOpInfo(bc);
21952
+ for (let i = 0; i < pop; i++) {
21953
+ removeEquiv2(localState, -localState.stack.length);
21954
+ localState.stack.pop();
21955
+ }
21956
+ for (let i = 0; i < push; i++) {
21957
+ localState.stack.push({ type: {
21958
+ type: 524287
21959
+ /* Any */
21960
+ } });
21961
+ }
21962
+ break;
21963
+ }
21964
+ }
21965
+ }
21966
+ function interpFunc(func, context) {
21967
+ const { symbolTable } = context;
21968
+ const equivSets = /* @__PURE__ */ new Map();
21969
+ const selfStores = /* @__PURE__ */ new Set();
21970
+ const liveInState = /* @__PURE__ */ new Map();
21971
+ const replacements = /* @__PURE__ */ new Map();
21972
+ const interpLogging = (0, import_chunk_HHQDDCTP.wouldLog)("interp", 1);
21973
+ if (interpLogging) {
21974
+ if ((0, import_chunk_HHQDDCTP.wouldLog)("interp", 7)) {
21975
+ (0, import_chunk_HHQDDCTP.setBanner)(functionBanner(func, context, "interp"));
21976
+ } else if ((0, import_chunk_HHQDDCTP.wouldLog)("interp", 3)) {
21977
+ (0, import_chunk_HHQDDCTP.setBanner)(
21978
+ () => `+++++++++++++ interp-prepare ${func.name} ++++++++++++++`
21979
+ );
21980
+ }
21981
+ }
21982
+ rpoPropagate(
21983
+ func,
21984
+ (block) => {
21985
+ if (interpLogging) {
21986
+ (0, import_chunk_HHQDDCTP.logger)(
21987
+ "interp",
21988
+ 3,
21989
+ `${offsetToString(block.offset)}: ${block.bytecodes[0]?.lineNum ? lineInfoToString(block.bytecodes[0]?.lineNum, context) : ""}
21990
+ ${interpStateToString(liveInState.get(block.offset))}`
21991
+ );
21992
+ (0, import_chunk_HHQDDCTP.logger)("interp", 9, blockToString(block, context));
21993
+ }
21994
+ return cloneState2(liveInState.get(block.offset));
21995
+ },
21996
+ (block, bc, localState) => {
21997
+ switch (bc.op) {
21998
+ case 19: {
21999
+ selfStores.delete(bc);
22000
+ equivSets.delete(bc);
22001
+ const curItem = localState.locals[bc.arg];
22002
+ const curEquivs = curItem?.equivs;
22003
+ const selfStore = curEquivs?.has(bc.arg) && curEquivs.has(-localState.stack.length);
22004
+ interpBytecode(bc, localState, context);
22005
+ if (!localState.loopBlock) {
22006
+ if (selfStore) {
22007
+ selfStores.add(bc);
22008
+ break;
22009
+ }
22010
+ const postItem = curItem ?? localState.locals[bc.arg];
22011
+ if (postItem.equivs) {
22012
+ equivSets.set(
22013
+ bc,
22014
+ new Set(Array.from(postItem.equivs).filter((e) => e >= 0))
22015
+ );
22016
+ }
22017
+ }
22018
+ break;
22019
+ }
22020
+ case 37:
22021
+ case 49:
22022
+ case 38:
22023
+ case 50:
22024
+ case 52:
22025
+ case 39: {
22026
+ interpBytecode(bc, localState, context);
22027
+ const topType = localState.stack[localState.stack.length - 1].type;
22028
+ (0, import_node_assert7.default)(isExact(topType));
22029
+ const index = localState.loopBlock ? null : findEquivalent(localState, topType.type, topType.value);
22030
+ let blockReps = replacements.get(block);
22031
+ if (index != null) {
22032
+ if (!blockReps) {
22033
+ blockReps = /* @__PURE__ */ new Map();
22034
+ replacements.set(block, blockReps);
22035
+ }
22036
+ if (index < 0) {
22037
+ const arg = localState.stack.length - ~index % localState.stack.length - 2;
22038
+ blockReps.set(bc, {
22039
+ op: 46,
22040
+ arg,
22041
+ offset: bc.offset,
22042
+ size: 2,
22043
+ invert: localState.stack.length <= ~index
22044
+ });
22045
+ } else {
22046
+ const arg = index % localState.locals.length;
22047
+ blockReps.set(bc, {
22048
+ op: 18,
22049
+ arg,
22050
+ offset: bc.offset,
22051
+ size: 2,
22052
+ invert: localState.locals.length <= index
22053
+ });
22054
+ }
22055
+ } else if (blockReps) {
22056
+ blockReps.delete(bc);
22057
+ if (!blockReps.size) {
22058
+ replacements.delete(block);
22059
+ }
22060
+ }
22061
+ break;
22062
+ }
22063
+ case 40:
22064
+ case 41:
22065
+ if (block.taken === block.offset) {
22066
+ const inState = liveInState.get(block.offset);
22067
+ (0, import_node_assert7.default)(inState);
22068
+ if (inState.stack.length !== localState.stack.length - 1) {
22069
+ const condition = localState.stack[localState.stack.length - 1];
22070
+ const isTrue = mustBeTrue(condition.type);
22071
+ const isFalse = mustBeFalse(condition.type);
22072
+ (0, import_node_assert7.default)(isTrue || isFalse);
22073
+ interpBytecode(bc, localState, context);
22074
+ if (isTrue === (bc.op === 40)) {
22075
+ localState.loopBlock = true;
22076
+ inState.loopBlock = true;
22077
+ return 16;
22078
+ }
22079
+ return 4;
22080
+ }
22081
+ }
22082
+ interpBytecode(bc, localState, context);
22083
+ break;
22084
+ default:
22085
+ interpBytecode(bc, localState, context);
22086
+ }
22087
+ return null;
22088
+ },
22089
+ () => {
22090
+ },
22091
+ (from, localState, succBlock, isExSucc) => {
22092
+ if (isExSucc) {
22093
+ const tryEntry = from.try[from.try.length - 1].tryStart;
22094
+ const entryDepth = liveInState.get(tryEntry)?.stack.length ?? 0;
22095
+ localState = cloneState2(localState);
22096
+ while (entryDepth < localState.stack.length) {
22097
+ if (localState.stack[localState.stack.length - 1].equivs) {
22098
+ removeEquiv2(localState, -localState.stack.length);
22099
+ }
22100
+ localState.stack.pop();
22101
+ }
22102
+ localState.stack.push({ type: {
22103
+ type: 524287
22104
+ /* Any */
22105
+ } });
22106
+ }
22107
+ const succState = liveInState.get(succBlock.offset);
22108
+ if (!succState) {
22109
+ liveInState.set(succBlock.offset, cloneState2(localState));
22110
+ return true;
22111
+ }
22112
+ if (!mergeInto(localState, succState))
22113
+ return false;
22114
+ if (interpLogging) {
22115
+ (0, import_chunk_HHQDDCTP.logger)("interp", 3, `Re-Merge to ${offsetToString(succBlock.offset)}`);
22116
+ }
22117
+ return true;
22118
+ }
22119
+ );
22120
+ if (interpLogging) {
22121
+ if ((0, import_chunk_HHQDDCTP.wouldLog)("interp", 5)) {
22122
+ (0, import_chunk_HHQDDCTP.setBanner)(
22123
+ functionBanner(func, context, "interp", (block, footer) => {
22124
+ if (footer)
22125
+ return "";
22126
+ return interpStateToString(liveInState.get(block.offset));
22127
+ })
22128
+ );
22129
+ } else {
22130
+ (0, import_chunk_HHQDDCTP.setBanner)(() => `=============== interp ${func.name} ==============`);
22131
+ }
22132
+ if (equivSets.size) {
22133
+ (0, import_chunk_HHQDDCTP.log)(`====== equivSets =====`);
22134
+ equivSets.forEach(
22135
+ (value2, key) => (0, import_chunk_HHQDDCTP.log)(
22136
+ `L${key.arg} === ${Array.from(value2).sort().join(" ")} ${key.lineNum ? lineInfoToString(key.lineNum, context) : ""}`
22137
+ )
22138
+ );
22139
+ }
22140
+ if (selfStores.size) {
22141
+ (0, import_chunk_HHQDDCTP.log)(`====== selfStores =====`);
22142
+ selfStores.forEach(
22143
+ (value2) => (0, import_chunk_HHQDDCTP.log)(`${bytecodeToString(value2, symbolTable)}`)
22144
+ );
22145
+ }
22146
+ if (replacements.size) {
22147
+ (0, import_chunk_HHQDDCTP.log)(`====== replacements =====`);
22148
+ replacements.forEach(
22149
+ (blockRep) => blockRep.forEach(
22150
+ (rep, bc) => (0, import_chunk_HHQDDCTP.log)(
22151
+ `${bytecodeToString(bc, symbolTable)} => ${rep.invert ? "~" : ""}${bytecodeToString(rep, symbolTable)} ${bc.lineNum ? lineInfoToString(bc.lineNum, context) : ""}`
22152
+ )
22153
+ )
22154
+ );
22155
+ }
22156
+ }
22157
+ selfStores.forEach((bc) => makeArgless(
22158
+ bc,
22159
+ 2
22160
+ /* popv */
22161
+ ));
22162
+ replacements.forEach((blockRep, block) => {
22163
+ for (let i = block.bytecodes.length; i--; ) {
22164
+ const orig = block.bytecodes[i];
22165
+ const rep = blockRep.get(orig);
22166
+ if (!rep)
22167
+ continue;
22168
+ orig.op = rep.op;
22169
+ if (rep.arg != null) {
22170
+ orig.arg = rep.arg;
22171
+ } else {
22172
+ delete orig.arg;
22173
+ }
22174
+ orig.size = rep.size;
22175
+ if (rep.invert) {
22176
+ const invv = { ...orig };
22177
+ invv.op = 45;
22178
+ invv.size = 1;
22179
+ invv.offset++;
22180
+ delete invv.arg;
22181
+ block.bytecodes.splice(i + 1, 0, invv);
22182
+ }
22183
+ }
22184
+ });
22185
+ if (interpLogging)
22186
+ (0, import_chunk_HHQDDCTP.setBanner)(null);
22187
+ return { liveInState, equivSets };
22188
+ }
22189
+ function instForType(type, offset) {
22190
+ if (!hasValue(type))
22191
+ return null;
22192
+ switch (type.type) {
22193
+ case 1:
22194
+ return { op: 44, offset, size: 1 };
22195
+ case 2:
22196
+ case 4:
22197
+ return {
22198
+ op: 43,
22199
+ arg: type.type === 2 ? 0 : 1,
22200
+ offset,
22201
+ size: 1
22202
+ };
22203
+ case 8:
22204
+ return { op: 37, arg: type.value, offset, size: 1 };
22205
+ case 16:
22206
+ return { op: 49, arg: type.value, offset, size: 1 };
22207
+ case 32:
22208
+ return { op: 38, arg: type.value, offset, size: 1 };
22209
+ case 64:
22210
+ return { op: 50, arg: type.value, offset, size: 1 };
22211
+ case 128:
22212
+ return {
22213
+ op: 52,
22214
+ arg: type.value.charCodeAt(0),
22215
+ offset,
22216
+ size: 1
22217
+ };
22218
+ case 131072: {
22219
+ const match = type.value.match(/<(\d+)>$/);
22220
+ (0, import_node_assert7.default)(match);
22221
+ return {
22222
+ op: 37,
22223
+ arg: parseInt(match[1], 10),
22224
+ offset,
22225
+ size: 1
22226
+ };
22227
+ }
22228
+ }
22229
+ return null;
22230
+ }
22231
+ var init_interp2 = (0, import_chunk_HHQDDCTP.__esm)({
22232
+ "src/readprg/interp.ts"() {
22233
+ "use strict";
22234
+ init_interp_binary();
22235
+ (0, import_chunk_HHQDDCTP.init_logger)();
22236
+ init_interp();
22237
+ init_types();
22238
+ init_union_type();
22239
+ init_bytecode();
22240
+ init_cflow();
22241
+ init_opcodes();
22242
+ }
22243
+ });
22244
+ function optimizeArrayInit(func, block, index, context, interpState) {
22245
+ (0, import_node_assert8.default)(
22246
+ block.bytecodes[index].op === 20 || block.bytecodes[index].op === 54
22247
+ /* newba */
22248
+ );
22249
+ if (!interpState) {
22250
+ interpState = cloneState2(null);
22251
+ }
22252
+ const putvStarts = [];
22253
+ let i;
22254
+ let initInst = null;
22255
+ let initType = null;
22256
+ let initLocal = null;
22257
+ let usedLocals = 0n;
22258
+ let local = -1;
22259
+ const depth = interpState.stack.length;
22260
+ for (i = index; ++i < block.bytecodes.length - 1; ) {
22261
+ const dup = block.bytecodes[i];
22262
+ if (dup.op !== 46 || dup.arg !== 0) {
22263
+ break;
22264
+ }
22265
+ interpBytecode(dup, interpState, context);
22266
+ const ipush = block.bytecodes[i + 1];
22267
+ interpBytecode(ipush, interpState, context);
22268
+ if (interpState.stack.length !== depth + 2 || interpState.stack[depth + 1].type.type !== 8 || interpState.stack[depth + 1].type.value !== putvStarts.length) {
22269
+ break;
22270
+ }
22271
+ let found = i;
22272
+ let thisInit = null;
22273
+ let thisLocal = null;
22274
+ for (let k = i + 1; ++k < block.bytecodes.length; ) {
22275
+ const bc = block.bytecodes[k];
22276
+ interpBytecode(bc, interpState, context);
22277
+ if (bc.op === 17 && interpState.stack.length === depth) {
22278
+ found = k;
22279
+ break;
22280
+ }
22281
+ if (bc.op === 18) {
22282
+ usedLocals |= 1n << BigInt(bc.arg);
22283
+ }
22284
+ const delta = interpState.stack.length - depth;
22285
+ if (delta === 3) {
22286
+ const t = interpState.stack[interpState.stack.length - 1].type;
22287
+ if (bc.op === 18) {
22288
+ thisLocal = bc;
22289
+ } else {
22290
+ thisLocal = null;
22291
+ }
22292
+ if (hasValue(t) && t.type & (1 | 6 | 120 | 128 | 256 | 131072)) {
22293
+ thisInit = t;
22294
+ continue;
22295
+ }
22296
+ thisInit = null;
22297
+ }
22298
+ if (delta < 0 || bc.op === 46 && bc.arg >= delta - 1) {
22299
+ break;
22300
+ }
22301
+ }
22302
+ if (found === i)
22303
+ break;
22304
+ if (found - i !== 3) {
22305
+ initLocal = initType = false;
22306
+ } else {
22307
+ if (initLocal !== false) {
22308
+ if (thisLocal) {
22309
+ if (initLocal == null) {
22310
+ initLocal = thisLocal;
22311
+ } else if (initLocal.arg !== thisLocal.arg && !interpState.locals[initLocal.arg]?.equivs?.has(thisLocal.arg)) {
22312
+ initLocal = false;
22313
+ }
22314
+ } else {
22315
+ initLocal = false;
22316
+ }
22317
+ }
22318
+ if (initType !== false) {
22319
+ if (thisInit) {
22320
+ if (initType == null) {
22321
+ initType = thisInit;
22322
+ } else if (initType.type !== thisInit.type || initType.value !== thisInit.value) {
22323
+ initType = false;
22324
+ }
22325
+ if (initType) {
22326
+ const bc = block.bytecodes[found - 1];
22327
+ if (bc.op !== 46 && (!initInst || initInst.size > bc.size)) {
22328
+ const { push, pop } = getOpInfo(bc);
22329
+ if (push === 1 && pop === 0) {
22330
+ initInst = bc;
22331
+ }
22332
+ }
22333
+ }
22334
+ } else {
22335
+ initType = false;
22336
+ }
22337
+ }
22338
+ }
22339
+ putvStarts.push(i);
22340
+ i = found;
22341
+ }
22342
+ if (initType && (block.bytecodes[index].op === 20 ? initType.type === 1 : initType.type === 8 && initType.value === 0)) {
22343
+ (0, import_chunk_HHQDDCTP.logger)(
22344
+ "array-init",
22345
+ 1,
22346
+ `${func.name}: Removing initialization of default initialized ${putvStarts.length} element array init at block ${offsetToString(
22347
+ block.offset
22348
+ )}, starting at index ${index}, at offset ${offsetToString(
22349
+ block.bytecodes[index].offset
22350
+ )}`
22351
+ );
22352
+ block.bytecodes.splice(index + 1, i - index - 1);
22353
+ return true;
22354
+ }
22355
+ const terminal = block.bytecodes[i];
22356
+ if (terminal?.op === 2) {
22357
+ const convertAputv = (i2) => {
22358
+ const bc = block.bytecodes[i2];
22359
+ const op = bc.op;
22360
+ bc.op = 2;
22361
+ bc.size = 1;
22362
+ delete bc.arg;
22363
+ (0, import_node_assert8.default)(
22364
+ op === 17
22365
+ /* aputv */
22366
+ );
22367
+ };
22368
+ convertAputv(i - 1);
22369
+ for (let i2 = putvStarts.length; i2--; ) {
22370
+ const offset = putvStarts[i2];
22371
+ block.bytecodes.splice(offset, 2);
22372
+ if (i2) {
22373
+ convertAputv(offset - 1);
22374
+ }
22375
+ }
22376
+ (0, import_chunk_HHQDDCTP.logger)(
22377
+ "array-init",
22378
+ 1,
22379
+ `${func.name}: Optimizing unused ${putvStarts.length} element array init at block ${offsetToString(
22380
+ block.offset
22381
+ )}, starting at index ${index}, at offset ${offsetToString(
22382
+ block.bytecodes[index].offset
22383
+ )}`
22384
+ );
22385
+ if ((0, import_chunk_HHQDDCTP.wouldLog)("array-init", 5)) {
22386
+ (0, import_chunk_HHQDDCTP.log)(blockToString(block, context));
22387
+ }
22388
+ return true;
22389
+ }
22390
+ const bytecode = (op, arg) => {
22391
+ const bc = { op, arg, size: opcodeSize(op), offset: context.nextOffset++ };
22392
+ if (arg == null)
22393
+ delete bc.arg;
22394
+ return bc;
22395
+ };
22396
+ const tryLocal = (n) => {
22397
+ if (local === -1 && terminal?.op === 19 && !(usedLocals >> BigInt(terminal.arg) & 1n) && putvStarts.length >= n) {
22398
+ local = terminal.arg;
22399
+ block.bytecodes.splice(i++, 1);
22400
+ block.bytecodes.splice(++index, 0, terminal);
22401
+ for (let ix = putvStarts.length; ix--; ) {
22402
+ const dupIx = ++putvStarts[ix];
22403
+ const dup = block.bytecodes[dupIx];
22404
+ (0, import_node_assert8.default)(dup.op === 46 && dup.arg === 0);
22405
+ block.bytecodes[dupIx].op = 18;
22406
+ block.bytecodes[dupIx].arg = local;
22407
+ }
22408
+ return true;
22409
+ }
22410
+ return false;
22411
+ };
22412
+ if (initType) {
22413
+ if (!initInst) {
22414
+ initInst = instForType(initType, block.bytecodes[index + 3].offset);
22415
+ }
22416
+ } else {
22417
+ initInst = null;
22418
+ }
22419
+ if (initLocal && (!initInst || initInst.size > initLocal.size)) {
22420
+ initInst = initLocal;
22421
+ }
22422
+ if (initInst) {
22423
+ if (putvStarts.length < 3)
22424
+ return false;
22425
+ tryLocal(3);
22426
+ (0, import_chunk_HHQDDCTP.logger)(
22427
+ "array-init",
22428
+ 1,
22429
+ `${func.name}: Optimizing ${putvStarts.length} element array init with constant initializer ${bytecodeToString(
22430
+ initInst,
22431
+ null
22432
+ )} at block ${offsetToString(
22433
+ block.offset
22434
+ )}, starting at index ${index}, at offset ${offsetToString(
22435
+ block.bytecodes[index].offset
22436
+ )}`
22437
+ );
22438
+ block.bytecodes.splice(index + 5, i - index - 5);
22439
+ block.bytecodes.splice(index + 1, 1);
22440
+ if (local < 0) {
22441
+ block.bytecodes[index + 1].op = 37;
22442
+ block.bytecodes[index + 1].arg = putvStarts.length;
22443
+ } else {
22444
+ if (index >= 2 && block.bytecodes[index - 2].op === 37 && block.bytecodes[index - 2].arg === putvStarts.length) {
22445
+ block.bytecodes.splice(index + 1, 1);
22446
+ block.bytecodes.splice(index - 1, 0, bytecode(46, 0));
22447
+ } else {
22448
+ block.bytecodes[index + 1].op = 37;
22449
+ block.bytecodes[index + 1].arg = putvStarts.length;
22450
+ }
22451
+ }
22452
+ (0, import_chunk_HHQDDCTP.logger)(
22453
+ "array-init",
22454
+ 5,
22455
+ `index: ${index}, i: ${i}
22456
+ ${blockToString(block, context)}`
22457
+ );
22458
+ const loopOffset2 = context.nextOffset;
22459
+ block.bytecodes.splice(
22460
+ index + 2,
22461
+ 1,
22462
+ bytecode(37, 1),
22463
+ bytecode(4, void 0),
22464
+ local >= 0 ? bytecode(18, local) : bytecode(46, 1),
22465
+ bytecode(46, 1),
22466
+ initInst
22467
+ );
22468
+ block.bytecodes.splice(
22469
+ index + 8,
22470
+ 0,
22471
+ bytecode(46, 0),
22472
+ bytecode(40, loopOffset2),
22473
+ bytecode(2, void 0)
22474
+ );
22475
+ splitBlock(func, block, index + 10);
22476
+ splitBlock(func, block, index + 2);
22477
+ const loop2 = func.blocks.get(loopOffset2);
22478
+ loop2.preds.add(loopOffset2);
22479
+ loop2.taken = loopOffset2;
22480
+ return true;
22481
+ }
22482
+ if (!tryLocal(3) && putvStarts.length < 4)
22483
+ return false;
22484
+ if (local >= 0) {
22485
+ block.bytecodes.splice(i, 0, bytecode(2, void 0));
22486
+ }
22487
+ for (let i2 = putvStarts.length; i2-- > 1; ) {
22488
+ block.bytecodes.splice(putvStarts[i2], 2);
22489
+ }
22490
+ (0, import_chunk_HHQDDCTP.logger)(
22491
+ "array-init",
22492
+ 1,
22493
+ `${func.name}: Optimizing ${putvStarts.length} element array init at block ${offsetToString(
22494
+ block.offset
22495
+ )}, starting at index ${index}, at offset ${offsetToString(
22496
+ block.bytecodes[index].offset
22497
+ )}`
22498
+ );
22499
+ let loopOffset;
22500
+ if (local >= 0) {
22501
+ block.bytecodes.splice(index + 1, 2);
22502
+ if (index >= 2 && block.bytecodes[index - 2].op === 37 && block.bytecodes[index - 2].arg === putvStarts.length) {
22503
+ block.bytecodes.splice(index - 1, 0, bytecode(46, 0));
22504
+ } else {
22505
+ block.bytecodes.splice(
22506
+ index + 1,
22507
+ 0,
22508
+ bytecode(37, putvStarts.length)
22509
+ );
22510
+ }
22511
+ index--;
22512
+ loopOffset = context.nextOffset;
22513
+ block.bytecodes.splice(
22514
+ index + 3,
22515
+ 0,
22516
+ bytecode(18, local),
22517
+ bytecode(46, 1),
22518
+ bytecode(37, 1),
22519
+ bytecode(4, void 0),
22520
+ bytecode(46, 0),
22521
+ bytecode(40, loopOffset)
22522
+ );
22523
+ } else {
22524
+ block.bytecodes[index + 2].op = 37;
22525
+ block.bytecodes[index + 2].arg = putvStarts.length - 1;
22526
+ loopOffset = context.nextOffset;
22527
+ block.bytecodes.splice(
22528
+ index + 3,
22529
+ 0,
22530
+ bytecode(46, 1),
22531
+ bytecode(46, 1),
22532
+ bytecode(37, 1),
22533
+ bytecode(4, void 0),
22534
+ bytecode(46, 0),
22535
+ bytecode(40, loopOffset)
22536
+ );
22537
+ }
22538
+ splitBlock(func, block, index + 9);
22539
+ splitBlock(func, block, index + 3);
22540
+ const loop = func.blocks.get(loopOffset);
22541
+ loop.preds.add(loopOffset);
22542
+ loop.taken = loopOffset;
22543
+ return true;
22544
+ }
22545
+ var init_array_init = (0, import_chunk_HHQDDCTP.__esm)({
22546
+ "src/readprg/array-init.ts"() {
22547
+ "use strict";
22548
+ init_types();
22549
+ (0, import_chunk_HHQDDCTP.init_util)();
22550
+ init_bytecode();
22551
+ init_interp2();
22552
+ init_opcodes();
22553
+ }
22554
+ });
22555
+ function localDCE(func, context) {
22556
+ if ((0, import_chunk_HHQDDCTP.wouldLog)("dce", 5)) {
22557
+ (0, import_chunk_HHQDDCTP.setBanner)(
22558
+ functionBanner(
22559
+ func,
22560
+ context,
22561
+ "local-dce-start",
22562
+ (block, footer) => footer ? `liveOutLocals: ${Array.from(
22563
+ liveOutLocals.get(block.offset) ?? []
22564
+ ).join(" ")}
22565
+ ` : ""
22566
+ )
22567
+ );
22568
+ }
22569
+ const { liveInLocals, liveOutLocals } = computeLiveLocals(func);
22570
+ let anyChanges = false;
22571
+ let changes = false;
22572
+ const makeNop = (bc) => {
22573
+ changes = true;
22574
+ makeArgless(
22575
+ bc,
22576
+ 0
22577
+ /* nop */
22578
+ );
22579
+ };
22580
+ const makePopv = (bc) => {
22581
+ changes = true;
22582
+ makeArgless(
22583
+ bc,
22584
+ 2
22585
+ /* popv */
22586
+ );
22587
+ };
22588
+ func.blocks.forEach((block) => {
22589
+ const reportPopv = (i, item, kill) => {
22590
+ (0, import_chunk_HHQDDCTP.logger)(
22591
+ "dce",
22592
+ 2,
22593
+ `${func.name}: Convert ${i}:${bytecodeToString(
22594
+ block.bytecodes[i],
22595
+ context.symbolTable
22596
+ )} to popv for ${item.deps.map(
22597
+ (i2) => `${i2}:${bytecodeToString(
22598
+ block.bytecodes[i2],
22599
+ context.symbolTable
22600
+ )}${kill ? "=>nop" : ""}`
22601
+ ).join(", ")} }`
22602
+ );
22603
+ };
22604
+ const reportNop = (item) => {
22605
+ (0, import_chunk_HHQDDCTP.logger)(
22606
+ "dce",
22607
+ 2,
22608
+ `${func.name}: Kill ${item.deps.map((i) => bytecodeToString(block.bytecodes[i], context.symbolTable)).join(", ")}`
22609
+ );
22610
+ };
22611
+ changes = false;
22612
+ const dceInfo = {
22613
+ stack: [],
22614
+ locals: new Set(liveOutLocals.get(block.offset))
22615
+ };
22616
+ for (let i = block.bytecodes.length; i--; ) {
22617
+ const bytecode = block.bytecodes[i];
22618
+ switch (bytecode.op) {
22619
+ case 19: {
22620
+ const liveLocal = dceInfo.locals.has(bytecode.arg);
22621
+ if (!liveLocal) {
22622
+ (0, import_chunk_HHQDDCTP.logger)(
22623
+ "dce",
22624
+ 2,
22625
+ `${func.name}: Killing store to unused local ${bytecode.arg} at ${offsetToString(block.offset)}:${i}`
22626
+ );
22627
+ makePopv(bytecode);
22628
+ dceInfo.stack.push({ dead: true, deps: [i] });
22629
+ } else {
22630
+ dceInfo.stack.push({ dead: false });
22631
+ }
22632
+ dceInfo.locals.delete(bytecode.arg);
22633
+ break;
22634
+ }
22635
+ case 2:
22636
+ dceInfo.stack.push({ dead: true, deps: [i] });
22637
+ break;
22638
+ case 46: {
22639
+ const item = dceInfo.stack.pop();
22640
+ if (item?.dead) {
22641
+ item.deps.push(i);
22642
+ reportNop(item);
22643
+ item.deps.forEach((index) => makeNop(block.bytecodes[index]));
22644
+ } else {
22645
+ if (dceInfo.stack.length > bytecode.arg) {
22646
+ dceInfo.stack[dceInfo.stack.length - 1 - bytecode.arg].dead = false;
22647
+ }
22648
+ }
22649
+ break;
22650
+ }
22651
+ case 18:
22652
+ case 44:
22653
+ case 43:
22654
+ case 24:
22655
+ case 37:
22656
+ case 38:
22657
+ case 39:
22658
+ case 52:
22659
+ case 49:
22660
+ case 50: {
22661
+ const item = dceInfo.stack.pop();
22662
+ if (item?.dead) {
22663
+ item.deps.push(i);
22664
+ reportNop(item);
22665
+ item.deps.forEach((index) => makeNop(block.bytecodes[index]));
22666
+ } else if (bytecode.op === 18) {
22667
+ dceInfo.locals.add(bytecode.arg);
22668
+ }
22669
+ break;
22670
+ }
22671
+ case 3:
22672
+ case 4:
22673
+ case 5:
22674
+ case 6:
22675
+ case 7:
22676
+ case 8:
22677
+ case 9:
22678
+ case 10:
22679
+ case 11:
22680
+ case 12:
22681
+ case 26:
22682
+ case 27:
22683
+ case 28:
22684
+ case 29:
22685
+ case 30:
22686
+ case 31:
22687
+ case 34:
22688
+ case 33:
22689
+ case 16:
22690
+ case 13: {
22691
+ const item = dceInfo.stack.pop();
22692
+ if (item?.dead) {
22693
+ reportPopv(i, item, false);
22694
+ makePopv(bytecode);
22695
+ dceInfo.stack.push({ dead: true, deps: item.deps.slice() });
22696
+ dceInfo.stack.push({ dead: true, deps: [i] });
22241
22697
  } else {
22242
- local = { type: {
22243
- type: 524287
22244
- /* Any */
22245
- } };
22698
+ dceInfo.stack.push({ dead: false });
22699
+ dceInfo.stack.push({ dead: false });
22246
22700
  }
22247
- localState.stack.push(local);
22248
- addEquiv2(localState, -localState.stack.length, bc.arg);
22249
22701
  break;
22250
22702
  }
22251
- case 19: {
22252
- selfStores.delete(bc);
22253
- equivSets.delete(bc);
22254
- let curItem = localState.locals[bc.arg];
22255
- if (!curItem) {
22256
- curItem = localState.locals[bc.arg] = {
22257
- type: {
22258
- type: 524287
22259
- /* Any */
22260
- }
22261
- };
22262
- }
22263
- const { dup: _dup, ...value2 } = localState.stack[localState.stack.length - 1];
22264
- if (value2.equivs) {
22265
- if (curItem.equivs?.has(-localState.stack.length)) {
22266
- selfStores.add(bc);
22267
- removeEquiv2(localState, -localState.stack.length);
22268
- localState.stack.pop();
22269
- break;
22270
- }
22271
- removeEquiv2(localState, bc.arg);
22272
- addEquiv2(localState, bc.arg, -localState.stack.length);
22273
- removeEquiv2(localState, -localState.stack.length);
22274
- if (curItem.equivs) {
22275
- equivSets.set(
22276
- bc,
22277
- new Set(Array.from(curItem.equivs).filter((e) => e >= 0))
22278
- );
22279
- }
22280
- } else if (curItem.equivs) {
22281
- removeEquiv2(localState, bc.arg);
22703
+ case 21:
22704
+ case 32:
22705
+ case 45:
22706
+ case 48:
22707
+ case 20:
22708
+ case 54:
22709
+ case 47: {
22710
+ const item = dceInfo.stack.pop();
22711
+ if (item?.dead) {
22712
+ reportPopv(i, item, true);
22713
+ makePopv(bytecode);
22714
+ item.deps.forEach((index) => makeNop(block.bytecodes[index]));
22715
+ dceInfo.stack.push({ dead: true, deps: [i] });
22716
+ } else {
22717
+ dceInfo.stack.push({ dead: false });
22282
22718
  }
22283
- localState.stack.pop();
22284
- localState.locals[bc.arg].type = value2.type;
22285
- break;
22286
- }
22287
- case 37:
22288
- xpush(localState, block, bc, 8, bc.arg);
22289
- break;
22290
- case 49:
22291
- xpush(localState, block, bc, 16, bc.arg);
22292
- break;
22293
- case 38:
22294
- xpush(localState, block, bc, 32, roundToFloat(bc.arg));
22295
- break;
22296
- case 50:
22297
- xpush(localState, block, bc, 64, bc.arg);
22298
- break;
22299
- case 52:
22300
- xpush(
22301
- localState,
22302
- block,
22303
- bc,
22304
- 128,
22305
- String.fromCharCode(bc.arg)
22306
- );
22307
- break;
22308
- case 43:
22309
- xpush(
22310
- localState,
22311
- block,
22312
- bc,
22313
- bc.arg ? 4 : 2
22314
- /* False */
22315
- );
22316
- break;
22317
- case 44:
22318
- xpush(
22319
- localState,
22320
- block,
22321
- bc,
22322
- 1
22323
- /* Null */
22324
- );
22325
- break;
22326
- case 39: {
22327
- const argSym = symbolTable?.symbolToLabelMap.get(bc.arg);
22328
- const value2 = argSym && symbolTable?.symbols.get(argSym)?.str || `symbol<${bc.arg}>`;
22329
- xpush(localState, block, bc, 131072, value2);
22330
22719
  break;
22331
22720
  }
22332
- default: {
22333
- const { pop, push } = getOpInfo(bc);
22334
- for (let i = 0; i < pop; i++) {
22335
- removeEquiv2(localState, -localState.stack.length);
22336
- localState.stack.pop();
22721
+ case 51:
22722
+ case 15:
22723
+ if (block.exsucc) {
22724
+ liveInLocals.get(block.exsucc)?.forEach((local) => dceInfo.locals.add(local));
22337
22725
  }
22338
- for (let i = 0; i < push; i++) {
22339
- localState.stack.push({ type: {
22340
- type: 524287
22341
- /* Any */
22342
- } });
22726
+ default: {
22727
+ let { push, pop } = getOpInfo(bytecode);
22728
+ while (push-- > 0) {
22729
+ dceInfo.stack.pop();
22343
22730
  }
22344
- break;
22345
- }
22346
- }
22347
- },
22348
- () => {
22349
- },
22350
- (from, localState, succBlock, isExSucc) => {
22351
- if (isExSucc) {
22352
- const tryEntry = from.try[from.try.length - 1].tryStart;
22353
- const entryDepth = liveInState.get(tryEntry)?.stack.length ?? 0;
22354
- localState = cloneState2(localState);
22355
- while (entryDepth < localState.stack.length) {
22356
- if (localState.stack[localState.stack.length - 1].equivs) {
22357
- removeEquiv2(localState, -localState.stack.length);
22731
+ while (pop-- > 0) {
22732
+ dceInfo.stack.push({ dead: false });
22358
22733
  }
22359
- localState.stack.pop();
22360
22734
  }
22361
- localState.stack.push({ type: {
22362
- type: 524287
22363
- /* Any */
22364
- } });
22365
- }
22366
- const succState = liveInState.get(succBlock.offset);
22367
- if (!succState) {
22368
- liveInState.set(succBlock.offset, cloneState2(localState));
22369
- return true;
22370
- }
22371
- if (!mergeInto(localState, succState))
22372
- return false;
22373
- if (interpLogging) {
22374
- (0, import_chunk_HHQDDCTP.logger)("interp", 3, `Re-Merge to ${offsetToString(succBlock.offset)}`);
22375
22735
  }
22376
- return true;
22377
- }
22378
- );
22379
- if (interpLogging) {
22380
- if ((0, import_chunk_HHQDDCTP.wouldLog)("interp", 5)) {
22381
- (0, import_chunk_HHQDDCTP.setBanner)(
22382
- functionBanner(func, context, "interp", (block, footer) => {
22383
- if (footer)
22384
- return "";
22385
- return interpStateToString(liveInState.get(block.offset));
22386
- })
22387
- );
22388
- } else {
22389
- (0, import_chunk_HHQDDCTP.setBanner)(() => `=============== interp ${func.name} ==============`);
22390
- }
22391
- if (equivSets.size) {
22392
- (0, import_chunk_HHQDDCTP.log)(`====== equivSets =====`);
22393
- equivSets.forEach(
22394
- (value2, key) => (0, import_chunk_HHQDDCTP.log)(
22395
- `L${key.arg} === ${Array.from(value2).sort().join(" ")} ${key.lineNum ? lineInfoToString(key.lineNum, context) : ""}`
22396
- )
22397
- );
22398
- }
22399
- if (selfStores.size) {
22400
- (0, import_chunk_HHQDDCTP.log)(`====== selfStores =====`);
22401
- selfStores.forEach(
22402
- (value2) => (0, import_chunk_HHQDDCTP.log)(`${bytecodeToString(value2, symbolTable)}`)
22403
- );
22404
22736
  }
22405
- if (replacements.size) {
22406
- (0, import_chunk_HHQDDCTP.log)(`====== replacements =====`);
22407
- replacements.forEach(
22408
- (blockRep) => blockRep.forEach(
22409
- (rep, bc) => (0, import_chunk_HHQDDCTP.log)(
22410
- `${bytecodeToString(bc, symbolTable)} => ${rep.invert ? "~" : ""}${bytecodeToString(rep, symbolTable)} ${bc.lineNum ? lineInfoToString(bc.lineNum, context) : ""}`
22411
- )
22412
- )
22737
+ if (changes) {
22738
+ anyChanges = true;
22739
+ block.bytecodes = block.bytecodes.filter(
22740
+ (bc) => bc.op !== 0
22741
+ /* nop */
22413
22742
  );
22743
+ if ((0, import_chunk_HHQDDCTP.wouldLog)("dce", 3)) {
22744
+ (0, import_chunk_HHQDDCTP.log)(functionBanner(func, context, "local-dce-end")());
22745
+ }
22414
22746
  }
22415
- }
22416
- selfStores.forEach((bc) => makeArgless(
22417
- bc,
22418
- 2
22419
- /* popv */
22420
- ));
22421
- replacements.forEach((blockRep, block) => {
22422
- for (let i = block.bytecodes.length; i--; ) {
22423
- const orig = block.bytecodes[i];
22424
- const rep = blockRep.get(orig);
22425
- if (!rep)
22426
- continue;
22427
- orig.op = rep.op;
22428
- if (rep.arg != null) {
22429
- orig.arg = rep.arg;
22430
- } else {
22431
- delete orig.arg;
22747
+ });
22748
+ (0, import_chunk_HHQDDCTP.setBanner)(null);
22749
+ return anyChanges;
22750
+ }
22751
+ function computeLiveLocals(func) {
22752
+ const liveOutLocals = /* @__PURE__ */ new Map();
22753
+ const liveInLocals = /* @__PURE__ */ new Map();
22754
+ postOrderPropagate(
22755
+ func,
22756
+ (block) => new Set(liveOutLocals.get(block.offset)),
22757
+ (block, bc, locals) => {
22758
+ switch (bc.op) {
22759
+ case 18:
22760
+ locals.add(bc.arg);
22761
+ break;
22762
+ case 19:
22763
+ locals.delete(bc.arg);
22764
+ break;
22765
+ case 51:
22766
+ case 15:
22767
+ if (block.exsucc) {
22768
+ liveInLocals.get(block.exsucc)?.forEach((local) => locals.add(local));
22769
+ }
22770
+ break;
22432
22771
  }
22433
- orig.size = rep.size;
22434
- if (rep.invert) {
22435
- const invv = { ...orig };
22436
- invv.op = 45;
22437
- invv.size = 1;
22438
- invv.offset++;
22439
- delete invv.arg;
22440
- block.bytecodes.splice(i + 1, 0, invv);
22772
+ },
22773
+ (block, locals) => {
22774
+ liveInLocals.set(block.offset, locals);
22775
+ },
22776
+ (locals, predBlock, isExPred) => {
22777
+ if (isExPred)
22778
+ return false;
22779
+ const predLocals = liveOutLocals.get(predBlock.offset);
22780
+ if (!predLocals) {
22781
+ liveOutLocals.set(predBlock.offset, new Set(locals));
22782
+ return true;
22441
22783
  }
22784
+ const size = predLocals.size;
22785
+ locals.forEach((local) => predLocals.add(local));
22786
+ return size !== predLocals.size;
22442
22787
  }
22443
- });
22444
- if (interpLogging)
22445
- (0, import_chunk_HHQDDCTP.setBanner)(null);
22446
- return { liveInState, equivSets };
22788
+ );
22789
+ return { liveInLocals, liveOutLocals };
22447
22790
  }
22448
- var init_interp2 = (0, import_chunk_HHQDDCTP.__esm)({
22449
- "src/readprg/interp.ts"() {
22791
+ var init_dce = (0, import_chunk_HHQDDCTP.__esm)({
22792
+ "src/readprg/dce.ts"() {
22450
22793
  "use strict";
22451
- (0, import_chunk_HHQDDCTP.init_logger)();
22452
- init_interp();
22453
- init_types();
22454
- init_union_type();
22794
+ (0, import_chunk_HHQDDCTP.init_util)();
22455
22795
  init_bytecode();
22456
22796
  init_cflow();
22457
22797
  init_opcodes();
@@ -22664,21 +23004,44 @@ var init_sharing = (0, import_chunk_HHQDDCTP.__esm)({
22664
23004
  });
22665
23005
  function optimizeFunc(func, context) {
22666
23006
  let changes;
23007
+ let liveInState;
22667
23008
  do {
22668
23009
  cleanCfg2(func, context);
22669
23010
  changes = localDCE(func, context);
22670
23011
  changes = blockSharing(func, context) || changes;
22671
23012
  changes = simpleOpts(func, context) || changes;
22672
- interpFunc(func, context);
23013
+ ({ liveInState } = interpFunc(func, context));
23014
+ changes = doArrayInits(func, liveInState, context) || changes;
22673
23015
  } while (changes);
23016
+ }
23017
+ function doArrayInits(func, liveInState, context) {
23018
+ const newAToProcess = /* @__PURE__ */ new Map();
22674
23019
  func.blocks.forEach((block) => {
22675
- for (let i = block.bytecodes.length; i--; ) {
22676
- const cur = block.bytecodes[i];
22677
- if (cur.op === 20) {
22678
- optimizeArrayInit(func, block, i, false, context);
22679
- }
23020
+ if (!block.bytecodes.some(
23021
+ (bc) => bc.op === 20 || bc.op === 54
23022
+ /* newba */
23023
+ )) {
23024
+ return;
22680
23025
  }
23026
+ const blockState = cloneState2(liveInState.get(block.offset));
23027
+ const newAStates = /* @__PURE__ */ new Map();
23028
+ block.bytecodes.forEach((bc, index) => {
23029
+ interpBytecode(bc, blockState, context);
23030
+ if (bc.op === 20 || bc.op === 54) {
23031
+ newAStates.set(index, cloneState2(blockState));
23032
+ }
23033
+ });
23034
+ newAToProcess.set(block, newAStates);
22681
23035
  });
23036
+ let changes = false;
23037
+ newAToProcess.forEach(
23038
+ (newAStates, block) => Array.from(newAStates.keys()).reverse().forEach((i) => {
23039
+ if (optimizeArrayInit(func, block, i, context, newAStates.get(i))) {
23040
+ changes = true;
23041
+ }
23042
+ })
23043
+ );
23044
+ return changes;
22682
23045
  }
22683
23046
  function simpleOpts(func, context) {
22684
23047
  const equalsSym = 8388787;
@@ -22686,24 +23049,14 @@ function simpleOpts(func, context) {
22686
23049
  return Array.from(func.blocks.values()).reduce((changes, block) => {
22687
23050
  for (let i = block.bytecodes.length; i--; ) {
22688
23051
  const cur = block.bytecodes[i];
22689
- if (cur.op === 0) {
23052
+ if (cur.op === 0 || context.config.removeArgc && cur.op === 53) {
22690
23053
  block.bytecodes.splice(i, 1);
22691
23054
  changes = true;
22692
23055
  if (logging3) {
22693
- (0, import_chunk_HHQDDCTP.log)(`${func.name}: deleting nop`);
22694
- if (i > 0) {
22695
- (0, import_chunk_HHQDDCTP.log)(
22696
- ` - previous bytecode was ${bytecodeToString(
22697
- block.bytecodes[i - 1],
22698
- null
22699
- )}`
22700
- );
22701
- }
23056
+ (0, import_chunk_HHQDDCTP.log)(`${func.name}: deleting ${bytecodeToString(cur, null)}`);
22702
23057
  }
22703
23058
  } else if (i && cur.op === 39 && cur.arg === equalsSym) {
22704
23059
  changes = equalSymbolToEq(block, i) || changes;
22705
- } else if (cur.op === 20) {
22706
- changes = optimizeArrayInit(func, block, i, true, context) || changes;
22707
23060
  } else if (i && cur.op === 10) {
22708
23061
  const prev = block.bytecodes[i - 1];
22709
23062
  if (prev.op === 37 || prev.op === 49) {
@@ -22840,7 +23193,7 @@ function cleanCfg2(func, context) {
22840
23193
  }
22841
23194
  const deadBlocks = /* @__PURE__ */ new Map();
22842
23195
  func.blocks.forEach((block) => {
22843
- if (isNopBlock(block)) {
23196
+ if (isNopBlock(block) && block.offset !== func.offset) {
22844
23197
  deadBlocks.set(block.offset, block.next);
22845
23198
  }
22846
23199
  });
@@ -22878,8 +23231,9 @@ function cleanCfg2(func, context) {
22878
23231
  removeBlock(func, offset);
22879
23232
  });
22880
23233
  }
23234
+ const deadCatches = /* @__PURE__ */ new Set();
22881
23235
  func.blocks.forEach((block) => {
22882
- if (block.next && !block.taken && block.next !== block.offset) {
23236
+ if (block.next && !block.taken && block.next !== block.offset && block.next !== func.offset) {
22883
23237
  const next = func.blocks.get(block.next);
22884
23238
  if (block.try === next.try) {
22885
23239
  if (next.preds.size === 1) {
@@ -22962,6 +23316,7 @@ function cleanCfg2(func, context) {
22962
23316
  )} with handler at ${handler}`
22963
23317
  );
22964
23318
  block.try.splice(i, 1);
23319
+ deadCatches.add(handler);
22965
23320
  }
22966
23321
  }
22967
23322
  if (!block.try.length) {
@@ -22969,6 +23324,29 @@ function cleanCfg2(func, context) {
22969
23324
  }
22970
23325
  }
22971
23326
  });
23327
+ deadCatches.forEach((cb) => {
23328
+ const todo = [cb];
23329
+ for (let i = 0; i < todo.length; i++) {
23330
+ const offset = todo[i];
23331
+ const block = func.blocks.get(offset);
23332
+ if (!block) {
23333
+ todo.splice(i--, 1);
23334
+ continue;
23335
+ }
23336
+ if (block.preds?.size) {
23337
+ continue;
23338
+ }
23339
+ if (block.next != null)
23340
+ todo.push(block.next);
23341
+ if (block.taken != null)
23342
+ todo.push(block.taken);
23343
+ if (block.exsucc != null)
23344
+ todo.push(block.exsucc);
23345
+ removeBlock(func, offset);
23346
+ todo.splice(i, 1);
23347
+ i = -1;
23348
+ }
23349
+ });
22972
23350
  (0, import_chunk_HHQDDCTP.setBanner)(null);
22973
23351
  }
22974
23352
  var init_optimize2 = (0, import_chunk_HHQDDCTP.__esm)({
@@ -28536,7 +28914,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
28536
28914
  const opt_time = await (0, import_chunk_HHQDDCTP.first_modified)(
28537
28915
  Object.values(fnMap).map((v) => v.output)
28538
28916
  );
28539
- if (source_time < opt_time && 1679602245113 < opt_time) {
28917
+ if (source_time < opt_time && 1680119325581 < opt_time) {
28540
28918
  return { hasTests, diagnostics: prevDiagnostics };
28541
28919
  }
28542
28920
  }
@@ -28568,7 +28946,7 @@ async function generateOneConfig(buildConfig, manifestXML, dependencyFiles, conf
28568
28946
  JSON.stringify({
28569
28947
  hasTests: hasTests2,
28570
28948
  diagnostics,
28571
- optimizerVersion: "1.1.21",
28949
+ optimizerVersion: "1.1.22",
28572
28950
  ...Object.fromEntries(
28573
28951
  configOptionsToCheck.map((option) => [option, config[option]])
28574
28952
  )
@@ -28806,7 +29184,8 @@ var init_worker_task = (0, import_chunk_HHQDDCTP.__esm)({
28806
29184
  data.xmlBuffer,
28807
29185
  data.xmlOffset,
28808
29186
  data.xmlLength,
28809
- data.key
29187
+ data.key,
29188
+ data.config
28810
29189
  );
28811
29190
  }
28812
29191
  };
@@ -28951,9 +29330,9 @@ function readPrgWithOffsets(view) {
28951
29330
  }
28952
29331
  return { view, sections };
28953
29332
  }
28954
- async function optimizeProgram(filepath, devKey, output) {
29333
+ async function optimizeProgram(filepath, devKey, output, config) {
28955
29334
  if (/\.iq$/i.test(filepath)) {
28956
- return optimizePackage(filepath, devKey, output);
29335
+ return optimizePackage(filepath, devKey, output, config);
28957
29336
  }
28958
29337
  const removeExt = (filepath2, ext) => path5.join(path5.dirname(filepath2), path5.basename(filepath2, ext));
28959
29338
  if (!output) {
@@ -28967,7 +29346,8 @@ async function optimizeProgram(filepath, devKey, output) {
28967
29346
  filepath,
28968
29347
  view,
28969
29348
  debugXml,
28970
- key
29349
+ key,
29350
+ config
28971
29351
  );
28972
29352
  const promises = [];
28973
29353
  promises.push(fs7.writeFile(output, buffer));
@@ -28986,7 +29366,7 @@ async function optimizeProgram(filepath, devKey, output) {
28986
29366
  await Promise.all(promises);
28987
29367
  return { signature, output };
28988
29368
  }
28989
- function optimizeProgramBuffer(filepath, view, debugXml, key) {
29369
+ function optimizeProgramBuffer(filepath, view, debugXml, key, config) {
28990
29370
  const { sections } = readPrgWithOffsets(view);
28991
29371
  (0, import_chunk_HHQDDCTP.logger)("readprg", 5, sections);
28992
29372
  const symbolTable = new SymbolTable();
@@ -29017,7 +29397,10 @@ function optimizeProgramBuffer(filepath, view, debugXml, key) {
29017
29397
  -1059145026
29018
29398
  /* TEXT */
29019
29399
  ].view, lineTable);
29400
+ if (!config)
29401
+ config = {};
29020
29402
  const context = {
29403
+ config,
29021
29404
  filepath,
29022
29405
  sections,
29023
29406
  bytecodes,
@@ -29041,7 +29424,7 @@ function optimizeProgramBuffer(filepath, view, debugXml, key) {
29041
29424
  buffers.push(Buffer.from(new ArrayBuffer(8)));
29042
29425
  return { signature, buffer: Buffer.concat(buffers) };
29043
29426
  }
29044
- function optimizePackage(filepath, devKey, output) {
29427
+ function optimizePackage(filepath, devKey, output, config) {
29045
29428
  if (!devKey) {
29046
29429
  throw new Error(`Can't sign ${filepath} without a developer key`);
29047
29430
  }
@@ -29081,7 +29464,8 @@ function optimizePackage(filepath, devKey, output) {
29081
29464
  xmlBuffer: xmlBuffer.buffer,
29082
29465
  xmlOffset: xmlBuffer.byteOffset,
29083
29466
  xmlLength: xmlBuffer.byteLength,
29084
- key
29467
+ key,
29468
+ config
29085
29469
  }
29086
29470
  }).then(
29087
29471
  ({
@@ -29232,7 +29616,7 @@ function optimizePackage(filepath, devKey, output) {
29232
29616
  })
29233
29617
  ).finally(() => poolStarted && stopPool());
29234
29618
  }
29235
- function optimizePrgAndDebug(prgName, prgBuffer, prgOffset, prgLength, xmlName, xmlBuffer, xmlOffset, xmlLength, key) {
29619
+ function optimizePrgAndDebug(prgName, prgBuffer, prgOffset, prgLength, xmlName, xmlBuffer, xmlOffset, xmlLength, key, config) {
29236
29620
  const xmlString = Buffer.from(xmlBuffer, xmlOffset, xmlLength).toString();
29237
29621
  const debugXml = xml_util_exports.parseXml(xmlString, xmlName);
29238
29622
  if (debugXml.body instanceof Error) {
@@ -29242,7 +29626,8 @@ function optimizePrgAndDebug(prgName, prgBuffer, prgOffset, prgLength, xmlName,
29242
29626
  prgName,
29243
29627
  new DataView(prgBuffer, prgOffset, prgLength),
29244
29628
  debugXml,
29245
- key
29629
+ key,
29630
+ config
29246
29631
  );
29247
29632
  return Promise.resolve({
29248
29633
  sigBuffer: result.signature?.buffer,