utility.addSchedulePhase(phase);
    public void computeMinLatencySchedule(        PipelineSchedulingUtility utility,        int childrenExecs[],        int dataInBuffers[],        boolean forcedPull)    {        // if only one child, just add all the appropriate phases!        // BUGBUG this is a HACK!        if (getNumChildren() == 1)        {            while (childrenExecs[0] != 0)            {                utility.addSchedulePhase(                    utility.getChildNextPhase(getHierarchicalChild(0)));                utility.advanceChildSchedule(getHierarchicalChild(0));                childrenExecs[0]--;            }            return;        }        // reset the buffer below the pipeline (it's not actually used        // for any computation)        dataInBuffers[getNumChildren()] = 0;        boolean completed;        do        {            boolean fakePull = false;            int fakePullSize = 0;            // calculate how much data the pipeline is supposed to push out            // during execution of this schedule            int pipelineOverallPush = 0;            {                int lastChildIndex = getNumChildren() - 1;                StreamInterface child = getHierarchicalChild(lastChildIndex);                int nPhase = childrenExecs[lastChildIndex] - 1;                for (; nPhase >= 0; nPhase--)                {                    PhasingSchedule phase =                        utility.getChildPhase(child, nPhase);                    pipelineOverallPush += phase.getOverallPush();                }                if (pipelineOverallPush == 0 && !forcedPull)                {                    pipelineOverallPush = childrenExecs[lastChildIndex];                    fakePull = true;                    fakePullSize =                        (int)Math.ceil(                            Math.sqrt(                                child.getSteadyPop()                                    * super.getChildNumExecs(lastChildIndex)));                }            }            // repeat while the last child still needs to pull some data            while (dataInBuffers[getNumChildren()] < pipelineOverallPush)            {                int numChildExecs[] = new int[getNumChildren()];                int dataPushed[] = new int[getNumChildren()];                int dataPeeked[] = new int[getNumChildren()];                int dataPopped[] = new int[getNumChildren()];                // figure out how many times each child needs to get executed                {                    int nextChildDataNeeded = 1, nChild;                    if (fakePull)                        nextChildDataNeeded =                            MIN(                                fakePullSize,                                childrenExecs[getNumChildren() - 1]);                    // go from bottom to top to pull data                    for (nChild = getNumChildren() - 1; nChild >= 0; nChild--)                    {                        StreamInterface child = getHierarchicalChild(nChild);                        // repeat while still need to provide data                         // for the next child                        while (nextChildDataNeeded > 0)                        {                            // figure out how much this phase/stage will                             // push/pop/peek                            PhasingSchedule phase =                                utility.getChildPhase(                                    child,                                    numChildExecs[nChild]);                            int phasePeek = phase.getOverallPeek();                            int phasePop = phase.getOverallPop();                            int phasePush = phase.getOverallPush();                            // update the overall phase child peek/pop                            dataPeeked[nChild] =                                MAX(                                    dataPeeked[nChild],                                    dataPopped[nChild] + phasePeek);                            dataPopped[nChild] += phasePop;                            // reduce the amount of data still needed                            // by the next child                            nextChildDataNeeded -= phasePush;                            if (fakePull && nChild == getNumChildren() - 1)                            {                                nextChildDataNeeded -= 1;                            }                            // note that I just executed another stage/phase                             // of this child                            numChildExecs[nChild]++;                            dataPushed[nChild] += phasePush;                        }                        // figure out how much data the previous child                        // still needs to provide:                        nextChildDataNeeded =                            dataPeeked[nChild] - dataInBuffers[nChild];                    }                    // and now go from top to bottom to push data                    int dataAdded = 0;                    for (nChild = 1; nChild < getNumChildren(); nChild++)                    {                        StreamInterface child = getHierarchicalChild(nChild);                        // repeat while still can run on prev child's data                        do                        {                            if (numChildExecs[nChild] == childrenExecs[nChild])                                break;                            // figure out how much this phase/stage will                             // push/pop/peek                            PhasingSchedule phase =                                utility.getChildPhase(                                    child,                                    numChildExecs[nChild]);                            int phasePeek = phase.getOverallPeek();                            int phasePop = phase.getOverallPop();                            int phasePush = phase.getOverallPush();                            // update the overall phase child peek/pop                            dataPeeked[nChild] =                                MAX(                                    dataPeeked[nChild],                                    dataPopped[nChild] + phasePeek);                            dataPopped[nChild] += phasePop;                            if (dataPeeked[nChild]                                > dataInBuffers[nChild] + dataPushed[nChild - 1])                                break;                            // note that I just executed another stage/phase                             // of this child                            numChildExecs[nChild]++;                            dataPushed[nChild] += phasePush;                        }                        while (true);                    }                }                if (fakePull)                {                    pipelineOverallPush -= numChildExecs[getNumChildren() - 1];                }                // construct an actual stage of the schedule and execute it                {                    PhasingSchedule steadyPhase = new PhasingSchedule(this);                    int nChild;                    for (nChild = 0; nChild < getNumChildren(); nChild++)                    {                        StreamInterface child = getHierarchicalChild(nChild);                        for (;                            numChildExecs[nChild] > 0;                            numChildExecs[nChild]--)                        {                            PhasingSchedule phase =                                utility.getChildNextPhase(child);                            // add this stage to the init schedule                            steadyPhase.appendPhase(phase);                            utility.advanceChildSchedule(child);                            // better have enough data in the buffer to execute                             // this phase                            ASSERT(                                nChild == 0                                    || dataInBuffers[nChild]                                        >= phase.getOverallPeek());                            // adjust buffers for this child                            dataInBuffers[nChild] -= phase.getOverallPop();                            dataInBuffers[nChild + 1] += phase.getOverallPush();                            // mark down that this child had another phase executed                            childrenExecs[nChild]--;                        }                    }                    // add the init stage to the init schedule                    utility.addSchedulePhase(steadyPhase);                }            }            // check if all children have been drained            // this needs to be done only if I'm executing in a            // forced pull mode            completed = true;            if (forcedPull)            {                int nChild;                for (nChild = 0; nChild < getNumChildren(); nChild++)                {                    completed &= (dataInBuffers[nChild] == 0);                }            }        }        while (!completed && forcedPull);        // it is possible that I need another phase to execute clean-up        // phases of the children that don't output from the pipeline        // (all but the bottom most child)        {            PhasingSchedule extraPhase = new PhasingSchedule(this);            int nChild;            for (nChild = 0; nChild < getNumChildren(); nChild++)            {                StreamInterface child = getHierarchicalChild(nChild);                for (; childrenExecs[nChild] > 0; childrenExecs[nChild]--)                {                    PhasingSchedule phase = utility.getChildNextPhase(child);                    // add this stage to the init schedule                    extraPhase.appendPhase(phase);                    utility.advanceChildSchedule(child);                    // better have enough data in the buffer to execute                     // this phase                    ASSERT(                        nChild == 0                            || dataInBuffers[nChild] >= phase.getOverallPeek());                    // adjust buffers for this child                    dataInBuffers[nChild] -= phase.getOverallPop();                    dataInBuffers[nChild + 1] += phase.getOverallPush();                }                // make sure that I've executed every child the right number                // of phases.  if this assert goes off, the error is not in                // the code right above (the for above will never execute)                // but in the init/steady schedule calculcation!                ASSERT(childrenExecs[nChild] == 0);            }            // have I done anything?            if (extraPhase.getNumPhases() != 0)            {                // yes - add the extra phase to the schedule!                utility.addSchedulePhase(extraPhase);            }        }    }