cpu_impl.hh revision 8887:20ea02da9c53
1/*
2 * Copyright (c) 2011 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2006 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Kevin Lim
41 *          Geoffrey Blake
42 */
43
44#include <list>
45#include <string>
46
47#include "arch/vtophys.hh"
48#include "base/refcnt.hh"
49#include "config/the_isa.hh"
50#include "cpu/base_dyn_inst.hh"
51#include "cpu/exetrace.hh"
52#include "cpu/simple_thread.hh"
53#include "cpu/static_inst.hh"
54#include "cpu/thread_context.hh"
55#include "cpu/checker/cpu.hh"
56#include "debug/Checker.hh"
57#include "sim/full_system.hh"
58#include "sim/sim_object.hh"
59#include "sim/stats.hh"
60
61using namespace std;
62using namespace TheISA;
63
64template <class Impl>
65void
66Checker<Impl>::advancePC(Fault fault)
67{
68    if (fault != NoFault) {
69        curMacroStaticInst = StaticInst::nullStaticInstPtr;
70        fault->invoke(tc, curStaticInst);
71        predecoder.reset();
72    } else {
73        if (curStaticInst) {
74            if (curStaticInst->isLastMicroop())
75                curMacroStaticInst = StaticInst::nullStaticInstPtr;
76            TheISA::PCState pcState = thread->pcState();
77            TheISA::advancePC(pcState, curStaticInst);
78            thread->pcState(pcState);
79            DPRINTF(Checker, "Advancing PC to %s.\n", thread->pcState());
80        }
81    }
82}
83//////////////////////////////////////////////////
84
85template <class Impl>
86void
87Checker<Impl>::handlePendingInt()
88{
89    DPRINTF(Checker, "IRQ detected at PC: %s with %d insts in buffer\n",
90                     thread->pcState(), instList.size());
91    DynInstPtr boundaryInst = NULL;
92    if (!instList.empty()) {
93        // Set the instructions as completed and verify as much as possible.
94        DynInstPtr inst;
95        typename std::list<DynInstPtr>::iterator itr;
96
97        for (itr = instList.begin(); itr != instList.end(); itr++) {
98            (*itr)->setCompleted();
99        }
100
101        inst = instList.front();
102        boundaryInst = instList.back();
103        verify(inst); // verify the instructions
104        inst = NULL;
105    }
106    if ((!boundaryInst && curMacroStaticInst &&
107          curStaticInst->isDelayedCommit() &&
108          !curStaticInst->isLastMicroop()) ||
109        (boundaryInst && boundaryInst->isDelayedCommit() &&
110         !boundaryInst->isLastMicroop())) {
111        panic("%lli: Trying to take an interrupt in middle of "
112              "a non-interuptable instruction!", curTick());
113    }
114    boundaryInst = NULL;
115    predecoder.reset();
116    curMacroStaticInst = StaticInst::nullStaticInstPtr;
117}
118
119template <class Impl>
120void
121Checker<Impl>::verify(DynInstPtr &completed_inst)
122{
123    DynInstPtr inst;
124
125    // Make sure serializing instructions are actually
126    // seen as serializing to commit. instList should be
127    // empty in these cases.
128    if ((completed_inst->isSerializing() ||
129        completed_inst->isSerializeBefore()) &&
130        (!instList.empty() ?
131         (instList.front()->seqNum != completed_inst->seqNum) : 0)) {
132        panic("%lli: Instruction sn:%lli at PC %s is serializing before but is"
133              " entering instList with other instructions\n", curTick(),
134              completed_inst->seqNum, completed_inst->pcState());
135    }
136
137    // Either check this instruction, or add it to a list of
138    // instructions waiting to be checked.  Instructions must be
139    // checked in program order, so if a store has committed yet not
140    // completed, there may be some instructions that are waiting
141    // behind it that have completed and must be checked.
142    if (!instList.empty()) {
143        if (youngestSN < completed_inst->seqNum) {
144            DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n",
145                    completed_inst->seqNum, completed_inst->pcState());
146            instList.push_back(completed_inst);
147            youngestSN = completed_inst->seqNum;
148        }
149
150        if (!instList.front()->isCompleted()) {
151            return;
152        } else {
153            inst = instList.front();
154            instList.pop_front();
155        }
156    } else {
157        if (!completed_inst->isCompleted()) {
158            if (youngestSN < completed_inst->seqNum) {
159                DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%s to list\n",
160                        completed_inst->seqNum, completed_inst->pcState());
161                instList.push_back(completed_inst);
162                youngestSN = completed_inst->seqNum;
163            }
164            return;
165        } else {
166            if (youngestSN < completed_inst->seqNum) {
167                inst = completed_inst;
168                youngestSN = completed_inst->seqNum;
169            } else {
170                return;
171            }
172        }
173    }
174
175    // Make sure a serializing instruction is actually seen as
176    // serializing. instList should be empty here
177    if (inst->isSerializeAfter() && !instList.empty()) {
178        panic("%lli: Instruction sn:%lli at PC %s is serializing after but is"
179             " exiting instList with other instructions\n", curTick(),
180             completed_inst->seqNum, completed_inst->pcState());
181    }
182    unverifiedInst = inst;
183    inst = NULL;
184
185    // Try to check all instructions that are completed, ending if we
186    // run out of instructions to check or if an instruction is not
187    // yet completed.
188    while (1) {
189        DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%s.\n",
190                unverifiedInst->seqNum, unverifiedInst->pcState());
191        unverifiedReq = NULL;
192        unverifiedReq = unverifiedInst->reqToVerify;
193        unverifiedMemData = unverifiedInst->memData;
194        // Make sure results queue is empty
195        while (!result.empty()) {
196            result.pop();
197        }
198        numCycles++;
199
200        Fault fault = NoFault;
201
202        // maintain $r0 semantics
203        thread->setIntReg(ZeroReg, 0);
204#ifdef TARGET_ALPHA
205        thread->setFloatRegDouble(ZeroReg, 0.0);
206#endif // TARGET_ALPHA
207
208        // Check if any recent PC changes match up with anything we
209        // expect to happen.  This is mostly to check if traps or
210        // PC-based events have occurred in both the checker and CPU.
211        if (changedPC) {
212            DPRINTF(Checker, "Changed PC recently to %s\n",
213                    thread->pcState());
214            if (willChangePC) {
215                if (newPCState == thread->pcState()) {
216                    DPRINTF(Checker, "Changed PC matches expected PC\n");
217                } else {
218                    warn("%lli: Changed PC does not match expected PC, "
219                         "changed: %s, expected: %s",
220                         curTick(), thread->pcState(), newPCState);
221                    CheckerCPU::handleError();
222                }
223                willChangePC = false;
224            }
225            changedPC = false;
226        }
227        if (changedNextPC) {
228            DPRINTF(Checker, "Changed NextPC recently to %#x\n",
229                    thread->nextInstAddr());
230            changedNextPC = false;
231        }
232
233        // Try to fetch the instruction
234        uint64_t fetchOffset = 0;
235        bool fetchDone = false;
236
237        while (!fetchDone) {
238            Addr fetch_PC = thread->instAddr();
239            fetch_PC = (fetch_PC & PCMask) + fetchOffset;
240
241            // If not in the middle of a macro instruction
242            if (!curMacroStaticInst) {
243                // set up memory request for instruction fetch
244                memReq = new Request(unverifiedInst->threadNumber, fetch_PC,
245                                     sizeof(MachInst),
246                                     0,
247                                     masterId,
248                                     fetch_PC, thread->contextId(),
249                                     unverifiedInst->threadNumber);
250                memReq->setVirt(0, fetch_PC, sizeof(MachInst),
251                                Request::INST_FETCH, masterId, thread->instAddr());
252
253
254                fault = itb->translateFunctional(memReq, tc, BaseTLB::Execute);
255
256                if (fault != NoFault) {
257                    if (unverifiedInst->getFault() == NoFault) {
258                        // In this case the instruction was not a dummy
259                        // instruction carrying an ITB fault.  In the single
260                        // threaded case the ITB should still be able to
261                        // translate this instruction; in the SMT case it's
262                        // possible that its ITB entry was kicked out.
263                        warn("%lli: Instruction PC %s was not found in the "
264                             "ITB!", curTick(), thread->pcState());
265                        handleError(unverifiedInst);
266
267                        // go to the next instruction
268                        advancePC(NoFault);
269
270                        // Give up on an ITB fault..
271                        delete memReq;
272                        unverifiedInst = NULL;
273                        return;
274                    } else {
275                        // The instruction is carrying an ITB fault.  Handle
276                        // the fault and see if our results match the CPU on
277                        // the next tick().
278                        fault = unverifiedInst->getFault();
279                        delete memReq;
280                        break;
281                    }
282                } else {
283                    PacketPtr pkt = new Packet(memReq,
284                                               MemCmd::ReadReq,
285                                               Packet::Broadcast);
286
287                    pkt->dataStatic(&machInst);
288                    icachePort->sendFunctional(pkt);
289                    machInst = gtoh(machInst);
290
291                    delete memReq;
292                    delete pkt;
293                }
294            }
295
296            if (fault == NoFault) {
297                TheISA::PCState pcState = thread->pcState();
298
299                if (isRomMicroPC(pcState.microPC())) {
300                    fetchDone = true;
301                    curStaticInst =
302                        microcodeRom.fetchMicroop(pcState.microPC(), NULL);
303                } else if (!curMacroStaticInst) {
304                    //We're not in the middle of a macro instruction
305                    StaticInstPtr instPtr = NULL;
306
307                    //Predecode, ie bundle up an ExtMachInst
308                    predecoder.setTC(thread->getTC());
309                    //If more fetch data is needed, pass it in.
310                    Addr fetchPC = (pcState.instAddr() & PCMask) + fetchOffset;
311                    predecoder.moreBytes(pcState, fetchPC, machInst);
312
313                    //If an instruction is ready, decode it.
314                    //Otherwise, we'll have to fetch beyond the
315                    //MachInst at the current pc.
316                    if (predecoder.extMachInstReady()) {
317                        fetchDone = true;
318                        ExtMachInst newMachInst =
319                            predecoder.getExtMachInst(pcState);
320                        thread->pcState(pcState);
321                        instPtr = thread->decoder.decode(newMachInst,
322                                                         pcState.instAddr());
323                        machInst = newMachInst;
324                    } else {
325                        fetchDone = false;
326                        fetchOffset += sizeof(TheISA::MachInst);
327                    }
328
329                    //If we decoded an instruction and it's microcoded,
330                    //start pulling out micro ops
331                    if (instPtr && instPtr->isMacroop()) {
332                        curMacroStaticInst = instPtr;
333                        curStaticInst =
334                            instPtr->fetchMicroop(pcState.microPC());
335                    } else {
336                        curStaticInst = instPtr;
337                    }
338                } else {
339                    // Read the next micro op from the macro-op
340                    curStaticInst =
341                        curMacroStaticInst->fetchMicroop(pcState.microPC());
342                    fetchDone = true;
343                }
344            }
345        }
346        // reset predecoder on Checker
347        predecoder.reset();
348
349        // Check Checker and CPU get same instruction, and record
350        // any faults the CPU may have had.
351        Fault unverifiedFault;
352        if (fault == NoFault) {
353            unverifiedFault = unverifiedInst->getFault();
354
355            // Checks that the instruction matches what we expected it to be.
356            // Checks both the machine instruction and the PC.
357            validateInst(unverifiedInst);
358        }
359
360        // keep an instruction count
361        numInst++;
362
363
364        // Either the instruction was a fault and we should process the fault,
365        // or we should just go ahead execute the instruction.  This assumes
366        // that the instruction is properly marked as a fault.
367        if (fault == NoFault) {
368            // Execute Checker instruction and trace
369            if (!unverifiedInst->isUnverifiable()) {
370                Trace::InstRecord *traceData = tracer->getInstRecord(curTick(),
371                                                           tc,
372                                                           curStaticInst,
373                                                           pcState(),
374                                                           curMacroStaticInst);
375                fault = curStaticInst->execute(this, traceData);
376                if (traceData) {
377                    traceData->dump();
378                    delete traceData;
379                }
380            }
381
382            if (fault == NoFault && unverifiedFault == NoFault) {
383                thread->funcExeInst++;
384                // Checks to make sure instrution results are correct.
385                validateExecution(unverifiedInst);
386
387                if (curStaticInst->isLoad()) {
388                    ++numLoad;
389                }
390            } else if (fault != NoFault && unverifiedFault == NoFault) {
391                panic("%lli: sn: %lli at PC: %s took a fault in checker "
392                      "but not in driver CPU\n", curTick(),
393                      unverifiedInst->seqNum, unverifiedInst->pcState());
394            } else if (fault == NoFault && unverifiedFault != NoFault) {
395                panic("%lli: sn: %lli at PC: %s took a fault in driver "
396                      "CPU but not in checker\n", curTick(),
397                      unverifiedInst->seqNum, unverifiedInst->pcState());
398            }
399        }
400
401        // Take any faults here
402        if (fault != NoFault) {
403            if (FullSystem) {
404                fault->invoke(tc, curStaticInst);
405                willChangePC = true;
406                newPCState = thread->pcState();
407                DPRINTF(Checker, "Fault, PC is now %s\n", newPCState);
408                curMacroStaticInst = StaticInst::nullStaticInstPtr;
409            }
410        } else {
411           advancePC(fault);
412        }
413
414        if (FullSystem) {
415            // @todo: Determine if these should happen only if the
416            // instruction hasn't faulted.  In the SimpleCPU case this may
417            // not be true, but in the O3 or Ozone case this may be true.
418            Addr oldpc;
419            int count = 0;
420            do {
421                oldpc = thread->instAddr();
422                system->pcEventQueue.service(tc);
423                count++;
424            } while (oldpc != thread->instAddr());
425            if (count > 1) {
426                willChangePC = true;
427                newPCState = thread->pcState();
428                DPRINTF(Checker, "PC Event, PC is now %s\n", newPCState);
429            }
430        }
431
432        // @todo:  Optionally can check all registers. (Or just those
433        // that have been modified).
434        validateState();
435
436        // Continue verifying instructions if there's another completed
437        // instruction waiting to be verified.
438        if (instList.empty()) {
439            break;
440        } else if (instList.front()->isCompleted()) {
441            unverifiedInst = NULL;
442            unverifiedInst = instList.front();
443            instList.pop_front();
444        } else {
445            break;
446        }
447    }
448    unverifiedInst = NULL;
449}
450
451template <class Impl>
452void
453Checker<Impl>::switchOut()
454{
455    instList.clear();
456}
457
458template <class Impl>
459void
460Checker<Impl>::takeOverFrom(BaseCPU *oldCPU)
461{
462}
463
464template <class Impl>
465void
466Checker<Impl>::validateInst(DynInstPtr &inst)
467{
468    if (inst->instAddr() != thread->instAddr()) {
469        warn("%lli: PCs do not match! Inst: %s, checker: %s",
470             curTick(), inst->pcState(), thread->pcState());
471        if (changedPC) {
472            warn("%lli: Changed PCs recently, may not be an error",
473                 curTick());
474        } else {
475            handleError(inst);
476        }
477    }
478
479    MachInst mi = static_cast<MachInst>(inst->staticInst->machInst);
480
481    if (mi != machInst) {
482        panic("%lli: Binary instructions do not match! Inst: %#x, "
483             "checker: %#x",
484             curTick(), mi, machInst);
485        handleError(inst);
486    }
487}
488
489template <class Impl>
490void
491Checker<Impl>::validateExecution(DynInstPtr &inst)
492{
493    uint64_t checker_val;
494    uint64_t inst_val;
495    int idx = -1;
496    bool result_mismatch = false;
497
498    if (inst->isUnverifiable()) {
499        // Unverifiable instructions assume they were executed
500        // properly by the CPU. Grab the result from the
501        // instruction and write it to the register.
502        copyResult(inst, 0, idx);
503    } else if (inst->numDestRegs() > 0 && !result.empty()) {
504        DPRINTF(Checker, "Dest regs %d, number of checker dest regs %d\n",
505                         inst->numDestRegs(), result.size());
506        for (int i = 0; i < inst->numDestRegs() && !result.empty(); i++) {
507            result.front().get(checker_val);
508            result.pop();
509            inst_val = 0;
510            inst->template popResult<uint64_t>(inst_val);
511            if (checker_val != inst_val) {
512                result_mismatch = true;
513                idx = i;
514                break;
515            }
516        }
517    } // Checker CPU checks all the saved results in the dyninst passed by
518      // the cpu model being checked against the saved results present in
519      // the static inst executed in the Checker.  Sometimes the number
520      // of saved results differs between the dyninst and static inst, but
521      // this is ok and not a bug.  May be worthwhile to try and correct this.
522
523    if (result_mismatch) {
524        warn("%lli: Instruction results do not match! (Values may not "
525             "actually be integers) Inst: %#x, checker: %#x",
526             curTick(), inst_val, checker_val);
527
528        // It's useful to verify load values from memory, but in MP
529        // systems the value obtained at execute may be different than
530        // the value obtained at completion.  Similarly DMA can
531        // present the same problem on even UP systems.  Thus there is
532        // the option to only warn on loads having a result error.
533        // The load/store queue in Detailed CPU can also cause problems
534        // if load/store forwarding is allowed.
535        if (inst->isLoad() && warnOnlyOnLoadError) {
536            copyResult(inst, inst_val, idx);
537        } else {
538            handleError(inst);
539        }
540    }
541
542    if (inst->nextInstAddr() != thread->nextInstAddr()) {
543        warn("%lli: Instruction next PCs do not match! Inst: %#x, "
544             "checker: %#x",
545             curTick(), inst->nextInstAddr(), thread->nextInstAddr());
546        handleError(inst);
547    }
548
549    // Checking side effect registers can be difficult if they are not
550    // checked simultaneously with the execution of the instruction.
551    // This is because other valid instructions may have modified
552    // these registers in the meantime, and their values are not
553    // stored within the DynInst.
554    while (!miscRegIdxs.empty()) {
555        int misc_reg_idx = miscRegIdxs.front();
556        miscRegIdxs.pop();
557
558        if (inst->tcBase()->readMiscRegNoEffect(misc_reg_idx) !=
559            thread->readMiscRegNoEffect(misc_reg_idx)) {
560            warn("%lli: Misc reg idx %i (side effect) does not match! "
561                 "Inst: %#x, checker: %#x",
562                 curTick(), misc_reg_idx,
563                 inst->tcBase()->readMiscRegNoEffect(misc_reg_idx),
564                 thread->readMiscRegNoEffect(misc_reg_idx));
565            handleError(inst);
566        }
567    }
568}
569
570
571// This function is weird, if it is called it means the Checker and
572// O3 have diverged, so panic is called for now.  It may be useful
573// to resynch states and continue if the divergence is a false positive
574template <class Impl>
575void
576Checker<Impl>::validateState()
577{
578    if (updateThisCycle) {
579        // Change this back to warn if divergences end up being false positives
580        panic("%lli: Instruction PC %#x results didn't match up, copying all "
581             "registers from main CPU", curTick(), unverifiedInst->instAddr());
582
583        // Terribly convoluted way to make sure O3 model does not implode
584        bool inSyscall = unverifiedInst->thread->inSyscall;
585        unverifiedInst->thread->inSyscall = true;
586
587        // Heavy-weight copying of all registers
588        thread->copyArchRegs(unverifiedInst->tcBase());
589        unverifiedInst->thread->inSyscall = inSyscall;
590
591        // Set curStaticInst to unverifiedInst->staticInst
592        curStaticInst = unverifiedInst->staticInst;
593        // Also advance the PC.  Hopefully no PC-based events happened.
594        advancePC(NoFault);
595        updateThisCycle = false;
596    }
597}
598
599template <class Impl>
600void
601Checker<Impl>::copyResult(DynInstPtr &inst, uint64_t mismatch_val,
602                          int start_idx)
603{
604    // We've already popped one dest off the queue,
605    // so do the fix-up then start with the next dest reg;
606    if (start_idx >= 0) {
607        RegIndex idx = inst->destRegIdx(start_idx);
608        if (idx < TheISA::FP_Base_DepTag) {
609            thread->setIntReg(idx, mismatch_val);
610        } else if (idx < TheISA::Ctrl_Base_DepTag) {
611            thread->setFloatRegBits(idx, mismatch_val);
612        } else if (idx < TheISA::Max_DepTag) {
613            thread->setMiscReg(idx - TheISA::Ctrl_Base_DepTag,
614                               mismatch_val);
615        }
616    }
617    start_idx++;
618    uint64_t res = 0;
619    for (int i = start_idx; i < inst->numDestRegs(); i++) {
620        RegIndex idx = inst->destRegIdx(i);
621        inst->template popResult<uint64_t>(res);
622        if (idx < TheISA::FP_Base_DepTag) {
623            thread->setIntReg(idx, res);
624        } else if (idx < TheISA::Ctrl_Base_DepTag) {
625            thread->setFloatRegBits(idx, res);
626        } else if (idx < TheISA::Max_DepTag) {
627            // Try to get the proper misc register index for ARM here...
628            thread->setMiscReg(idx - TheISA::Ctrl_Base_DepTag, res);
629        } // else Register is out of range...
630    }
631}
632
633template <class Impl>
634void
635Checker<Impl>::dumpAndExit(DynInstPtr &inst)
636{
637    cprintf("Error detected, instruction information:\n");
638    cprintf("PC:%s, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n"
639            "Completed:%i\n",
640            inst->pcState(),
641            inst->nextInstAddr(),
642            inst->seqNum,
643            inst->threadNumber,
644            inst->isCompleted());
645    inst->dump();
646    CheckerCPU::dumpAndExit();
647}
648
649template <class Impl>
650void
651Checker<Impl>::dumpInsts()
652{
653    int num = 0;
654
655    InstListIt inst_list_it = --(instList.end());
656
657    cprintf("Inst list size: %i\n", instList.size());
658
659    while (inst_list_it != instList.end())
660    {
661        cprintf("Instruction:%i\n",
662                num);
663
664        cprintf("PC:%s\n[sn:%lli]\n[tid:%i]\n"
665                "Completed:%i\n",
666                (*inst_list_it)->pcState(),
667                (*inst_list_it)->seqNum,
668                (*inst_list_it)->threadNumber,
669                (*inst_list_it)->isCompleted());
670
671        cprintf("\n");
672
673        inst_list_it--;
674        ++num;
675    }
676
677}
678