@markw65/monkeyc-optimizer 1.0.29 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -330,3 +330,16 @@ More fixes found via open source projects.
330
330
  - Actually run the rest of the expected-to-crash tests
331
331
  - Better error messages from pragma checker
332
332
  - Better regex for filtering projects
333
+
334
+ ### 1.0.30
335
+
336
+ - Less greedy approach to finding candidate sets
337
+ - slightly better size reduction when globals maybe modified
338
+ - Fix the control flow after the test of a while loop
339
+ - one of the edges was in the wrong place, leading to suboptimal solutions in some cases
340
+
341
+ Bug Fixes
342
+
343
+ - Fix a bug that could lead to the optimizer never completing
344
+ - Fix a bug that prevented inlining functions that ended in a BlockStatement
345
+ - Fix a bug that could cause nested inlined functions inlined in declarations to not be removed
package/build/api.cjs CHANGED
@@ -928,6 +928,10 @@ function inlineWithArgs(state, func, call, context) {
928
928
  if (!func.node || !func.node.body) {
929
929
  return null;
930
930
  }
931
+ const lastStmt = (block) => {
932
+ const last = block.body.slice(-1)[0];
933
+ return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
934
+ };
931
935
  let retStmtCount = 0;
932
936
  if (context.type === "ReturnStatement") {
933
937
  const last = func.node.body.body.slice(-1)[0];
@@ -950,7 +954,7 @@ function inlineWithArgs(state, func, call, context) {
950
954
  return null;
951
955
  }
952
956
  if (retStmtCount === 1) {
953
- const last = func.node.body.body.slice(-1)[0];
957
+ const [last] = lastStmt(func.node.body);
954
958
  if (!last ||
955
959
  last.type !== "ReturnStatement" ||
956
960
  ((context.type === "AssignmentExpression" ||
@@ -975,21 +979,21 @@ function inlineWithArgs(state, func, call, context) {
975
979
  }
976
980
  inliner_diagnostic(state, call.loc, null);
977
981
  if (context.type !== "ReturnStatement" && retStmtCount) {
978
- const last = body.body[body.body.length - 1];
982
+ const [last, block] = lastStmt(body);
979
983
  if (last.type != "ReturnStatement") {
980
984
  throw new Error("ReturnStatement got lost!");
981
985
  }
982
986
  if (last.argument) {
983
987
  if (context.type === "AssignmentExpression") {
984
988
  context.right = last.argument;
985
- body.body[body.body.length - 1] = {
989
+ block.body[block.body.length - 1] = {
986
990
  type: "ExpressionStatement",
987
991
  expression: context,
988
992
  };
989
993
  }
990
994
  else if (context.type === "VariableDeclarator") {
991
995
  const { id, init: _init, kind: _kind, ...rest } = context;
992
- body.body[body.body.length - 1] = {
996
+ block.body[block.body.length - 1] = {
993
997
  ...rest,
994
998
  type: "ExpressionStatement",
995
999
  expression: {
@@ -1003,11 +1007,11 @@ function inlineWithArgs(state, func, call, context) {
1003
1007
  }
1004
1008
  else {
1005
1009
  const side_exprs = inliner_unused(last.argument);
1006
- body.body.splice(body.body.length - 1, 1, ...side_exprs);
1010
+ block.body.splice(block.body.length - 1, 1, ...side_exprs);
1007
1011
  }
1008
1012
  }
1009
1013
  else {
1010
- --body.body.length;
1014
+ --block.body.length;
1011
1015
  }
1012
1016
  }
1013
1017
  return body;
@@ -1271,7 +1275,8 @@ function control_flow_buildReducedGraph(state, func, notice) {
1271
1275
  if (node.type === "WhileStatement") {
1272
1276
  head = localState.newBlock(top.continue);
1273
1277
  state.traverse(node.test);
1274
- localState.addEdge(localState.newBlock(), top.break);
1278
+ localState.addEdge(localState.curBlock, top.break);
1279
+ localState.newBlock();
1275
1280
  }
1276
1281
  else {
1277
1282
  head = localState.newBlock();
@@ -1592,6 +1597,8 @@ function cleanCfg(head) {
1592
1597
  }
1593
1598
  }));
1594
1599
  }
1600
+ if (!cur.node)
1601
+ cur.node = succ.node;
1595
1602
  }
1596
1603
  }
1597
1604
  });
@@ -1690,6 +1697,20 @@ function declName(decl) {
1690
1697
  throw new Error(`Unexpected EventDecl type: ${decl.type}`);
1691
1698
  }
1692
1699
  }
1700
+ function logAntState(s, decl) {
1701
+ const defs = Array.from(s.ant).reduce((defs, event) => {
1702
+ if (event.type === "def" || event.type === "mod")
1703
+ defs++;
1704
+ return defs;
1705
+ }, 0);
1706
+ console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
1707
+ console.log(` - members: ${Array.from(s.members)
1708
+ .map(([block, live]) => block.order + (live ? "t" : "f"))
1709
+ .join(", ")}`);
1710
+ }
1711
+ function logAntDecls(antDecls) {
1712
+ antDecls.forEach(logAntState);
1713
+ }
1693
1714
  function pre_sizeBasedPRE(state, func) {
1694
1715
  if (!func.node.body)
1695
1716
  return;
@@ -1704,14 +1725,7 @@ function pre_sizeBasedPRE(state, func) {
1704
1725
  if (candidates) {
1705
1726
  if (logging) {
1706
1727
  console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
1707
- candidates.forEach((s, decl) => {
1708
- const defs = Array.from(s.ant).reduce((defs, event) => {
1709
- if (event.type === "def")
1710
- defs++;
1711
- return defs;
1712
- }, 0);
1713
- console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live`);
1714
- });
1728
+ logAntDecls(candidates);
1715
1729
  }
1716
1730
  const nodeMap = new Map();
1717
1731
  const declMap = new Map();
@@ -1957,21 +1971,28 @@ function cloneAnticipatedState(as) {
1957
1971
  members: new Map(as.members),
1958
1972
  };
1959
1973
  }
1974
+ function mergeAnticipatedState(ae, be) {
1975
+ mergeSet(ae.ant, be.ant);
1976
+ be.members.forEach((live, block) => ae.members.set(block, live));
1977
+ if (be.live)
1978
+ ae.live = true;
1979
+ }
1960
1980
  function cloneAnticipatedDecls(ad) {
1961
1981
  const copy = anticipatedDecls();
1962
1982
  for (const [k, v] of ad) {
1963
- copy.set(k, cloneAnticipatedState(v));
1983
+ if (!v.isIsolated) {
1984
+ copy.set(k, cloneAnticipatedState(v));
1985
+ }
1964
1986
  }
1965
1987
  return copy;
1966
1988
  }
1967
1989
  function mergeAnticipatedDecls(a, b) {
1968
1990
  for (const [k, v] of b) {
1991
+ if (v.isIsolated)
1992
+ continue;
1969
1993
  const ae = a.get(k);
1970
1994
  if (ae) {
1971
- mergeSet(ae.ant, v.ant);
1972
- v.members.forEach((live, block) => ae.members.set(block, live));
1973
- if (v.live)
1974
- ae.live = true;
1995
+ mergeAnticipatedState(ae, v);
1975
1996
  }
1976
1997
  else {
1977
1998
  a.set(k, cloneAnticipatedState(v));
@@ -1985,6 +2006,7 @@ function equalStates(a, b) {
1985
2006
  const be = b.get(k);
1986
2007
  if (!be ||
1987
2008
  be.live != ae.live ||
2009
+ be.isIsolated != ae.isIsolated ||
1988
2010
  !equalSet(ae.ant, be.ant) ||
1989
2011
  !equalMap(ae.members, be.members)) {
1990
2012
  return false;
@@ -2055,7 +2077,8 @@ function candidateCost(candState) {
2055
2077
  cost += defCost(candState.node);
2056
2078
  }
2057
2079
  });
2058
- cost += defCost(candState.node) * candidateBoundary(candState).size;
2080
+ const boundarySize = candidateBoundary(candState).size;
2081
+ cost += defCost(candState.node) * boundarySize;
2059
2082
  return cost;
2060
2083
  }
2061
2084
  function computeAttributes(head) {
@@ -2193,9 +2216,7 @@ function computeAttributes(head) {
2193
2216
  if (isUpdate || candidates.live) {
2194
2217
  candidates.ant.add(event);
2195
2218
  }
2196
- if (!isUpdate) {
2197
- candidates.live = false;
2198
- }
2219
+ candidates.live = isUpdate;
2199
2220
  break;
2200
2221
  }
2201
2222
  }
@@ -2204,12 +2225,23 @@ function computeAttributes(head) {
2204
2225
  curState.forEach((antState) => {
2205
2226
  antState.head = top;
2206
2227
  antState.members.set(top, antState.live);
2228
+ if (!antState.live && candidateBoundary(antState).size === 0) {
2229
+ // we found a group that's isolated from the rest
2230
+ // of the function. Don't merge it with earlier
2231
+ // refs and defs, because we can take it or leave
2232
+ // it based on its own cost.
2233
+ antState.isIsolated = true;
2234
+ }
2207
2235
  });
2208
2236
  const oldState = blockStates[top.order];
2209
2237
  if (oldState && equalStates(oldState, curState)) {
2210
2238
  continue;
2211
2239
  }
2212
2240
  blockStates[top.order] = curState;
2241
+ if (logging) {
2242
+ console.log(`Updated block ${top.order}`);
2243
+ logAntDecls(curState);
2244
+ }
2213
2245
  if (top.preds) {
2214
2246
  top.preds.forEach((pred) => enqueue(pred));
2215
2247
  }
@@ -2222,7 +2254,9 @@ function computeAttributes(head) {
2222
2254
  if (cost >= 0)
2223
2255
  return;
2224
2256
  const existing = candidateDecls.get(decl);
2225
- if (!existing || candidateCost(existing) > cost) {
2257
+ if (!existing ||
2258
+ existing.isIsolated ||
2259
+ candidateCost(existing) > cost) {
2226
2260
  const boundary = candidateBoundary(events);
2227
2261
  if (!Array.from(boundary).every((block) => {
2228
2262
  if (block !== events.head && block.events) {
@@ -2262,7 +2296,11 @@ function computeAttributes(head) {
2262
2296
  return;
2263
2297
  }
2264
2298
  events.live = false;
2265
- if (candidateCost(events) != cost) {
2299
+ if (existing && existing.isIsolated) {
2300
+ delete existing.isIsolated;
2301
+ mergeAnticipatedState(events, existing);
2302
+ }
2303
+ else if (candidateCost(events) != cost) {
2266
2304
  throw new Error(`cost of block ${i} changed`);
2267
2305
  }
2268
2306
  candidateDecls.set(decl, events);
@@ -3444,10 +3482,10 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
3444
3482
  while (i < node.declarations.length) {
3445
3483
  const decl = declarations[i++];
3446
3484
  if (decl.init && decl.init.type === "CallExpression") {
3447
- const inlined = optimizeCall(state, decl.init, decl);
3485
+ const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
3448
3486
  if (!inlined)
3449
3487
  continue;
3450
- if (inlined.type != "BlockStatement") {
3488
+ if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
3451
3489
  throw new Error("Unexpected inlined result");
3452
3490
  }
3453
3491
  if (!results) {
@@ -11625,6 +11625,10 @@ function inlineWithArgs(state, func, call, context) {
11625
11625
  if (!func.node || !func.node.body) {
11626
11626
  return null;
11627
11627
  }
11628
+ const lastStmt = (block) => {
11629
+ const last = block.body.slice(-1)[0];
11630
+ return last.type === "BlockStatement" ? lastStmt(last) : [last, block];
11631
+ };
11628
11632
  let retStmtCount = 0;
11629
11633
  if (context.type === "ReturnStatement") {
11630
11634
  const last = func.node.body.body.slice(-1)[0];
@@ -11647,7 +11651,7 @@ function inlineWithArgs(state, func, call, context) {
11647
11651
  return null;
11648
11652
  }
11649
11653
  if (retStmtCount === 1) {
11650
- const last = func.node.body.body.slice(-1)[0];
11654
+ const [last] = lastStmt(func.node.body);
11651
11655
  if (!last ||
11652
11656
  last.type !== "ReturnStatement" ||
11653
11657
  ((context.type === "AssignmentExpression" ||
@@ -11672,21 +11676,21 @@ function inlineWithArgs(state, func, call, context) {
11672
11676
  }
11673
11677
  diagnostic(state, call.loc, null);
11674
11678
  if (context.type !== "ReturnStatement" && retStmtCount) {
11675
- const last = body.body[body.body.length - 1];
11679
+ const [last, block] = lastStmt(body);
11676
11680
  if (last.type != "ReturnStatement") {
11677
11681
  throw new Error("ReturnStatement got lost!");
11678
11682
  }
11679
11683
  if (last.argument) {
11680
11684
  if (context.type === "AssignmentExpression") {
11681
11685
  context.right = last.argument;
11682
- body.body[body.body.length - 1] = {
11686
+ block.body[block.body.length - 1] = {
11683
11687
  type: "ExpressionStatement",
11684
11688
  expression: context,
11685
11689
  };
11686
11690
  }
11687
11691
  else if (context.type === "VariableDeclarator") {
11688
11692
  const { id, init: _init, kind: _kind, ...rest } = context;
11689
- body.body[body.body.length - 1] = {
11693
+ block.body[block.body.length - 1] = {
11690
11694
  ...rest,
11691
11695
  type: "ExpressionStatement",
11692
11696
  expression: {
@@ -11700,11 +11704,11 @@ function inlineWithArgs(state, func, call, context) {
11700
11704
  }
11701
11705
  else {
11702
11706
  const side_exprs = unused(last.argument);
11703
- body.body.splice(body.body.length - 1, 1, ...side_exprs);
11707
+ block.body.splice(block.body.length - 1, 1, ...side_exprs);
11704
11708
  }
11705
11709
  }
11706
11710
  else {
11707
- --body.body.length;
11711
+ --block.body.length;
11708
11712
  }
11709
11713
  }
11710
11714
  return body;
@@ -11966,7 +11970,8 @@ function buildReducedGraph(state, func, notice) {
11966
11970
  if (node.type === "WhileStatement") {
11967
11971
  head = localState.newBlock(top.continue);
11968
11972
  state.traverse(node.test);
11969
- localState.addEdge(localState.newBlock(), top.break);
11973
+ localState.addEdge(localState.curBlock, top.break);
11974
+ localState.newBlock();
11970
11975
  }
11971
11976
  else {
11972
11977
  head = localState.newBlock();
@@ -12287,6 +12292,8 @@ function cleanCfg(head) {
12287
12292
  }
12288
12293
  }));
12289
12294
  }
12295
+ if (!cur.node)
12296
+ cur.node = succ.node;
12290
12297
  }
12291
12298
  }
12292
12299
  });
@@ -12385,6 +12392,20 @@ function declName(decl) {
12385
12392
  throw new Error(`Unexpected EventDecl type: ${decl.type}`);
12386
12393
  }
12387
12394
  }
12395
+ function logAntState(s, decl) {
12396
+ const defs = Array.from(s.ant).reduce((defs, event) => {
12397
+ if (event.type === "def" || event.type === "mod")
12398
+ defs++;
12399
+ return defs;
12400
+ }, 0);
12401
+ console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live, ${s.isIsolated ? "" : "!"}isolated`);
12402
+ console.log(` - members: ${Array.from(s.members)
12403
+ .map(([block, live]) => block.order + (live ? "t" : "f"))
12404
+ .join(", ")}`);
12405
+ }
12406
+ function logAntDecls(antDecls) {
12407
+ antDecls.forEach(logAntState);
12408
+ }
12388
12409
  function sizeBasedPRE(state, func) {
12389
12410
  if (!func.node.body)
12390
12411
  return;
@@ -12399,14 +12420,7 @@ function sizeBasedPRE(state, func) {
12399
12420
  if (candidates) {
12400
12421
  if (logging) {
12401
12422
  console.log(`Found ${candidates.size} candidates in ${func.fullName}`);
12402
- candidates.forEach((s, decl) => {
12403
- const defs = Array.from(s.ant).reduce((defs, event) => {
12404
- if (event.type === "def")
12405
- defs++;
12406
- return defs;
12407
- }, 0);
12408
- console.log(` - ${declFullName(decl)}: ${candidateCost(s)} bytes, ${s.ant.size - defs} refs, ${defs} defs, ${s.live ? "" : "!"}live`);
12409
- });
12423
+ logAntDecls(candidates);
12410
12424
  }
12411
12425
  const nodeMap = new Map();
12412
12426
  const declMap = new Map();
@@ -12652,21 +12666,28 @@ function cloneAnticipatedState(as) {
12652
12666
  members: new Map(as.members),
12653
12667
  };
12654
12668
  }
12669
+ function mergeAnticipatedState(ae, be) {
12670
+ mergeSet(ae.ant, be.ant);
12671
+ be.members.forEach((live, block) => ae.members.set(block, live));
12672
+ if (be.live)
12673
+ ae.live = true;
12674
+ }
12655
12675
  function cloneAnticipatedDecls(ad) {
12656
12676
  const copy = anticipatedDecls();
12657
12677
  for (const [k, v] of ad) {
12658
- copy.set(k, cloneAnticipatedState(v));
12678
+ if (!v.isIsolated) {
12679
+ copy.set(k, cloneAnticipatedState(v));
12680
+ }
12659
12681
  }
12660
12682
  return copy;
12661
12683
  }
12662
12684
  function mergeAnticipatedDecls(a, b) {
12663
12685
  for (const [k, v] of b) {
12686
+ if (v.isIsolated)
12687
+ continue;
12664
12688
  const ae = a.get(k);
12665
12689
  if (ae) {
12666
- mergeSet(ae.ant, v.ant);
12667
- v.members.forEach((live, block) => ae.members.set(block, live));
12668
- if (v.live)
12669
- ae.live = true;
12690
+ mergeAnticipatedState(ae, v);
12670
12691
  }
12671
12692
  else {
12672
12693
  a.set(k, cloneAnticipatedState(v));
@@ -12680,6 +12701,7 @@ function equalStates(a, b) {
12680
12701
  const be = b.get(k);
12681
12702
  if (!be ||
12682
12703
  be.live != ae.live ||
12704
+ be.isIsolated != ae.isIsolated ||
12683
12705
  !equalSet(ae.ant, be.ant) ||
12684
12706
  !equalMap(ae.members, be.members)) {
12685
12707
  return false;
@@ -12750,7 +12772,8 @@ function candidateCost(candState) {
12750
12772
  cost += defCost(candState.node);
12751
12773
  }
12752
12774
  });
12753
- cost += defCost(candState.node) * candidateBoundary(candState).size;
12775
+ const boundarySize = candidateBoundary(candState).size;
12776
+ cost += defCost(candState.node) * boundarySize;
12754
12777
  return cost;
12755
12778
  }
12756
12779
  function computeAttributes(head) {
@@ -12888,9 +12911,7 @@ function computeAttributes(head) {
12888
12911
  if (isUpdate || candidates.live) {
12889
12912
  candidates.ant.add(event);
12890
12913
  }
12891
- if (!isUpdate) {
12892
- candidates.live = false;
12893
- }
12914
+ candidates.live = isUpdate;
12894
12915
  break;
12895
12916
  }
12896
12917
  }
@@ -12899,12 +12920,23 @@ function computeAttributes(head) {
12899
12920
  curState.forEach((antState) => {
12900
12921
  antState.head = top;
12901
12922
  antState.members.set(top, antState.live);
12923
+ if (!antState.live && candidateBoundary(antState).size === 0) {
12924
+ // we found a group that's isolated from the rest
12925
+ // of the function. Don't merge it with earlier
12926
+ // refs and defs, because we can take it or leave
12927
+ // it based on its own cost.
12928
+ antState.isIsolated = true;
12929
+ }
12902
12930
  });
12903
12931
  const oldState = blockStates[top.order];
12904
12932
  if (oldState && equalStates(oldState, curState)) {
12905
12933
  continue;
12906
12934
  }
12907
12935
  blockStates[top.order] = curState;
12936
+ if (logging) {
12937
+ console.log(`Updated block ${top.order}`);
12938
+ logAntDecls(curState);
12939
+ }
12908
12940
  if (top.preds) {
12909
12941
  top.preds.forEach((pred) => enqueue(pred));
12910
12942
  }
@@ -12917,7 +12949,9 @@ function computeAttributes(head) {
12917
12949
  if (cost >= 0)
12918
12950
  return;
12919
12951
  const existing = candidateDecls.get(decl);
12920
- if (!existing || candidateCost(existing) > cost) {
12952
+ if (!existing ||
12953
+ existing.isIsolated ||
12954
+ candidateCost(existing) > cost) {
12921
12955
  const boundary = candidateBoundary(events);
12922
12956
  if (!Array.from(boundary).every((block) => {
12923
12957
  if (block !== events.head && block.events) {
@@ -12957,7 +12991,11 @@ function computeAttributes(head) {
12957
12991
  return;
12958
12992
  }
12959
12993
  events.live = false;
12960
- if (candidateCost(events) != cost) {
12994
+ if (existing && existing.isIsolated) {
12995
+ delete existing.isIsolated;
12996
+ mergeAnticipatedState(events, existing);
12997
+ }
12998
+ else if (candidateCost(events) != cost) {
12961
12999
  throw new Error(`cost of block ${i} changed`);
12962
13000
  }
12963
13001
  candidateDecls.set(decl, events);
@@ -14138,10 +14176,10 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
14138
14176
  while (i < node.declarations.length) {
14139
14177
  const decl = declarations[i++];
14140
14178
  if (decl.init && decl.init.type === "CallExpression") {
14141
- const inlined = optimizeCall(state, decl.init, decl);
14179
+ const inlined = replace(optimizeCall(state, decl.init, decl), decl.init);
14142
14180
  if (!inlined)
14143
14181
  continue;
14144
- if (inlined.type != "BlockStatement") {
14182
+ if (Array.isArray(inlined) || inlined.type != "BlockStatement") {
14145
14183
  throw new Error("Unexpected inlined result");
14146
14184
  }
14147
14185
  if (!results) {
@@ -14905,7 +14943,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
14905
14943
  // the oldest optimized file, we don't need to regenerate
14906
14944
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
14907
14945
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
14908
- if (source_time < opt_time && 1656718632916 < opt_time) {
14946
+ if (source_time < opt_time && 1656860602542 < opt_time) {
14909
14947
  return { hasTests, diagnostics: prevDiagnostics };
14910
14948
  }
14911
14949
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@markw65/monkeyc-optimizer",
3
3
  "type": "module",
4
- "version": "1.0.29",
4
+ "version": "1.0.30",
5
5
  "description": "Source to source optimizer for Garmin Monkey C code",
6
6
  "main": "build/optimizer.cjs",
7
7
  "types": "build/src/optimizer.d.ts",