internal static bool ReplacePushPopPattern(BlockStatement b, IMetadataHost host) {
Contract.Requires(b != null);
Contract.Requires(host != null);
bool replacedPattern = false;
var statements = b.Statements;
for (int i = 0; i < statements.Count-1; i++) {
//First identify count consecutive push statements
int count = 0;
while (i+count < statements.Count-1 && statements[i+count] is PushStatement) count++;
if (count == 0) continue;
Contract.Assert(i+count < statements.Count);
Contract.Assert(count < statements.Count);
for (int j = 0; j < count; j++) {
Contract.Assume(statements[i+j] is PushStatement);
if (((PushStatement)statements[i+j]).ValueToPush is DupValue) goto nextIteration;
}
Contract.Assert(i >= 0);
Contract.Assert(i < statements.Count-1);
//If any of the push statements (other than the first one) contains pop expressions, replace them with the corresponding push values and remove the pushes.
int pushcount = 1; //the number of push statements that are eligble for removal at this point.
for (int j = i + 1; j < i + count; j++) {
Contract.Assume(j < statements.Count); //because i+count < statements.Count for the initial value of count and count just decreases
Contract.Assume(j >= 0); //because i >= 0 and j never decreases to less than i
Contract.Assume(statements[j] is PushStatement); //because i < j < i+count and i..i+count-1 are all push statements
PushStatement st = (PushStatement)statements[j];
PopCounter pcc = new PopCounter();
pcc.Traverse(st.ValueToPush);
int numberOfPushesToRemove = pushcount;
if (pcc.count > 0) {
if (pcc.count < numberOfPushesToRemove) numberOfPushesToRemove = pcc.count;
int firstPushToRemove = j - numberOfPushesToRemove;
PopReplacer prr = new PopReplacer(host, statements, firstPushToRemove, pcc.count-numberOfPushesToRemove);
st.ValueToPush = prr.Rewrite(st.ValueToPush);
statements.RemoveRange(firstPushToRemove, numberOfPushesToRemove);
if (pcc.count > numberOfPushesToRemove) return true; //We've consumed all of the pushes and we're done.
count -= numberOfPushesToRemove; //Fewer pushes now remain
pushcount -= numberOfPushesToRemove; //Likewise fewer eligible pushes remain
//Start over again at firstPushToRemove, which now indexes the first statement not yet considered.
j = firstPushToRemove-1;
continue;
}
pushcount++;
}
Contract.Assume(count >= 0);
Contract.Assume(i+count < statements.Count);
var nextStatement = statements[i + count];
Contract.Assume(nextStatement != null);
if (!(nextStatement is IExpressionStatement || nextStatement is IPushStatement || nextStatement is IReturnStatement || nextStatement is IThrowStatement)) continue;
PopCounter pc = new PopCounter();
pc.Traverse(nextStatement);
if (pc.count == 0) continue;
if (pc.count < count) {
i += count-pc.count; //adjust i to point to the first push statement to remove because of subsequent pops.
count = pc.count;
}
Contract.Assume(i < statements.Count);
PopReplacer pr = new PopReplacer(host, statements, i, pc.count-count);
pr.Rewrite(nextStatement);
var s = nextStatement as Statement;
if (s != null)
s.Locations.AddRange(statements[i].Locations);
Contract.Assume(count >= 0);
Contract.Assert(i+count < statements.Count);
statements.RemoveRange(i, count);
replacedPattern = true;
nextIteration: ;
}
return replacedPattern;
}