ressurect optimizeShifts (just in js optimizer and tests), with a fix for ensuring at most one value with unknown lower bits; #2481
This commit is contained in:
Родитель
0b7912b0cf
Коммит
b912cd9ae7
|
@ -1842,6 +1842,12 @@ This pointer might make sense in another type signature: i: 0
|
|||
for input, expected, passes in [
|
||||
(path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(),
|
||||
['hoistMultiples', 'removeAssignsToUndefined', 'simplifyExpressions']),
|
||||
(path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(),
|
||||
['simplifyExpressions', 'optimizeShiftsConservative']),
|
||||
(path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
|
||||
['simplifyExpressions', 'optimizeShiftsAggressive']),
|
||||
(path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(),
|
||||
['optimizeShiftsAggressive']),
|
||||
(path_from_root('tools', 'test-js-optimizer-si.js'), open(path_from_root('tools', 'test-js-optimizer-si-output.js')).read(),
|
||||
['simplifyIfs']),
|
||||
(path_from_root('tools', 'test-js-optimizer-regs.js'), open(path_from_root('tools', 'test-js-optimizer-regs-output.js')).read(),
|
||||
|
|
|
@ -975,6 +975,397 @@ function simplifyIfs(ast) {
|
|||
});
|
||||
}
|
||||
|
||||
// In typed arrays mode 2, we can have
|
||||
// HEAP[x >> 2]
|
||||
// very often. We can in some cases do the shift on the variable itself when it is set,
|
||||
// to greatly reduce the number of shift operations.
|
||||
function optimizeShiftsInternal(ast, conservative) {
|
||||
assert(!asm);
|
||||
var MAX_SHIFTS = 3;
|
||||
traverseGeneratedFunctions(ast, function(fun) {
|
||||
var funMore = true;
|
||||
var funFinished = {};
|
||||
while (funMore) {
|
||||
funMore = false;
|
||||
// Recognize variables and parameters
|
||||
var vars = {};
|
||||
function newVar(name, param, addUse) {
|
||||
if (!vars[name]) {
|
||||
vars[name] = {
|
||||
param: param,
|
||||
defs: addUse ? 1 : 0,
|
||||
uses: 0,
|
||||
timesShifted: [0, 0, 0, 0], // zero shifts of size 0, 1, 2, 3
|
||||
benefit: 0,
|
||||
primaryShift: -1
|
||||
};
|
||||
}
|
||||
}
|
||||
// params
|
||||
if (fun[2]) {
|
||||
fun[2].forEach(function(arg) {
|
||||
newVar(arg, true, true);
|
||||
});
|
||||
}
|
||||
// vars
|
||||
// XXX if var has >>=, ignore it here? That means a previous pass already optimized it
|
||||
var hasSwitch = traverse(fun, function(node, type) {
|
||||
if (type === 'var') {
|
||||
node[1].forEach(function(arg) {
|
||||
newVar(arg[0], false, arg[1]);
|
||||
});
|
||||
} else if (type === 'switch') {
|
||||
// The relooper can't always optimize functions, and we currently don't work with
|
||||
// switch statements when optimizing shifts. Bail.
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (hasSwitch) {
|
||||
break;
|
||||
}
|
||||
// uses and defs TODO: weight uses by being inside a loop (powers). without that, we
|
||||
// optimize for code size, not speed.
|
||||
var stack = [];
|
||||
traverse(fun, function(node, type) {
|
||||
stack.push(node);
|
||||
if (type === 'name' && vars[node[1]] && stack[stack.length-2][0] != 'assign') {
|
||||
vars[node[1]].uses++;
|
||||
} else if (type === 'assign' && node[2][0] === 'name' && vars[node[2][1]]) {
|
||||
vars[node[2][1]].defs++;
|
||||
}
|
||||
});
|
||||
// First, break up elements inside a shift. This lets us see clearly what to do next.
|
||||
traverse(fun, function(node, type) {
|
||||
if (type === 'binary' && node[1] === '>>' && node[3][0] === 'num') {
|
||||
var shifts = node[3][1];
|
||||
if (shifts <= MAX_SHIFTS) {
|
||||
// Check for validity. It is ok to have a single element that might have non-zeroed lower bits, but no more.
|
||||
// x + 4 >> 2 === (x >> 2) + (4 >> 2), but not x + 3 >> 2 === (x >> 2) + (3 >> 2) (c.f. x=1, we get 1 !== 0)
|
||||
var seen = '', ok = true;
|
||||
function checkShift(subNode) {
|
||||
if (subNode[0] === 'binary') {
|
||||
switch (subNode[1]) {
|
||||
case '+': case '|': case '&': { // this could be more comprehensive, but likely not needed
|
||||
checkShift(subNode[2]);
|
||||
checkShift(subNode[3]);
|
||||
break;
|
||||
}
|
||||
case '>>': case '>>>': {
|
||||
checkShift(subNode[2]);
|
||||
break;
|
||||
}
|
||||
case '<<': {
|
||||
if (subNode[3][0] === 'num' && subNode[3][1] >= shifts) break; // bits are clear, all good
|
||||
checkShift(subNode[2]);
|
||||
break;
|
||||
}
|
||||
case '*': {
|
||||
if (subNode[3][0] === 'num') {
|
||||
var value = subNode[3][1];
|
||||
if (((value >> shifts) << shifts) === value) return; // bits are clear, all good
|
||||
}
|
||||
checkShift(subNode[2]);
|
||||
checkShift(subNode[3]);
|
||||
break;
|
||||
}
|
||||
default: ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (subNode[0] === 'name') {
|
||||
var name = subNode[1];
|
||||
if (!seen) {
|
||||
seen = name;
|
||||
} else if (name !== seen) {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (subNode[0] === 'num') {
|
||||
var value = subNode[1];
|
||||
if (((value >> shifts) << shifts) !== value) ok = false;
|
||||
return;
|
||||
}
|
||||
if (subNode[0] === 'sub') {
|
||||
if (seen) ok = false;
|
||||
seen = 'heap access';
|
||||
return;
|
||||
}
|
||||
ok = false; // anything else is bad
|
||||
}
|
||||
checkShift(node[2]);
|
||||
if (!ok) return;
|
||||
|
||||
// Push the >> inside the value elements
|
||||
function addShift(subNode) {
|
||||
if (subNode[0] === 'binary' && subNode[1] === '+') {
|
||||
subNode[2] = addShift(subNode[2]);
|
||||
subNode[3] = addShift(subNode[3]);
|
||||
return subNode;
|
||||
}
|
||||
if (subNode[0] === 'name' && !subNode[2]) { // names are returned with a shift, but we also note their being shifted
|
||||
var name = subNode[1];
|
||||
if (vars[name]) {
|
||||
vars[name].timesShifted[shifts]++;
|
||||
subNode[2] = true;
|
||||
}
|
||||
}
|
||||
return ['binary', '>>', subNode, ['num', shifts]];
|
||||
}
|
||||
return addShift(node[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
traverse(fun, function(node, type) {
|
||||
if (node[0] === 'name' && node[2]) {
|
||||
return node.slice(0, 2); // clean up our notes
|
||||
}
|
||||
});
|
||||
// At this point, shifted expressions are split up, and we know who the vars are and their info, so we can decide
|
||||
// TODO: vars that depend on other vars
|
||||
for (var name in vars) {
|
||||
var data = vars[name];
|
||||
var totalTimesShifted = sum(data.timesShifted);
|
||||
if (totalTimesShifted === 0) {
|
||||
continue;
|
||||
}
|
||||
if (totalTimesShifted != Math.max.apply(null, data.timesShifted)) {
|
||||
// TODO: Handle multiple different shifts
|
||||
continue;
|
||||
}
|
||||
if (funFinished[name]) continue;
|
||||
// We have one shift size (and possible unshifted uses). Consider replacing this variable with a shifted clone. If
|
||||
// the estimated benefit is >0, we will do it
|
||||
if (data.defs === 1) {
|
||||
data.benefit = totalTimesShifted - 2*(data.defs + (data.param ? 1 : 0));
|
||||
}
|
||||
if (conservative) data.benefit = 0;
|
||||
if (data.benefit > 0) {
|
||||
funMore = true; // We will reprocess this function
|
||||
for (var i = 0; i < 4; i++) {
|
||||
if (data.timesShifted[i]) {
|
||||
data.primaryShift = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//printErr(JSON.stringify(vars));
|
||||
function cleanNotes() { // We need to mark 'name' nodes as 'processed' in some passes here; this cleans the notes up
|
||||
traverse(fun, function(node, type) {
|
||||
if (node[0] === 'name' && node[2]) {
|
||||
return node.slice(0, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
cleanNotes();
|
||||
// Apply changes
|
||||
function needsShift(name) {
|
||||
return vars[name] && vars[name].primaryShift >= 0;
|
||||
}
|
||||
for (var name in vars) { // add shifts for params and var's for all new variables
|
||||
var data = vars[name];
|
||||
if (needsShift(name)) {
|
||||
if (data.param) {
|
||||
fun[3].unshift(['var', [[name + '$s' + data.primaryShift, ['binary', '>>', ['name', name], ['num', data.primaryShift]]]]]);
|
||||
} else {
|
||||
fun[3].unshift(['var', [[name + '$s' + data.primaryShift]]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
var stack = [];
|
||||
traverse(fun, function(node, type) { // add shift to assignments
|
||||
stack.push(node);
|
||||
if (node[0] === 'assign' && node[1] === true && node[2][0] === 'name' && needsShift(node[2][1]) && !node[2][2]) {
|
||||
var name = node[2][1];
|
||||
var data = vars[name];
|
||||
var parent = stack[stack.length-3];
|
||||
var statements = getStatements(parent);
|
||||
assert(statements, 'Invalid parent for assign-shift: ' + dump(parent));
|
||||
var i = statements.indexOf(stack[stack.length-2]);
|
||||
statements.splice(i+1, 0, ['stat', ['assign', true, ['name', name + '$s' + data.primaryShift], ['binary', '>>', ['name', name, true], ['num', data.primaryShift]]]]);
|
||||
} else if (node[0] === 'var') {
|
||||
var args = node[1];
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
var name = arg[0];
|
||||
var data = vars[name];
|
||||
if (arg[1] && needsShift(name)) {
|
||||
args.splice(i+1, 0, [name + '$s' + data.primaryShift, ['binary', '>>', ['name', name, true], ['num', data.primaryShift]]]);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
});
|
||||
cleanNotes();
|
||||
var stack = [];
|
||||
traverse(fun, function(node, type) { // replace shifted name with new variable
|
||||
stack.push(node);
|
||||
if (node[0] === 'binary' && node[1] === '>>' && node[2][0] === 'name' && needsShift(node[2][1]) && node[3][0] === 'num') {
|
||||
var name = node[2][1];
|
||||
var data = vars[name];
|
||||
var parent = stack[stack.length-2];
|
||||
// Don't modify in |x$sN = x >> 2|, in normal assigns and in var assigns
|
||||
if (parent[0] === 'assign' && parent[2][0] === 'name' && parent[2][1] === name + '$s' + data.primaryShift) return;
|
||||
if (parent[0] === name + '$s' + data.primaryShift) return;
|
||||
if (node[3][1] === data.primaryShift) {
|
||||
return ['name', name + '$s' + data.primaryShift];
|
||||
}
|
||||
}
|
||||
});
|
||||
cleanNotes();
|
||||
var SIMPLE_SHIFTS = set('<<', '>>');
|
||||
var more = true;
|
||||
while (more) { // combine shifts in the same direction as an optimization
|
||||
more = false;
|
||||
traverse(fun, function(node, type) {
|
||||
if (node[0] === 'binary' && node[1] in SIMPLE_SHIFTS && node[2][0] === 'binary' && node[2][1] === node[1] &&
|
||||
node[3][0] === 'num' && node[2][3][0] === 'num') { // do not turn a << b << c into a << b + c; while logically identical, it is slower
|
||||
more = true;
|
||||
return ['binary', node[1], node[2][2], ['num', node[3][1] + node[2][3][1]]];
|
||||
}
|
||||
});
|
||||
}
|
||||
// Before recombining, do some additional optimizations
|
||||
traverse(fun, function(node, type) {
|
||||
// Apply constant shifts onto constants
|
||||
if (type === 'binary' && node[1] === '>>' && node[2][0] === 'num' && node[3][0] === 'num' && node[3][1] <= MAX_SHIFTS) {
|
||||
var subNode = node[2];
|
||||
var shifts = node[3][1];
|
||||
var result = subNode[1] / Math.pow(2, shifts);
|
||||
if (result % 1 === 0) {
|
||||
subNode[1] = result;
|
||||
return subNode;
|
||||
}
|
||||
}
|
||||
// Optimize the case of ($a*80)>>2 into ($a*20)|0
|
||||
if (type === 'binary' && node[1] in SIMPLE_SHIFTS &&
|
||||
node[2][0] === 'binary' && node[2][1] === '*') {
|
||||
var mulNode = node[2];
|
||||
if (mulNode[2][0] === 'num') {
|
||||
var temp = mulNode[2];
|
||||
mulNode[2] = mulNode[3];
|
||||
mulNode[3] = temp;
|
||||
}
|
||||
if (mulNode[3][0] === 'num') {
|
||||
if (node[1] === '<<') {
|
||||
mulNode[3][1] *= Math.pow(2, node[3][1]);
|
||||
node[1] = '|';
|
||||
node[3][1] = 0;
|
||||
return node;
|
||||
} else {
|
||||
if (mulNode[3][1] % Math.pow(2, node[3][1]) === 0) {
|
||||
mulNode[3][1] /= Math.pow(2, node[3][1]);
|
||||
node[1] = '|';
|
||||
node[3][1] = 0;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* XXX - theoretically useful optimization(s), but commented out as not helpful in practice
|
||||
// Transform (x << 2) >> 2 into x & mask or something even simpler
|
||||
if (type === 'binary' && node[1] === '>>' && node[3][0] === 'num' &&
|
||||
node[2][0] === 'binary' && node[2][1] === '<<' && node[2][3][0] === 'num' && node[3][1] === node[2][3][1]) {
|
||||
var subNode = node[2];
|
||||
var shifts = node[3][1];
|
||||
var mask = ((0xffffffff << shifts) >>> shifts) | 0;
|
||||
return ['binary', '&', subNode[2], ['num', mask]];
|
||||
//return ['binary', '|', subNode[2], ['num', 0]];
|
||||
//return subNode[2];
|
||||
}
|
||||
*/
|
||||
});
|
||||
// Re-combine remaining shifts, to undo the breaking up we did before. may require reordering inside +'s
|
||||
var stack = [];
|
||||
traverse(fun, function(node, type) {
|
||||
stack.push(node);
|
||||
if (type === 'binary' && node[1] === '+' && (stack[stack.length-2][0] != 'binary' || stack[stack.length-2][1] !== '+')) {
|
||||
// 'Flatten' added items
|
||||
var addedItems = [];
|
||||
function flatten(node) {
|
||||
if (node[0] === 'binary' && node[1] === '+') {
|
||||
flatten(node[2]);
|
||||
flatten(node[3]);
|
||||
} else {
|
||||
addedItems.push(node);
|
||||
}
|
||||
}
|
||||
flatten(node);
|
||||
var originalOrder = addedItems.slice();
|
||||
function key(node) { // a unique value for all relevant shifts for recombining, non-unique for stuff we don't need to bother with
|
||||
function originalOrderKey(item) {
|
||||
return -originalOrder.indexOf(item);
|
||||
}
|
||||
if (node[0] === 'binary' && node[1] in SIMPLE_SHIFTS) {
|
||||
if (node[3][0] === 'num' && node[3][1] <= MAX_SHIFTS) return 2*node[3][1] + (node[1] === '>>' ? 100 : 0); // 0-106
|
||||
return (node[1] === '>>' ? 20000 : 10000) + originalOrderKey(node);
|
||||
}
|
||||
if (node[0] === 'num') return -20000 + node[1];
|
||||
return -10000 + originalOrderKey(node); // Don't modify the original order if we don't modify anything
|
||||
}
|
||||
for (var i = 0; i < addedItems.length; i++) {
|
||||
if (addedItems[i][0] === 'string') return; // this node is not relevant for us
|
||||
}
|
||||
addedItems.sort(function(node1, node2) {
|
||||
return key(node1) - key(node2);
|
||||
});
|
||||
// Regenerate items, now sorted
|
||||
var i = 0;
|
||||
while (i < addedItems.length-1) { // re-combine inside addedItems
|
||||
var k = key(addedItems[i]), k1 = key(addedItems[i+1]);
|
||||
if (k === k1 && k >= 0 && k1 <= 106) {
|
||||
addedItems[i] = ['binary', addedItems[i][1], ['binary', '+', addedItems[i][2], addedItems[i+1][2]], addedItems[i][3]];
|
||||
addedItems.splice(i+1, 1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
var num = 0;
|
||||
for (i = 0; i < addedItems.length; i++) { // combine all numbers into one
|
||||
if (addedItems[i][0] === 'num') {
|
||||
num += addedItems[i][1];
|
||||
addedItems.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
if (num != 0) { // add the numbers into an existing shift, we
|
||||
// prefer (x+5)>>7 over (x>>7)+5 , since >>'s result is known to be 32-bit and is more easily optimized.
|
||||
// Also, in the former we can avoid the parentheses, which saves a little space (the number will be bigger,
|
||||
// so it might take more space, but normally at most one more digit).
|
||||
var added = false;
|
||||
for (i = 0; i < addedItems.length; i++) {
|
||||
if (addedItems[i][0] === 'binary' && addedItems[i][1] === '>>' && addedItems[i][3][0] === 'num' && addedItems[i][3][1] <= MAX_SHIFTS) {
|
||||
addedItems[i] = ['binary', '>>', ['binary', '+', addedItems[i][2], ['num', num << addedItems[i][3][1]]], addedItems[i][3]];
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
addedItems.unshift(['num', num]);
|
||||
}
|
||||
}
|
||||
var ret = addedItems.pop();
|
||||
while (addedItems.length > 0) { // re-create AST from addedItems
|
||||
ret = ['binary', '+', ret, addedItems.pop()];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
});
|
||||
// Note finished variables
|
||||
for (var name in vars) {
|
||||
funFinished[name] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function optimizeShiftsConservative(ast) {
|
||||
optimizeShiftsInternal(ast, true);
|
||||
}
|
||||
|
||||
function optimizeShiftsAggressive(ast) {
|
||||
optimizeShiftsInternal(ast, false);
|
||||
}
|
||||
|
||||
// We often have branchings that are simplified so one end vanishes, and
|
||||
// we then get
|
||||
// if (!(x < 5))
|
||||
|
@ -5276,6 +5667,8 @@ var passes = {
|
|||
removeAssignsToUndefined: removeAssignsToUndefined,
|
||||
//removeUnneededLabelSettings: removeUnneededLabelSettings,
|
||||
simplifyExpressions: simplifyExpressions,
|
||||
optimizeShiftsConservative: optimizeShiftsConservative,
|
||||
optimizeShiftsAggressive: optimizeShiftsAggressive,
|
||||
simplifyIfs: simplifyIfs,
|
||||
hoistMultiples: hoistMultiples,
|
||||
loopOptimizer: loopOptimizer,
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
function shifty($id2) {
|
||||
var c1$s2;
|
||||
var $tp$s2;
|
||||
var $parameters_addr$s2;
|
||||
var $wavelet38$s2;
|
||||
var _dwt_norms_real$s2;
|
||||
var $a_addr$s2;
|
||||
var _idents$s2;
|
||||
var $id3$s3;
|
||||
var $id2$s1 = $id2 >> 1;
|
||||
q(HEAP32[$id >> 2]);
|
||||
q(HEAP32[$id + 40 >> 2]);
|
||||
q(HEAP32[$id + 80 >> 2]);
|
||||
q(HEAP32[unknown1 + unknown2 + $id >> 2]);
|
||||
q(HEAP32[unknown1 + $id + unknown2 >> 2]);
|
||||
var localUnchanged1 = get(1), localUnchanged2 = get(1);
|
||||
q(HEAP32[localUnchanged1 + $id + localUnchanged2 >> 2]);
|
||||
q($id >> _something_);
|
||||
$id = q("..");
|
||||
q($id << _somethingElse_);
|
||||
pause(-1);
|
||||
q(HEAP32[$id2$s1]);
|
||||
q(HEAP32[$id2$s1]);
|
||||
q(HEAP32[$id2$s1]);
|
||||
q(HEAP32[$id2$s1]);
|
||||
q(HEAP32[$id2$s1 + 20]);
|
||||
q(HEAP32[$id2$s1 + 40]);
|
||||
var $id3 = get(74), $id3$s3 = $id3 >> 3;
|
||||
q(HEAP32[$id3$s3]);
|
||||
q(HEAP32[$id3$s3 + 5]);
|
||||
q(HEAP32[$id3$s3 + 10]);
|
||||
q($id3);
|
||||
pause(0);
|
||||
var _idents = get("abc"), _idents$s2 = _idents >> 2;
|
||||
q(HEAP32[HEAP32[_idents$s2] + 8 >> 2]);
|
||||
q(HEAP32[HEAP32[_idents$s2] + 8 >> 2]);
|
||||
q(HEAP32[HEAP32[_idents$s2] + 8 >> 2]);
|
||||
pause(1);
|
||||
var $sn_addr = get(12), $a_addr = get(999), $a_addr$s2 = $a_addr >> 2;
|
||||
var $i = get(112233);
|
||||
q(HEAP32[(($sn_addr - 1 << 1) + 1 << 2 >> 2) + $a_addr$s2]);
|
||||
q(HEAP32[(($i - 1 << 1) + 1 << 2 >> 2) + $a_addr$s2]);
|
||||
q(HEAP32[($i << 3 >> 2) + $a_addr$s2]);
|
||||
q(HEAP32[($i << 2 >> 2) + $a_addr$s2]);
|
||||
q($a_addr$s2, z($a_addr$s2));
|
||||
pause(2);
|
||||
var $level = HEAP[get(322) >> 2];
|
||||
var _dwt_norms_real = get("a"), _dwt_norms_real$s2 = _dwt_norms_real >> 2, $orient = get("cheez");
|
||||
q(HEAP32[($level << 3 >> 2) + _dwt_norms_real$s2 + ($orient * 20 | 0)]);
|
||||
q(HEAP32[(($level << 3) + 4 >> 2) + _dwt_norms_real$s2 + ($orient * 20 | 0)]);
|
||||
q(HEAP32[(($level << 3) + 8 >> 2) + _dwt_norms_real$s2 + ($orient * 20 | 0)]);
|
||||
pause(3);
|
||||
var $wavelet38 = get(38), $wavelet38$s2 = $wavelet38 >> 2;
|
||||
$k = $a_addr;
|
||||
q(HEAPF32[HEAP32[$wavelet38$s2] + ($k << 4) + 8 >> 2]);
|
||||
q(HEAPF32[HEAP32[$wavelet38$s2] + ($k << 4) + 12 >> 2]);
|
||||
q(HEAPF32[HEAP32[$wavelet38$s2] + ($k << 4) + 400 >> 2]);
|
||||
pause(4);
|
||||
var $p = $k, $parameters_addr = get("burger"), $parameters_addr$s2 = $parameters_addr >> 2;
|
||||
q(HEAP32[(($p << 2) + 5624 >> 2) + $parameters_addr$s2]);
|
||||
q(HEAP32[(($p << 2) + 5644 >> 2) + $parameters_addr$s2]);
|
||||
q(HEAP32[(($p << 2) + 5664 >> 2) + $parameters_addr$s2]);
|
||||
pause(5);
|
||||
var $res_spec242 = get($real), $cp = get("b"), $tileno = arguments[2];
|
||||
q(HEAP32[(($res_spec242 - 1 << 2) + 5624 >> 2) + $parameters_addr$s2]);
|
||||
q(HEAP32[(HEAP32[$cp + 108 >> 2] + 420 >> 2) + ($tileno * 1397 | 0)]);
|
||||
pause(6);
|
||||
q($idx << 3);
|
||||
q(1 << $idx << 1);
|
||||
print(INDENT + "Entering: _main" + "hi");
|
||||
pause(7);
|
||||
var $tp = get("tp"), $tp$s2 = $tp >> 2;
|
||||
q($tp$s2);
|
||||
q($tp$s2);
|
||||
q($tp$s2);
|
||||
HEAP32[$H400] = $tp;
|
||||
HEAP32[$tp] = 5;
|
||||
HEAP32[$tp$s2] = 5;
|
||||
HEAP32[HEAP[$tp$s2]] = 5;
|
||||
HEAP32[HEAP[$tp$s2] >> 2] = 5;
|
||||
pause(7);
|
||||
q(go() >> 1 << 1);
|
||||
q(go() << 1 >> 1);
|
||||
q(go() >> 2);
|
||||
q(go() << 2);
|
||||
q(go() >> 8 << 8);
|
||||
q(go() << 8 >> 8);
|
||||
q(go() >> 16);
|
||||
q(go() << 16);
|
||||
q(go() + 2 >> 2);
|
||||
var c1 = get(), c1$s2 = c1 >> 2;
|
||||
HEAP32[c1$s2] = 1;
|
||||
HEAP32[c1$s2 + 1] = 1;
|
||||
HEAP32[(get() << 2 >> 2) + c1$s2] = 1;
|
||||
var c2 = get();
|
||||
HEAP32[c2 >> 2] = 1;
|
||||
HEAP32[c2 + 3 >> 2] = 1;
|
||||
HEAP32[c2 + (get() << 2) >> 2] = 1;
|
||||
var c3 = get();
|
||||
HEAP32[c3 >> 2] = 1;
|
||||
HEAP32[c3 + 4 >> 2] = 1;
|
||||
HEAP32[(get() << 1) + c3 >> 2] = 1;
|
||||
var c4 = get(), c5 = get();
|
||||
HEAP32[c4 >> 2] = 1;
|
||||
HEAP32[c4 + 4 >> 2] = 1;
|
||||
HEAP32[c4 + c5 >> 2] = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
// TODO also with >> 1 and >> 3
|
||||
// also HEAP*U*, and HEAP8, 16
|
||||
function shifty($id2) {
|
||||
// $id is a non-ssa, $id2 is a param. both should be replaced with a shifted version
|
||||
q(HEAP32[$id >> 2]);
|
||||
q(HEAP32[($id + 40) >> 2]);
|
||||
q(HEAP32[($id + 80 | 0) >> 2]);
|
||||
q(HEAP32[(unknown1 + unknown2 + $id) >> 2]);
|
||||
q(HEAP32[(unknown1 + $id + unknown2) >> 2]); // unknowns should be shifted together
|
||||
var localUnchanged1 = get(1), localUnchanged2 = get(1);
|
||||
q(HEAP32[(localUnchanged1 + $id + localUnchanged2) >> 2]); // unknowns should be shifted together
|
||||
q($id >> _something_); // non-fixed shift
|
||||
$id = q('..');
|
||||
q($id << _somethingElse_); // non-fixed shift
|
||||
pause(-1);
|
||||
q(HEAP32[$id2 >> 1]);
|
||||
q(HEAP32[$id2 >> 1]);
|
||||
q(HEAP32[$id2 >> 1]);
|
||||
q(HEAP32[$id2 >> 1]);
|
||||
q(HEAP32[($id2 + 40) >> 1]);
|
||||
q(HEAP32[($id2 + 80 | 0) >> 1]);
|
||||
var $id3 = get(74);
|
||||
q(HEAP32[$id3 >> 3]);
|
||||
q(HEAP32[($id3 + 40) >> 3]);
|
||||
q(HEAP32[($id3 + 80 | 0) >> 3]);
|
||||
q($id3);
|
||||
pause(0);
|
||||
// similar, but inside another HEAP
|
||||
var _idents = get('abc');
|
||||
q(HEAP32[(HEAP32[_idents >> 2] + 8 | 0) >> 2]);
|
||||
q(HEAP32[(HEAP32[_idents >> 2] + 8 | 0) >> 2]);
|
||||
q(HEAP32[(HEAP32[_idents >> 2] + 8 | 0) >> 2]);
|
||||
pause(1);
|
||||
// $i's shifts should consolidate (the last should be 0..?
|
||||
// since we may have had |0 in the middle!)
|
||||
var $sn_addr = get(12), $a_addr = get(999);
|
||||
var $i = get(112233);
|
||||
q(HEAP32[($a_addr + ((($sn_addr - 1 << 1) + 1 | 0) << 2) | 0) >> 2]);
|
||||
q(HEAP32[($a_addr + ((($i - 1 << 1) + 1 | 0) << 2) | 0) >> 2]);
|
||||
q(HEAP32[($a_addr + (($i << 1 | 0) << 2) | 0) >> 2]);
|
||||
q(HEAP32[($a_addr + ($i << 2)) >> 2]);
|
||||
q($a_addr >> 2, z($a_addr >> 2));
|
||||
pause(2);
|
||||
var $level = HEAP[get(322) >> 2]; // ignore this
|
||||
var _dwt_norms_real = get('a'), $orient = get('cheez');
|
||||
q(HEAP32[(_dwt_norms_real + $orient * 80 + ($level << 3) | 0) >> 2]);
|
||||
q(HEAP32[(_dwt_norms_real + $orient * 80 + ($level << 3) + 4 | 0) >> 2]);
|
||||
q(HEAP32[(_dwt_norms_real + $orient * 80 + ($level << 3) + 8 | 0) >> 2]);
|
||||
pause(3);
|
||||
// reuse $a_addr here
|
||||
var $wavelet38 = get(38);
|
||||
$k = $a_addr;
|
||||
q(HEAPF32[(HEAP32[$wavelet38 >> 2] + ($k << 4) + 8 | 0) >> 2]);
|
||||
q(HEAPF32[(HEAP32[$wavelet38 >> 2] + ($k << 4) + 12 | 0) >> 2]);
|
||||
q(HEAPF32[(HEAP32[$wavelet38 >> 2] + ($k << 4) + 400 | 0) >> 2]);
|
||||
pause(4);
|
||||
// reuse $k, which already reuses $a_addr
|
||||
var $p = $k, $parameters_addr = get('burger')
|
||||
q(HEAP32[($parameters_addr + 5624 + ($p << 2) | 0) >> 2]);
|
||||
q(HEAP32[($parameters_addr + 5644 + ($p << 2) | 0) >> 2]);
|
||||
q(HEAP32[($parameters_addr + 5664 + ($p << 2) | 0) >> 2]);
|
||||
pause(5);
|
||||
// loops count as more uses!
|
||||
var $res_spec242 = get($real), $cp = get('b'), $tileno = arguments[2];
|
||||
q(HEAP32[($parameters_addr + 5624 + (($res_spec242 - 1 | 0) << 2) | 0) >> 2]);
|
||||
q(HEAP32[(HEAP32[($cp + 108 | 0) >> 2] + $tileno * 5588 + 420 | 0) >> 2]);
|
||||
pause(6);
|
||||
q($idx << 1 << 2);
|
||||
q(1 << $idx << 1); // Do not turn this into the slower 1 << $idx + 1 (which is identical though)
|
||||
print(INDENT + "Entering: _main" + "hi"); // this order should not be modified
|
||||
pause(7);
|
||||
var $tp = get('tp');
|
||||
q($tp >> 2);
|
||||
q($tp >> 2);
|
||||
q($tp >> 2);
|
||||
HEAP32[$H400] = $tp;
|
||||
HEAP32[$tp] = 5;
|
||||
HEAP32[$tp >> 2] = 5;
|
||||
HEAP32[HEAP[$tp >> 2]] = 5;
|
||||
HEAP32[HEAP[$tp >> 2] >> 2] = 5;
|
||||
pause(7);
|
||||
q(go() >> 1 << 1);
|
||||
q(go() << 1 >> 1);
|
||||
q(go() >> 1 >> 1);
|
||||
q(go() << 1 << 1);
|
||||
q(go() >> 8 << 8);
|
||||
q(go() << 8 >> 8);
|
||||
q(go() >> 8 >> 8);
|
||||
q(go() << 8 << 8);
|
||||
q((go() + 2) >> 2); // the 2 >> 2 can't be simplified
|
||||
// only values provable to have lower bits clear are ok
|
||||
var c1 = get(); // ok
|
||||
HEAP32[c1 >> 2] = 1;
|
||||
HEAP32[c1 + 4 >> 2] = 1;
|
||||
HEAP32[c1 + (get() << 2) >> 2] = 1;
|
||||
var c2 = get(); // bad constant
|
||||
HEAP32[c2 >> 2] = 1;
|
||||
HEAP32[c2 + 3 >> 2] = 1;
|
||||
HEAP32[c2 + (get() << 2) >> 2] = 1;
|
||||
var c3 = get(); // bad revshift
|
||||
HEAP32[c3 >> 2] = 1;
|
||||
HEAP32[c3 + 4 >> 2] = 1;
|
||||
HEAP32[c3 + (get() << 1) >> 2] = 1;
|
||||
var c4 = get(), c5 = get(); // bad unknown var
|
||||
HEAP32[c4 >> 2] = 1;
|
||||
HEAP32[c4 + 4 >> 2] = 1;
|
||||
HEAP32[c4 + c5 >> 2] = 1;
|
||||
}
|
||||
// EMSCRIPTEN_GENERATED_FUNCTIONS: ["shifty"]
|
|
@ -0,0 +1,17 @@
|
|||
function shifty() {
|
||||
$pPage = HEAP32[$pCur_addr + ($26 << 16 >> 16 << 2) + 116 >> 2];
|
||||
var $ead_192394 = HEAP32[$pCur_addr + ($26 << 16 >> 16 << 2) + 116 >> 2];
|
||||
$pPage2 = HEAP32[($26 << 16 >> 16 << 2) + $pCur_addr + 116];
|
||||
var $ead_192394b = HEAP32[($26 << 16 >> 16 << 2) + $pCur_addr + 116];
|
||||
$pPage2 = HEAP32[($26 << 16 >> 16) + $pCur_addr + 116];
|
||||
var $ead_192394b = HEAP32[($26 << 16 >> 16) + $pCur_addr + 116];
|
||||
q(4);
|
||||
q($13 + 8 >> 2);
|
||||
q($13 + 28 >> 2);
|
||||
q($13 + 60 >> 2);
|
||||
q($13 + $15 + 12 >> 2);
|
||||
q(HEAPF32[$output + ($j37 << 4) + 4 >> 2]);
|
||||
q($13 + 13 << 2);
|
||||
q(h() >> 2 << 2);
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
function shifty() {
|
||||
$pPage = HEAP32[$pCur_addr + 116 + ($26 << 16 >> 16 << 2) >> 2];
|
||||
var $ead_192394 = HEAP32[$pCur_addr + 116 + ($26 << 16 >> 16 << 2) >> 2];
|
||||
$pPage2 = HEAP32[$pCur_addr + 116 + ($26 << 16 >> 16 << 2)];
|
||||
var $ead_192394b = HEAP32[$pCur_addr + 116 + ($26 << 16 >> 16 << 2)];
|
||||
$pPage2 = HEAP32[$pCur_addr + 116 + ($26 << 16 >> 16)];
|
||||
var $ead_192394b = HEAP32[$pCur_addr + 116 + ($26 << 16 >> 16)];
|
||||
// We prefer to do additions then shifts, so the shift happens last, because the shift output is known to be 32-bit. So these should not change
|
||||
q(16 >> 2);
|
||||
q($13 + 8 >> 2);
|
||||
q(28 + $13 >> 2);
|
||||
q(48 + $13 + 12 >> 2);
|
||||
q($13 + $15 + 12 >> 2);
|
||||
q(HEAPF32[$output + ($j37 << 4) + 4 >> 2]);
|
||||
q(5 + $13 + 8 << 2);
|
||||
q(((h() | 0) >> 2) << 2); // removing the shifts is dangerous
|
||||
}
|
||||
// EMSCRIPTEN_GENERATED_FUNCTIONS: ["shifty"]
|
|
@ -0,0 +1,49 @@
|
|||
function _png_create_write_struct_2($user_png_ver, $error_ptr, $error_fn, $warn_fn, $mem_ptr, $malloc_fn, $free_fn) {
|
||||
var $png_ptr$s2;
|
||||
var label;
|
||||
label = 2;
|
||||
var setjmpTable = {
|
||||
"2": (function(value) {
|
||||
label = 5;
|
||||
$call1 = value;
|
||||
}),
|
||||
dummy: 0
|
||||
};
|
||||
while (1) try {
|
||||
switch (label) {
|
||||
case 2:
|
||||
var $png_ptr;
|
||||
var $call = _png_create_struct(1);
|
||||
$png_ptr = $call;
|
||||
var $call1 = (HEAP32[$png_ptr >> 2] = label, 0);
|
||||
label = 5;
|
||||
break;
|
||||
case 5:
|
||||
var $2 = $png_ptr;
|
||||
if (($call1 | 0) == 0) {
|
||||
label = 4;
|
||||
break;
|
||||
} else {
|
||||
label = 3;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
var $4 = HEAP32[($png_ptr >> 2) + (148 >> 2)];
|
||||
_png_free($2, $4);
|
||||
HEAP32[($png_ptr >> 2) + (148 >> 2)] = 0;
|
||||
_png_destroy_struct($png_ptr);
|
||||
var $retval_0 = 0;
|
||||
label = 4;
|
||||
break;
|
||||
case 4:
|
||||
var $retval_0;
|
||||
return $retval_0;
|
||||
default:
|
||||
assert(0, "bad label: " + label);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!e.longjmp) throw e;
|
||||
setjmpTable[e.label](e.value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
function _png_create_write_struct_2($user_png_ver, $error_ptr, $error_fn, $warn_fn, $mem_ptr, $malloc_fn, $free_fn) {
|
||||
var $png_ptr$s2;
|
||||
var label;
|
||||
label = 2;
|
||||
var setjmpTable = {
|
||||
"2": (function(value) {
|
||||
label = 5;
|
||||
$call1 = value;
|
||||
}),
|
||||
dummy: 0
|
||||
};
|
||||
while (1) try {
|
||||
switch (label) {
|
||||
case 2:
|
||||
var $png_ptr;
|
||||
var $call = _png_create_struct(1);
|
||||
$png_ptr = $call;
|
||||
var $call1 = (HEAP32[$png_ptr >> 2] = label, 0);
|
||||
label = 5;
|
||||
break;
|
||||
case 5:
|
||||
var $2 = $png_ptr;
|
||||
if (($call1 | 0) == 0) {
|
||||
label = 4;
|
||||
break;
|
||||
} else {
|
||||
label = 3;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
var $4 = HEAP32[($png_ptr >> 2) + (148 >> 2)];
|
||||
_png_free($2, $4);
|
||||
HEAP32[($png_ptr >> 2) + (148 >> 2)] = 0;
|
||||
_png_destroy_struct($png_ptr);
|
||||
var $retval_0 = 0;
|
||||
label = 4;
|
||||
break;
|
||||
case 4:
|
||||
var $retval_0;
|
||||
return $retval_0;
|
||||
default:
|
||||
assert(0, "bad label: " + label);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!e.longjmp) throw e;
|
||||
setjmpTable[e.label](e.value);
|
||||
}
|
||||
}
|
||||
// EMSCRIPTEN_GENERATED_FUNCTIONS: ["_png_create_write_struct_2"]
|
||||
|
Загрузка…
Ссылка в новой задаче