403
403
// Various expression simplifications. Pre run before closure (where we still have metadata), Post run after.
405
405
function simplifyExpressionsPre(ast) {
406
// Look for (x&A)<<B>>B and replace it with X&A if possible.
407
function simplifySignExtends(ast) {
408
traverseGenerated(ast, function(node, type) {
409
if (type == 'binary' && node[1] == '>>' && node[3][0] == 'num' &&
410
node[2][0] == 'binary' && node[2][1] == '<<' && node[2][3][0] == 'num' && node[3][1] == node[2][3][1]) {
411
var innerNode = node[2][2];
412
var shifts = node[3][1];
413
if (innerNode[0] == 'binary' && innerNode[1] == '&' && innerNode[3][0] == 'num') {
414
var mask = innerNode[3][1];
415
if (mask << shifts >> shifts == mask) {
406
423
// When there is a bunch of math like (((8+5)|0)+12)|0, only the external |0 is needed, one correction is enough.
407
424
// At each node, ((X|0)+Y)|0 can be transformed into (X+Y): The inner corrections are not needed
408
425
// TODO: Is the same is true for 0xff, 0xffff?
1482
1505
// Replace all var definitions with assignments; we will add var definitions at the top after we registerize
1483
1506
// We also mark local variables - i.e., having a var definition
1484
1507
var localVars = {};
1485
var hasSwitch = false; // we cannot optimize variables if there is a switch
1508
var hasSwitch = false; // we cannot optimize variables if there is a switch, unless in asm mode
1486
1509
traverse(fun, function(node, type) {
1487
1510
if (type == 'var') {
1488
1511
node[1].forEach(function(defined) { localVars[defined[0]] = 1 });
1596
1627
} else if (type in CONTROL_FLOW) {
1599
}, function(node, type) {
1600
if (type in CONTROL_FLOW) {
1601
// Invalidate all dominating on this level, further users make it unoptimizable
1602
for (var name in levelDominations[level]) {
1603
varLevels[name] = 0;
1628
// recurse children, in the context of a loop
1630
case 'while': case 'do': {
1631
traverse(node[1], possibilifier);
1633
traverse(node[2], possibilifier);
1638
traverse(node[1], possibilifier);
1639
for (var i = 2; i <= 4; i++) {
1641
traverse(node[i], possibilifier);
1647
traverse(node[1], possibilifier);
1649
traverse(node[2], possibilifier);
1653
traverse(node[3], possibilifier);
1659
traverse(node[1], possibilifier);
1660
var cases = node[2];
1661
for (var i = 0; i < cases.length; i++) {
1663
traverse(cases[i][1], possibilifier);
1668
default: throw dumpAst(node);
1605
levelDominations[level] = null;
1670
return null; // prevent recursion into children, which we already did
1609
1673
var optimizables = {};
1674
if (!hasSwitch || asm) {
1611
1675
for (var possible in possibles) {
1612
1676
if (!unoptimizables[possible]) optimizables[possible] = 1;
1680
//printErr('optimizables: ' + JSON.stringify(optimizables));
1681
//printErr('unoptimizables: ' + JSON.stringify(unoptimizables));
1615
1683
// Go through the function's code, assigning 'registers'.
1616
1684
// The only tricky bit is to keep variables locked on a register through loops,
1617
1685
// since they can potentially be returned to. Optimizable variables lock onto
1771
1839
// In memSafe mode, we are more careful and assume functions can replace HEAP and FUNCTION_TABLE, which
1772
1840
// can happen in ALLOW_MEMORY_GROWTH mode
1774
var ELIMINATION_SAFE_NODES = set('var', 'assign', 'call', 'if', 'toplevel', 'do', 'return', 'label'); // do is checked carefully, however
1842
var ELIMINATION_SAFE_NODES = set('var', 'assign', 'call', 'if', 'toplevel', 'do', 'return', 'label', 'switch'); // do is checked carefully, however
1775
1843
var NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS = set('name', 'num', 'string', 'binary', 'sub', 'unary-prefix');
1776
1844
var IGNORABLE_ELIMINATOR_SCAN_NODES = set('num', 'toplevel', 'string', 'break', 'continue', 'dot'); // dot can only be STRING_TABLE.*
1777
var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', 'switch', 'for', 'while', 'array', 'throw'); // we could handle some of these, TODO, but nontrivial (e.g. for while, the condition is hit multiple times after the body)
1845
var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', 'for', 'while', 'array', 'throw'); // we could handle some of these, TODO, but nontrivial (e.g. for while, the condition is hit multiple times after the body)
1779
1847
function eliminate(ast, memSafe) {
1780
1848
// Find variables that have a single use, and if they can be eliminated, do so
1828
1897
uses[name]--; // because the name node will show up by itself in the previous case
1900
} else if (type == 'switch') {
1905
// we cannot eliminate variables if there is a switch
1906
if (hasSwitch && !asm) return;
1833
1908
var potentials = {}; // local variables with 1 definition and 1 use
1834
1909
var sideEffectFree = {}; // whether a local variable has no side effects in its definition
1835
for (var name in locals) {
1911
function unprocessVariable(name) {
1912
if (name in potentials) delete potentials[name];
1913
if (name in varsToRemove) delete varsToRemove[name];
1914
if (name in sideEffectFree) delete sideEffectFree[name];
1915
if (name in varsToTryToRemove) delete varsToTryToRemove[name];
1917
function processVariable(name) {
1836
1918
if (definitions[name] == 1 && uses[name] == 1) {
1837
1919
potentials[name] = 1;
1838
1920
} else if (uses[name] == 0 && (!definitions[name] || definitions[name] <= 1)) { // no uses, no def or 1 def (cannot operate on phis, and the llvm optimizer will remove unneeded phis anyhow)
1839
1921
var hasSideEffects = false;
1841
traverse(values[name], function(node, type) {
1842
if (!(type in NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS)) {
1843
hasSideEffects = true; // cannot remove this unused variable, constructing it has side effects
1922
var value = values[name];
1924
// TODO: merge with other side effect code
1925
// First, pattern-match
1926
// (HEAP32[((tempDoublePtr)>>2)]=((HEAP32[(($_sroa_0_0__idx1)>>2)])|0),HEAP32[(((tempDoublePtr)+(4))>>2)]=((HEAP32[((($_sroa_0_0__idx1)+(4))>>2)])|0),(+(HEAPF64[(tempDoublePtr)>>3])))
1927
// which has no side effects and is the special form of converting double to i64.
1928
if (!(value[0] == 'seq' && value[1][0] == 'assign' && value[1][2][0] == 'sub' && value[1][2][2][0] == 'binary' && value[1][2][2][1] == '>>' &&
1929
value[1][2][2][2][0] == 'name' && value[1][2][2][2][1] == 'tempDoublePtr')) {
1930
// If not that, then traverse and scan normally.
1931
traverse(value, function(node, type) {
1932
if (!(type in NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS)) {
1933
hasSideEffects = true; // cannot remove this unused variable, constructing it has side effects
1848
1939
if (!hasSideEffects) {
1849
1940
varsToRemove[name] = !definitions[name] ? 2 : 1; // remove it normally
1850
1941
sideEffectFree[name] = true;
1942
// Each time we remove a variable with 0 uses, if its value has no
1943
// side effects and vanishes too, then we can remove a use from variables
1944
// appearing in it, and possibly eliminate again
1946
traverse(value, function(node, type) {
1947
if (type == 'name') {
1949
node[1] = ''; // we can remove this - it will never be shown, and should not be left to confuse us as we traverse
1950
if (name in locals) {
1951
uses[name]--; // cannot be infinite recursion since we descend an energy function
1952
assert(uses[name] >= 0);
1953
unprocessVariable(name);
1954
processVariable(name);
1852
1960
varsToTryToRemove[name] = 1; // try to remove it later during scanning
1964
for (var name in locals) {
1965
processVariable(name);
1856
1968
//printErr('defs: ' + JSON.stringify(definitions));
1857
1969
//printErr('uses: ' + JSON.stringify(uses));
1858
1970
//printErr('values: ' + JSON.stringify(values));
2133
2245
traverseInOrder(node[1]);
2134
2246
traverseInOrder(node[2]);
2135
2247
traverseInOrder(node[3]);
2248
} else if (type == 'switch') {
2249
traverseInOrder(node[1]);
2250
var cases = node[2];
2251
for (var i = 0; i < cases.length; i++) {
2253
assert(c[0] === null || c[0][0] == 'num' || (c[0][0] == 'unary-prefix' && c[0][2][0] == 'num'));
2255
for (var j = 0; j < stats.length; j++) {
2256
traverseInOrder(stats[j]);
2137
2260
if (!(type in ABORTING_ELIMINATOR_SCAN_NODES)) {
2138
2261
printErr('unfamiliar eliminator scan node: ' + JSON.stringify(node));
2181
2304
traverse(func, function(block) {
2182
var stats = getStatements(block);
2305
// Look for statements, including while-switch pattern
2306
var stats = getStatements(block) || (block[0] == 'while' && block[2][0] == 'switch' ? [block[2]] : stats);
2183
2307
if (!stats) return;
2308
//printErr('Stats: ' + JSON.stringify(stats).substr(0,100));
2185
2310
//printErr('new StatBlock');
2186
2311
for (var i = 0; i < stats.length; i++) {
2187
2312
var node = stats[i];
2188
//printErr('StatBlock[' + i + '] => ' + JSON.stringify(node));
2313
//printErr('StatBlock[' + i + '] => ' + JSON.stringify(node).substr(0,100));
2189
2314
var type = node[0];
2190
2315
if (type == 'stat') {
2191
2316
node = node[1];
2353
2478
compress: function() { compress = true },
2354
2479
noPrintMetadata: function() { printMetadata = false },
2355
2480
asm: function() { asm = true },
2356
last: function() { last = true }
2481
last: function() { last = true },
2482
closure: function(){} // handled in python