/// <summary> /// Return all statements after this one /// </summary> /// <param name="statement"></param> /// <returns></returns> internal static IEnumerable <IStatement> AllStatementsAfter(this IStatement statement) { var parent = statement.FindCompoundParent(); if (parent != null) { var statements = parent.Statements.SkipWhile(s => s != statement).Skip(1); foreach (var s in statements) { yield return(s); } // Now, go up one. foreach (var s in statement.Parent.AllStatementsAfter()) { yield return(s); } } }
/// <summary> /// Return all statements before this one. we march to the start of each block, and then up, and then all ones previous to that. Each time we go /// up a level we ignore that one. /// </summary> /// <param name="statement"></param> /// <returns></returns> internal static IEnumerable <IStatement> AllStatementsPrevious(this IStatement statement) { var parent = statement.FindCompoundParent(); if (parent != null) { var statements = parent.Statements.TakeWhile(s => s != statement).Reverse(); foreach (var s in statements) { yield return(s); } // Now, go up one. foreach (var s in statement.Parent.AllStatementsPrevious()) { yield return(s); } } }
/// <summary> /// sToPop is a member of block, and can be or is the first statement. We see if we can pop it up /// a level, and then start a scan for an equivalent statement, marching up the list. If we /// find an equivalent statement, we will perform the combination and removal. /// </summary> /// <param name="block">The block of statements that holds sToPop</param> /// <param name="sToPop">The statement to try to up level.</param> /// <param name="previousStatements">Statements before this one in this block. This block might not actually contain this statement, in which case we must have this!</param> /// <param name="followingStatements">Statements after this one in this block. This block might not actually contain this statement, in which case we must have this!</param> /// <returns>True if something was done to the statement, false if we aborted for whatever reason.</returns> private static bool FindEquivalentAboveAndCombine(IStatementCompound block, IStatement sToPop, IEnumerable<IStatement> previousStatements = null, IEnumerable<IStatement> followingStatements = null, IStatement betweenStatement = null ) { // Can we combine these guys? This is when one sits in the other. if (!(block is IStatementLoop) && block.TryCombineStatement(sToPop, new BlockRenamer(sToPop.Parent.FindBookingParent(), block.FindBookingParent()))) { sToPop.FindCompoundParent().Remove(sToPop); return false; } // If we can't get data flow information about a statement, then we can't do anything. var sInfo = sToPop as ICMStatementInfo; if (sInfo == null) { return false; } // For this next step we need to fetch the list of statements above us. Either it has // been supplied to us, or we will have to generate it. if (previousStatements == null) { previousStatements = block.Statements.TakeWhile(s => s != sInfo).Reverse(); followingStatements = block.Statements.SkipWhile(s => s != sInfo).Skip(1); betweenStatement = sToPop; } // Make sure we can get the statement to the top of the block. As we move it // forward, we want to also see if we can combine it in a straight-up way // with each statement as we go by it. bool madeItToTheFront = true; foreach (var prevStatement in previousStatements) { if (MakeStatmentsEquivalent(prevStatement, sToPop)) { return true; } if (!StatementCommutes(prevStatement, sToPop)) { madeItToTheFront = false; } } // Next, lets see if there isn't a statement *after* this one that we can combine it with. However, // to do this, we have to move the statements *after* forward previous to this one. So a little painful. // No need to do this if we working at the level of the statement: this ground will automatically be covered // later in the loop. if (betweenStatement != sToPop && betweenStatement is ICMCompoundStatementInfo) { foreach (var followStatement in followingStatements) { if (followStatement is ICMStatementInfo) { // Can we commute this statement from where it is to before the statement we are working on? if (StatementCommutes(followStatement, followingStatements.TakeWhile(f => f != followStatement).Reverse())) { // Next is the tricky part. We are now sitting one down from the block that contains // the sToPop statement. Can we move it up above the block? If the statements are the same, // then we know it is ok to move it pass all the contents of the block (otherwise we would not be here). // But what if it is an if statement, and the if statement depends on something in sToPop? Then // we can't move it. var betweenAsBlock = betweenStatement as ICMCompoundStatementInfo; if (betweenAsBlock.CommutesWithGatingExpressions(followStatement as ICMStatementInfo)) { if (MakeStatmentsEquivalent(followStatement, sToPop)) { // To keep continuity and unitarity, this follow statement now has to be moved before the betweenStatement! var parent = followStatement.Parent as IStatementCompound; parent.Remove(followStatement); parent.AddBefore(followStatement, betweenStatement); return true; } } } } } } // Now the only option left is to pop it up one level. We can do that only if we were able to // shift the statement all the way to the front. if (!madeItToTheFront) { return false; } // The statement can be moved to the top of the block, and isn't the same as // anything else we passed. Can we pull it out one level? // The key to answering this is: are all the variables it needs defined at the next // level up? And if not, are the missing ones simply declared down here and need to be moved up? var nParent = block.Parent.FindBookingParent(); if (nParent == null) { return false; } var sDependent = sInfo.DependentVariables; var availAtParent = nParent.AllDeclaredVariables.Select(n => n.RawValue).Intersect(sDependent); IEnumerable<string> declaredInBlock = Enumerable.Empty<string>(); if (block is IBookingStatementBlock) { declaredInBlock = (block as IBookingStatementBlock).DeclaredVariables.Select(np => np.RawValue).Intersect(sDependent); } if ((availAtParent.Count() + declaredInBlock.Count()) != sDependent.Count()) { return false; } // If this there is a variable declared in the block internally, then we can't lift it up and out. // Also make sure that we can lift it past if there is a gating expression. if (block is ICMCompoundStatementInfo) { var compoundInfo = block as ICMCompoundStatementInfo; if (compoundInfo.InternalResultVarialbes.Select(p => p.RawValue).Intersect(sDependent).Any()) { return false; } if (!compoundInfo.CommutesWithGatingExpressions(sInfo)) { return false; } } // If we are going to try to lift past a loop, we have to make sure the statement is idempotent. if (block is IStatementLoop && !StatementIdempotent(sToPop)) { return false; } // And the next figure out where we are in the list of statements. var nPrevStatements = nParent.Statements.TakeWhile(ps => ps != block).Reverse(); var nFollowStatements = nParent.Statements.SkipWhile(ps => ps != block).Skip(1); // And repeat one level up with some tail recursion! var statementMoved = FindEquivalentAboveAndCombine(nParent, sToPop, nPrevStatements, nFollowStatements, block); // There is one other thing to try. If we couldn't move it above us (e.g. statementMoved is false), it could be // we can leave the statement here, rather than in its original location. if (!statementMoved) { var parentsToBlock = sToPop .WalkParents(false) .TakeWhile(s => s != block) .Concat(new IStatementCompound[] { block }).ToArray(); // If no lifting out of some statement between us and the statement, then don't do it. var parentsNotOK = parentsToBlock .Where(s => s is ICMCompoundStatementInfo) .Cast<ICMCompoundStatementInfo>() .Where(s => !s.AllowNormalBubbleUp); if (parentsNotOK.Any()) { return false; } // The next thing we have to double check is that we can do the lifting, and nothing we are going to // lift is going to impact some variable. var dependents = sInfo.DependentVariables; var dependentAffected = parentsToBlock .Select(p => p.CheckForVariableAsInternalResult(dependents)) .Where(t => t); if (dependentAffected.Any()) { return false; } return MoveStatement(sToPop, block); } return statementMoved; }
/// <summary> /// sToPop is a member of block, and can be or is the first statement. We see if we can pop it up /// a level, and then start a scan for an equivalent statement, marching up the list. If we /// find an equivalent statement, we will perform the combination and removal. /// </summary> /// <param name="block">The block of statements that holds sToPop</param> /// <param name="sToPop">The statement to try to up level.</param> /// <param name="previousStatements">Statements before this one in this block. This block might not actually contain this statement, in which case we must have this!</param> /// <param name="followingStatements">Statements after this one in this block. This block might not actually contain this statement, in which case we must have this!</param> /// <returns>True if something was done to the statement, false if we aborted for whatever reason.</returns> private static bool FindEquivalentAboveAndCombine(IStatementCompound block, IStatement sToPop, IEnumerable <IStatement> previousStatements = null, IEnumerable <IStatement> followingStatements = null, IStatement betweenStatement = null ) { // Can we combine these guys? This is when one sits in the other. if (!(block is IStatementLoop) && block.TryCombineStatement(sToPop, new BlockRenamer(sToPop.Parent.FindBookingParent(), block.FindBookingParent()))) { sToPop.FindCompoundParent().Remove(sToPop); return(false); } // If we can't get data flow information about a statement, then we can't do anything. var sInfo = sToPop as ICMStatementInfo; if (sInfo == null) { return(false); } // For this next step we need to fetch the list of statements above us. Either it has // been supplied to us, or we will have to generate it. if (previousStatements == null) { previousStatements = block.Statements.TakeWhile(s => s != sInfo).Reverse(); followingStatements = block.Statements.SkipWhile(s => s != sInfo).Skip(1); betweenStatement = sToPop; } // Make sure we can get the statement to the top of the block. As we move it // forward, we want to also see if we can combine it in a straight-up way // with each statement as we go by it. bool madeItToTheFront = true; foreach (var prevStatement in previousStatements) { if (MakeStatmentsEquivalent(prevStatement, sToPop)) { return(true); } if (!StatementCommutes(prevStatement, sToPop)) { madeItToTheFront = false; } } // Next, lets see if there isn't a statement *after* this one that we can combine it with. However, // to do this, we have to move the statements *after* forward previous to this one. So a little painful. // No need to do this if we working at the level of the statement: this ground will automatically be covered // later in the loop. if (betweenStatement != sToPop && betweenStatement is ICMCompoundStatementInfo) { foreach (var followStatement in followingStatements) { if (followStatement is ICMStatementInfo) { // Can we commute this statement from where it is to before the statement we are working on? if (StatementCommutes(followStatement, followingStatements.TakeWhile(f => f != followStatement).Reverse())) { // Next is the tricky part. We are now sitting one down from the block that contains // the sToPop statement. Can we move it up above the block? If the statements are the same, // then we know it is ok to move it pass all the contents of the block (otherwise we would not be here). // But what if it is an if statement, and the if statement depends on something in sToPop? Then // we can't move it. var betweenAsBlock = betweenStatement as ICMCompoundStatementInfo; if (betweenAsBlock.CommutesWithGatingExpressions(followStatement as ICMStatementInfo)) { if (MakeStatmentsEquivalent(followStatement, sToPop)) { // To keep continuity and unitarity, this follow statement now has to be moved before the betweenStatement! var parent = followStatement.Parent as IStatementCompound; parent.Remove(followStatement); parent.AddBefore(followStatement, betweenStatement); return(true); } } } } } } // Now the only option left is to pop it up one level. We can do that only if we were able to // shift the statement all the way to the front. if (!madeItToTheFront) { return(false); } // The statement can be moved to the top of the block, and isn't the same as // anything else we passed. Can we pull it out one level? // The key to answering this is: are all the variables it needs defined at the next // level up? And if not, are the missing ones simply declared down here and need to be moved up? var nParent = block.Parent.FindBookingParent(); if (nParent == null) { return(false); } var sDependent = sInfo.DependentVariables; var availAtParent = nParent.AllDeclaredVariables.Select(n => n.RawValue).Intersect(sDependent); IEnumerable <string> declaredInBlock = Enumerable.Empty <string>(); if (block is IBookingStatementBlock) { declaredInBlock = (block as IBookingStatementBlock).DeclaredVariables.Select(np => np.RawValue).Intersect(sDependent); } if ((availAtParent.Count() + declaredInBlock.Count()) != sDependent.Count()) { return(false); } // If this there is a variable declared in the block internally, then we can't lift it up and out. // Also make sure that we can lift it past if there is a gating expression. if (block is ICMCompoundStatementInfo) { var compoundInfo = block as ICMCompoundStatementInfo; if (compoundInfo.InternalResultVarialbes.Select(p => p.RawValue).Intersect(sDependent).Any()) { return(false); } if (!compoundInfo.CommutesWithGatingExpressions(sInfo)) { return(false); } } // If we are going to try to lift past a loop, we have to make sure the statement is idempotent. if (block is IStatementLoop && !StatementIdempotent(sToPop)) { return(false); } // And the next figure out where we are in the list of statements. var nPrevStatements = nParent.Statements.TakeWhile(ps => ps != block).Reverse(); var nFollowStatements = nParent.Statements.SkipWhile(ps => ps != block).Skip(1); // And repeat one level up with some tail recursion! var statementMoved = FindEquivalentAboveAndCombine(nParent, sToPop, nPrevStatements, nFollowStatements, block); // There is one other thing to try. If we couldn't move it above us (e.g. statementMoved is false), it could be // we can leave the statement here, rather than in its original location. if (!statementMoved) { var parentsToBlock = sToPop .WalkParents(false) .TakeWhile(s => s != block) .Concat(new IStatementCompound[] { block }).ToArray(); // If no lifting out of some statement between us and the statement, then don't do it. var parentsNotOK = parentsToBlock .Where(s => s is ICMCompoundStatementInfo) .Cast <ICMCompoundStatementInfo>() .Where(s => !s.AllowNormalBubbleUp); if (parentsNotOK.Any()) { return(false); } // The next thing we have to double check is that we can do the lifting, and nothing we are going to // lift is going to impact some variable. var dependents = sInfo.DependentVariables; var dependentAffected = parentsToBlock .Select(p => p.CheckForVariableAsInternalResult(dependents)) .Where(t => t); if (dependentAffected.Any()) { return(false); } return(MoveStatement(sToPop, block)); } return(statementMoved); }