Skip to content

Commit 阶段

commit 阶段在 iew 阶段之后,主要负责指令的提交,由于 iew 阶段需要频繁与 commit 阶段交互,因此首先对 commit 阶段有所理解十分重要。这篇文章主要对 commit 阶段的相关内容进行讲解。

传递的信息

从 commit 的类成员可以看出来,commit 阶段会向前面阶段传递传递信息,因此首先理解这个传递的数据结构是很重要的,给出这个数据结构:

cpp
struct CommitComm
{

    // 下一条指令的pc地址,预测错误的时候使用;在违反内存序的时候这个值不变
    std::unique_ptr<PCStateBase> pc; // *F

    // 指向预测错误的那条指令
    DynInstPtr mispredictInst;  // *F

    // 在非预测错误下,造成流水线清空的指令
    DynInstPtr squashInst; // *F

    // 严格有序访问的指令
    DynInstPtr strictlyOrderedLoad; // *I

    // 向前告知哪些非预测性的指令能进行执行了
    InstSeqNum nonSpecSeqNum; // *I

    // 当前已经 commit 到哪条指令了
    InstSeqNum doneSeqNum; // *F, I

    // 当前ROB的空闲数
    unsigned freeROBEntries; // *R

    // 首次触发 squash 的时间
    bool squash; // *F, D, R, I
    
    // 仍然在处理 squash,这是因为单个周期能够处理的 squash 的问题
    bool robSquashing; // *F, D, R, I

    /// Rename should re-read number of free rob entries
    bool usedROB; // *R

    /// Notify Rename that the ROB is empty
    bool emptyROB; // *R

    // 告知分支发生与否
    bool branchTaken; // *F

    // 中断被悬停等待
    bool interruptPending; // *F

    // 中断在这个周期被处理
    bool clearInterrupt; // *F

    // 告知IEW阶段强行执行内存序的手段
    bool strictlyOrdered; // *I

};

o3 cpu startup 阶段

在 o3 cpu 的 startup 阶段,会触发 commit 的 startupStage 方法,这个方法如下:

cpp
void
Commit::startupStage()
{
    // 进行 ROB 相关的初始化
    rob->setActiveThreads(activeThreads);
    rob->resetEntries();

    // 广播 ROB 的空闲情况,usedROB 很可能是告诉前阶段 ROB 可用,
    // 或者说是告诉前阶段可以读取 ROB 的相关信息
    for (ThreadID tid = 0; tid < numThreads; tid++) {
        toIEW->commitInfo[tid].usedROB = true;
        toIEW->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid);
        toIEW->commitInfo[tid].emptyROB = true;
    }

    cpu->activateStage(CPU::CommitIdx);
    cpu->activityThisCycle();
}

执行阶段

执行阶段仍然和之前一样,主要执行的是 tick 阶段的函数,首先对 tick 阶段的主要脉络进行分析,随后逐个函数进行分析:

cpp
void
Commit::tick()
{
    wroteToTimeBuffer = false;
    // 首先默认下个周期这个流水线阶段不会被激活
    _nextStatus = Inactive;

    // 如果当前没有活动线程,就等于没有可人提交的,于是直接返回
    if (activeThreads->empty())
        return;

    std::list<ThreadID>::iterator threads = activeThreads->begin();
    std::list<ThreadID>::iterator end = activeThreads->end();

    // 检查之前的流水线清空(squash)是否完成,如果完成则会尝试改变一些状态
    while (threads != end) {
        ThreadID tid = *threads++;

        // 当前阶段有没有 store 被提交
        committedStores[tid] = false;

        // 如果先前处于 ROBSquashing 状态
        // 注意在触发流水线清空之后,不管前一个阶段是否处理完,状态都会被设置为 ROBSquashing
        // 因此在下个阶段开始的时候进行检查,如果处理完了就改变下状态,然后继续干本阶段的事
        //                             如果没处理完就当前阶段继续处理,然后干本阶段的事
        // 这里保证的是每个周期都能进行流水线的清空
        if (commitStatus[tid] == ROBSquashing) {

            if (rob->isDoneSquashing(tid)) {
                // 如果处理完了更改状态
                commitStatus[tid] = Running;
            } else {
                DPRINTF(Commit,"[tid:%i] Still Squashing, cannot commit any"
                        " insts this cycle.\n", tid);
                // 如果没处理完继续处理
                rob->doSquash(tid);
                // 告知前面阶段,本阶段仍然在处理流水线的排空
                // 至少到本阶段为止,流水线排空还没完成
                toIEW->commitInfo[tid].robSquashing = true;
                wroteToTimeBuffer = true;
            }
        }
    }

    // 核心函数,在这个函数中尝试进行ROB的commit
    // 正是在提交的过程中可能会触发流水线清空
    commit();

    // 将 IEW 阶段传过来的指令标记为可提交的,如果 IEW 把某条指令传过来,
    // 说明这条指令已经执行写回完了,可以提交了
    // Rename阶段:将指令提前放到 ROB 中
    // IEW阶段:将指令标记为可提交的,这样 ROB 中的相关指令在下个阶段就能被提交了
    markCompletedInsts();

    threads = activeThreads->begin();

    // 这边只是 log 相关的信息,在前面将 IEW 阶段传送过来的指令标记为能够提交之后,
    // 看看重排序缓冲中是否有指令准备好提交了
    while (threads != end) {
        ThreadID tid = *threads++;

        if (!rob->isEmpty(tid) && rob->readHeadInst(tid)->readyToCommit()) {
            // The ROB has more instructions it can commit. Its next status
            // will be active.
            _nextStatus = Active;

            [[maybe_unused]] const DynInstPtr &inst = rob->readHeadInst(tid);

            DPRINTF(Commit,"[tid:%i] Instruction [sn:%llu] PC %s is head of"
                    " ROB and ready to commit\n",
                    tid, inst->seqNum, inst->pcState());

        } else if (!rob->isEmpty(tid)) {
            const DynInstPtr &inst = rob->readHeadInst(tid);

            ppCommitStall->notify(inst);

            DPRINTF(Commit,"[tid:%i] Can't commit, Instruction [sn:%llu] PC "
                    "%s is head of ROB and not ready\n",
                    tid, inst->seqNum, inst->pcState());
        }

        DPRINTF(Commit, "[tid:%i] ROB has %d insts & %d free entries.\n",
                tid, rob->countInsts(tid), rob->numFreeEntries(tid));
    }

    // 标记本阶段是活动的
    if (wroteToTimeBuffer) {
        DPRINTF(Activity, "Activity This Cycle.\n");
        cpu->activityThisCycle();
    }

    // 尝试改变commit阶段的状态
    updateStatus();
}

commit 函数

如上面所说,整个执行过程中,最重要的就是这个 commit 函数,下面对这个 commit 函数进行解析:

cpp
void
Commit::commit()
{
    // 如果工作在全系统模式下,触发对中断的获取
    if (FullSystem) {
        if (cpu->checkInterrupts(0))
            propagateInterrupt();
    }
    // 如果我们工作在SE模式下,interrupt == NoFault 应该是成立的

    // 首先处理是否有流水线排空情况
    std::list<ThreadID>::iterator threads = activeThreads->begin();
    std::list<ThreadID>::iterator end = activeThreads->end();

    // 记录产生流水线排空的线程数
    int num_squashing_threads = 0;

    while (threads != end) {
        ThreadID tid = *threads++;

        // trapSquash 先前被设置
        // 在先前的异常触发了清空
        if (trapSquash[tid]) {
            assert(!tcSquash[tid]);
            // 处理这个清空,在这之中会调用 squashall 来清空 tid 相关的所有指令
            squashFromTrap(tid);

            // 如果执行了退出的系统调用或者不能往下进行
            // 则调度线程退出事件并进行退出
            if (cpu->isThreadExiting(tid))
                cpu->scheduleThreadExitEvent(tid);
        } else if (tcSquash[tid]) {
            assert(commitStatus[tid] != TrapPending);
            // 处理这个清空,在这之中会调用 squashall 来清空 tid 相关的所有指令
            squashFromTC(tid);
        } else if (commitStatus[tid] == SquashAfterPending) {
            // 由 SquashAfter 引起的清空,SquashAfter 是由 commitInsts 调用引起的
            squashFromSquashAfter(tid);
        }

        // 处理正常指令带来的流水线清空
        // 先前指令触发了清空 && 当前不在处理异常 && 待清空的序列号 <= 最年轻的序列号
        if (fromIEW->squash[tid] &&
            commitStatus[tid] != TrapPending &&
            fromIEW->squashedSeqNum[tid] <= youngestSeqNum[tid]) {

            // 分支预测错误带来的清空
            if (fromIEW->mispredictInst[tid]) {
                DPRINTF(Commit,
                    "[tid:%i] Squashing due to branch mispred "
                    "PC:%#x [sn:%llu]\n",
                    tid,
                    fromIEW->mispredictInst[tid]->pcState().instAddr(),
                    fromIEW->squashedSeqNum[tid]);
            } else {
                // 违反内存序带来的清空
                DPRINTF(Commit,
                    "[tid:%i] Squashing due to order violation [sn:%llu]\n",
                    tid, fromIEW->squashedSeqNum[tid]);
            }

            DPRINTF(Commit, "[tid:%i] Redirecting to PC %#x\n",
                    tid, *fromIEW->pc[tid]);

            // 将当前 commit 的状态设置为正在处理清空
            commitStatus[tid] = ROBSquashing;

            // 将 squashed_inst 设置为造成清空指令的序列号
            InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid];

            // 如果 IEW 阶段设置了连同指令本身一同被清空
            // 则 squashed_inst - 1,保证指令本身也被清空
            if (fromIEW->includeSquashInst[tid]) {
                squashed_inst--;
            }

            // 由于 squashed_inst 后续的指令都被清空
            // 于是当前线程最年轻的序列号就是 squashed_inst
            youngestSeqNum[tid] = squashed_inst;

            // 告知 ROB 开始清空
            rob->squash(squashed_inst, tid);
            // 表示对 ROB 进行了相关的改变
            changedROBNumEntries[tid] = true;

            // 向前告知当前线程最后一条被提交的指令号
            toIEW->commitInfo[tid].doneSeqNum = squashed_inst;
            // 向前告知当前阶段触发了清空
            toIEW->commitInfo[tid].squash = true;

            // 向前告知当前阶段有在处理清空
            toIEW->commitInfo[tid].robSquashing = true;

            // 向前告知产生清空的是哪条指令
            toIEW->commitInfo[tid].mispredictInst =
                fromIEW->mispredictInst[tid];
            // 向前告知当前分支是否发生
            toIEW->commitInfo[tid].branchTaken =
                fromIEW->branchTaken[tid];

            // 从 ROB 中返回 squashed_inst 这个序列号对应的指令
            // 可能是:
            //     造成 squashed 的指令
            //     造成 squashed 的指令的前一条指令,fromIEW->includeSquashInst[tid] 设置的情况下
            //     NULL, fromIEW->includeSquashInst[tid] 设置的情况下,squashed_inst--后 ROB 中可能不存在对应的指令
            toIEW->commitInfo[tid].squashInst =
                                    rob->findInst(tid, squashed_inst);
            if (toIEW->commitInfo[tid].mispredictInst) {
                // 无条件分支永远发生,只不过刚开始可能预测会出错
                if (toIEW->commitInfo[tid].mispredictInst->isUncondCtrl()) {
                     toIEW->commitInfo[tid].branchTaken = true;
                }
                ++stats.branchMispredicts;
            }

            // 向前传递实际正确取指的pc
            set(toIEW->commitInfo[tid].pc, fromIEW->pc[tid]);
        }

        // 如果当前线程正在进行清空,增加清空流水线的指令数
        if (commitStatus[tid] == ROBSquashing) {
            num_squashing_threads++;
        }
    }

    // 前面这部分总体而言主要在判断条件触发流水线的清空
    // 但是这些清空不一定在一个时钟周期内完成
    // 因此如果发现前面的代码有在处理清空,设置 _nextStatus 为 Active
    // 保证下个时钟周期 commit 阶段继续工作来处理清空
    if (num_squashing_threads) {
        _nextStatus = Active;
    }

    // 如果不是每个硬件线程都在处理清空
    if (num_squashing_threads != numThreads) {
        // 从 rename 阶段获取新的指令插入到 ROB 中
        getInsts();

        // 尝试进行提交
        commitInsts();
    }

    // 对当前活动的线程进行遍历
    threads = activeThreads->begin();

    // 继续向前面阶段传递一些信息
    while (threads != end) {
        ThreadID tid = *threads++;

        if (changedROBNumEntries[tid]) {
            // 向前面阶段告知这个阶段使用了 ROB,可能产生了一些空余空间
            toIEW->commitInfo[tid].usedROB = true;
            // 将空余空间的数目向前传递
            toIEW->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid);

            wroteToTimeBuffer = true;
            changedROBNumEntries[tid] = false;
            if (rob->isEmpty(tid))
                checkEmptyROB[tid] = true;
        }

        // 在满足三个条件下设置 ROB 为空,暂不明白这三个条件是什么
        if (checkEmptyROB[tid] && rob->isEmpty(tid) &&
            !iewStage->hasStoresToWB(tid) && !committedStores[tid]) {
            checkEmptyROB[tid] = false;
            toIEW->commitInfo[tid].usedROB = true;
            toIEW->commitInfo[tid].emptyROB = true;
            toIEW->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid);
            wroteToTimeBuffer = true;
        }

    }
}

squashAll 函数

squashAllsquashFromTrapsquashFromTCsquashFromSquashAfter 中都有调用,下详细解释:

cpp
void
Commit::squashAll(ThreadID tid)
{
    // 不管 ROB 是否为空,都找到一个最小的序列号能够将整个 ROB 清空
    InstSeqNum squashed_inst = rob->isEmpty(tid) ?
        lastCommitedSeqNum[tid] : rob->readHeadInst(tid)->seqNum - 1;

    // 将最年轻的序列号(离目前为止最近的序列号)更新为离现在最近提交的序列号
    youngestSeqNum[tid] = lastCommitedSeqNum[tid];

    // 开始标记清空
    rob->squash(squashed_inst, tid);
    changedROBNumEntries[tid] = true;

    // 向前告知当前线程最后一条被提交的指令号
    toIEW->commitInfo[tid].doneSeqNum = squashed_inst;
    // 向前传递当前阶段触发了清空与正在处理清空
    toIEW->commitInfo[tid].squash = true;
    toIEW->commitInfo[tid].robSquashing = true;

    // 由于是全部的清空,因此没有显示的导致清空的指令
    toIEW->commitInfo[tid].mispredictInst = NULL;
    toIEW->commitInfo[tid].squashInst = NULL;

    set(toIEW->commitInfo[tid].pc, pc[tid]);
}

getInsts 函数

cpp
void
Commit::getInsts()
{
    DPRINTF(Commit, "Getting instructions from Rename stage.\n");

    // 从 Rename 阶段提前获取指令,插入到 ROB
    int insts_to_process = std::min((int)renameWidth, fromRename->size);

    for (int inst_num = 0; inst_num < insts_to_process; ++inst_num) {
        const DynInstPtr &inst = fromRename->insts[inst_num];
        ThreadID tid = inst->threadNumber;

        // 当前指令不是清空指令,且当前线程并不在处理清空(假如正在处理清空,插进去也被清空了)
        // 且当前线程并不在处理异常
        if (!inst->isSquashed() &&
            commitStatus[tid] != ROBSquashing &&
            commitStatus[tid] != TrapPending) {
            changedROBNumEntries[tid] = true;

            DPRINTF(Commit, "[tid:%i] [sn:%llu] Inserting PC %s into ROB.\n",
                    tid, inst->seqNum, inst->pcState());

            rob->insertInst(inst);

            assert(rob->getThreadEntries(tid) <= rob->getMaxEntries(tid));

            youngestSeqNum[tid] = inst->seqNum;
        } else {
            // 这些指令被略过可能也没关系,因为当前线程 ROB 已经被清空了
            // 说明后续的指令流会进行改变,因此这部分可能直接丢弃了也没事
            assert(commitStatus[tid] != ROBSquashing);
            assert(commitStatus[tid] != TrapPending);
            DPRINTF(Commit, "[tid:%i] [sn:%llu] "
                    "Instruction PC %s was squashed, skipping.\n",
                    tid, inst->seqNum, inst->pcState());
        }
    }
}

commitInsts 函数

commitInsts 是进行 commit 的核心函数。

cpp
void
Commit::commitInsts()
{

    DPRINTF(Commit, "Trying to commit instructions in the ROB.\n");

    // 记录提交指令的条数
    unsigned num_committed = 0;

    DynInstPtr head_inst;

    // 在设置的每个时钟周期的最多提交数目内尽可能多的进行提交
    while (num_committed < commitWidth) {

        // 获取进行提交的线程,这里获得的线程是由设置的 SMTFetchPolicy 决定的
        ThreadID commit_thread = getCommittingThread();

        // 在 SE 模式下由于没有中断,可能不会进入到这个 if
        if (interrupt != NoFault) {
            // 硬件事务内存要推迟中断,这里直接清空不做处理
            // 这里的推迟是怎么做的暂时不清楚
            if (executingHtmTransaction(commit_thread)) {
                cpu->clearInterrupts(0);
                toIEW->commitInfo[0].clearInterrupt = true;
                interrupt = NoFault;
                avoidQuiesceLiveLock = true;
            } else {
                // 处理异常,执行相关的处理,但是不建模时序,建模时序在后面进行
                handleInterrupt();
            }
        }

        // 如果获取不到能够进行commit的线程,直接返回
        if (commit_thread == -1 || !rob->isHeadReady(commit_thread))
            break;
            
        // 拿到最头部的指令
        head_inst = rob->readHeadInst(commit_thread);

        ThreadID tid = head_inst->threadNumber;

        assert(tid == commit_thread);

        DPRINTF(Commit,
                "Trying to commit head instruction, [tid:%i] [sn:%llu]\n",
                tid, head_inst->seqNum);

        // 如果最头部的指令是被淘汰的,直接从 ROB 中移除就是了
        if (head_inst->isSquashed()) {

            DPRINTF(Commit, "Retiring squashed instruction from "
                    "ROB.\n");

            rob->retireHead(commit_thread);

            ++stats.commitSquashedInsts;
            // Notify potential listeners that this instruction is squashed
            ppSquash->notify(head_inst);

            // Record that the number of ROB entries has changed.
            changedROBNumEntries[tid] = true;
        } else {
            // 如果不是,属于正常commit,更新pc值,这个pc值应该代表着程序commit到了哪里
            set(pc[tid], head_inst->pcState());

            // 尝试对这个头部指令进行提交
            // 提交不成功的条件是:1.没有执行 2.执行产生了异常
            bool commit_success = commitHead(head_inst, num_committed);

            if (commit_success) {
                // 成功commit,更新相关的统计数据
                ++num_committed;
                cpu->commitStats[tid]
                    ->committedInstType[head_inst->opClass()]++;
                stats.committedInstType[tid][head_inst->opClass()]++;
                ppCommit->notify(head_inst);

                // 硬件事务内存相关

                if (head_inst->isHtmStart())
                    htmStarts[tid]++;

                if (head_inst->inHtmTransactionalState()) {
                    assert(executingHtmTransaction(tid));
                } else {
                    assert(!executingHtmTransaction(tid));
                }

                if (head_inst->isHtmStop())
                    htmStops[tid]++;
                
                // 设置对 ROB 进行了更改
                changedROBNumEntries[tid] = true;

                // 更新最后完成指令的信息,并向前传递
                toIEW->commitInfo[tid].doneSeqNum = head_inst->seqNum;

                if (tid == 0)
                    canHandleInterrupts = !head_inst->isDelayedCommit();

                // at this point store conditionals should either have
                // been completed or predicated false
                assert(!head_inst->isStoreConditional() ||
                       head_inst->isCompleted() ||
                       !head_inst->readPredicate());

                // 更新相关的寄存器
                head_inst->updateMiscRegs();

                // checker cpu 相关
                if (cpu->checker) {
                    cpu->checker->verify(head_inst);
                }

                cpu->traceFunctions(pc[tid]->instAddr());

                // 记录当前 commit 到了哪个 pc 值
                head_inst->staticInst->advancePC(*pc[tid]);

                // 更新最后 commit 的指令
                lastCommitedSeqNum[tid] = head_inst->seqNum;

                // 如果这个指令本身就被设置了 SquashAfter 标志
                if (head_inst->isSquashAfter())
                    squashAfter(tid, head_inst);

                // 切换 cpu 时候的排空相关
                if (drainPending) {
                    if (pc[tid]->microPC() == 0 && interrupt == NoFault &&
                        !thread[tid]->trapPending) {
                        
                        DPRINTF(Drain, "Draining: %i:%s\n", tid, *pc[tid]);
                        squashAfter(tid, head_inst);
                        cpu->commitDrained(tid);
                        drainImminent = true;
                    }
                }

                
                // CISC,是对于微指令边界的判断
                // RISC,每条指令都是边界
                bool onInstBoundary = !head_inst->isMicroop() ||
                                      head_inst->isLastMicroop() ||
                                      !head_inst->isDelayedCommit();

                if (onInstBoundary) {
                    int count = 0;
                    Addr oldpc;
                    // Make sure we're not currently updating state while
                    // handling PC events.
                    assert(!thread[tid]->noSquashFromTC &&
                           !thread[tid]->trapPending);
                    do {
                        oldpc = pc[tid]->instAddr();
                        thread[tid]->pcEventQueue.service(
                                oldpc, thread[tid]->getTC());
                        count++;
                    } while (oldpc != pc[tid]->instAddr());
                    if (count > 1) {
                        DPRINTF(Commit,
                                "PC skip function event, stopping commit\n");
                        break;
                    }
                }

                // 处理之前尚未处理的中断
                if (!interrupt && avoidQuiesceLiveLock &&
                    onInstBoundary && cpu->checkInterrupts(0))
                    squashAfter(tid, head_inst);
            } else {
                // 如果提交头部指令失败,直接停止提交
                // 有一条指令提交失败,直接终止整个提交过程
                DPRINTF(Commit, "Unable to commit head instruction PC:%s "
                        "[tid:%i] [sn:%llu].\n",
                        head_inst->pcState(), tid ,head_inst->seqNum);
                break;
            }// if (commit_success)
        }
    }

    // 更新相关的统计数据
    DPRINTF(CommitRate, "%i\n", num_committed);
    stats.numCommittedDist.sample(num_committed);

    if (num_committed == commitWidth) {
        stats.commitEligibleSamples++;
    }
}

commitHead 函数

cpp
bool
Commit::commitHead(const DynInstPtr &head_inst, unsigned inst_num)
{
    assert(head_inst);

    // 获取到线程号
    ThreadID tid = head_inst->threadNumber;

    // 根据下面的提示基本可以认为,落到 commit 阶段还没被执行的指令一定是 nospec 的指令
    if (!head_inst->isExecuted()) {
        assert(head_inst->isNonSpeculative() || head_inst->isStoreConditional()
               || head_inst->isReadBarrier() || head_inst->isWriteBarrier()
               || head_inst->isAtomic()
               || (head_inst->isLoad() && head_inst->strictlyOrdered()));

        DPRINTF(Commit,
                "Encountered a barrier or non-speculative "
                "instruction [tid:%i] [sn:%llu] "
                "at the head of the ROB, PC %s.\n",
                tid, head_inst->seqNum, head_inst->pcState());

        // 这部分后面做的其实就是想要给前面的 iew 提供 nospec 的号码
        // 让前面的 iew 开始执行 nospec
        // 首先要保证所有的内存指令都已经被写回了 
        if (inst_num > 0 || iewStage->hasStoresToWB(tid)) {
            DPRINTF(Commit,
                    "[tid:%i] [sn:%llu] "
                    "Waiting for all stores to writeback.\n",
                    tid, head_inst->seqNum);
            return false;
        }

        // i在写回了之后对 nospec 的号码进行设置

        toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum;

        // 现在准备要被调度执行了
        head_inst->clearCanCommit();

        // 分别向前面传递不同的信息
        if (head_inst->isLoad() && head_inst->strictlyOrdered()) {
            DPRINTF(Commit, "[tid:%i] [sn:%llu] "
                    "Strictly ordered load, PC %s.\n",
                    tid, head_inst->seqNum, head_inst->pcState());
            // 向前传递严格的内存序的信息
            toIEW->commitInfo[tid].strictlyOrdered = true;
            toIEW->commitInfo[tid].strictlyOrderedLoad = head_inst;
        } else {
            ++stats.commitNonSpecStalls;
        }

        return false;
    }
    //上面一旦返回 false,所有指令的提交就都无法继续了,实现了顺序的功能

    // 检查前面指令本身的执行是不是触发了异常
    Fault inst_fault = head_inst->getFault();

    // 如果是硬件事务内存触发的异常,则将异常设置为硬件事务内存专用的异常
    if (inst_fault != NoFault && head_inst->inHtmTransactionalState()) {
        // There exists a generic HTM fault common to all ISAs
        if (!std::dynamic_pointer_cast<GenericHtmFailureFault>(inst_fault)) {
            DPRINTF(HtmCpu, "%s - fault (%s) encountered within transaction"
                            " - converting to GenericHtmFailureFault\n",
            head_inst->staticInst->getName(), inst_fault->name());
            inst_fault = std::make_shared<GenericHtmFailureFault>(
                head_inst->getHtmTransactionUid(),
                HtmFailureFaultCause::EXCEPTION);
        }
        // If this point is reached and the fault inherits from the HTM fault,
        // then there is no need to raise a new fault
    }

    // 非 store 的指令如果没有产生异常被认为完成
    if (!head_inst->isStore() && inst_fault == NoFault) {
        head_inst->setCompleted();
    }

    // 如果产生了异常
    if (inst_fault != NoFault) {
        DPRINTF(Commit, "Inst [tid:%i] [sn:%llu] PC %s has a fault\n",
                tid, head_inst->seqNum, head_inst->pcState());

        if (iewStage->hasStoresToWB(tid) || inst_num > 0) {
            DPRINTF(Commit,
                    "[tid:%i] [sn:%llu] "
                    "Stores outstanding, fault must wait.\n",
                    tid, head_inst->seqNum);
            return false;
        }

        // inst_num == 0 && !iewStage->hasStoresToWB(tid) 才执行后面的
        head_inst->setCompleted();

        // If instruction has faulted, let the checker execute it and
        // check if it sees the same fault and control flow.
        if (cpu->checker) {
            // Need to check the instruction before its fault is processed
            cpu->checker->verify(head_inst);
        }

        assert(!thread[tid]->noSquashFromTC);

        // 进行异常处理的时候设置,暂时不明白意图
        thread[tid]->noSquashFromTC = true;

        // 处理异常
        cpu->trap(inst_fault, tid,
                  head_inst->notAnInst() ? nullStaticInstPtr :
                      head_inst->staticInst);

        // 处理完异常再改回来
        thread[tid]->noSquashFromTC = false;

        // 状态设置为正在处理异常
        commitStatus[tid] = TrapPending;

        DPRINTF(Commit,
            "[tid:%i] [sn:%llu] Committing instruction with fault\n",
            tid, head_inst->seqNum);
        if (head_inst->traceData) {
            // We ignore ReExecution "faults" here as they are not real
            // (architectural) faults but signal flush/replays.
            if (debug::ExecFaulting
                && dynamic_cast<ReExec*>(inst_fault.get()) == nullptr) {

                head_inst->traceData->setFaulting(true);
                head_inst->traceData->setFetchSeq(head_inst->seqNum);
                head_inst->traceData->setCPSeq(thread[tid]->numOp);
                head_inst->traceData->dump();
            }
            delete head_inst->traceData;
            head_inst->traceData = NULL;
        }

        // 建模异常的事件,事件触发的时候应该会把 commitStatus 改回来
        generateTrapEvent(tid, inst_fault);
        return false;
    }

    // 没有发生异常的话更新统计数据
    updateComInstStats(head_inst);

    DPRINTF(Commit,
            "[tid:%i] [sn:%llu] Committing instruction with PC %s\n",
            tid, head_inst->seqNum, head_inst->pcState());
    if (head_inst->traceData) {
        head_inst->traceData->setFetchSeq(head_inst->seqNum);
        head_inst->traceData->setCPSeq(thread[tid]->numOp);
        head_inst->traceData->dump();
        delete head_inst->traceData;
        head_inst->traceData = NULL;
    }
    if (head_inst->isReturn()) {
        DPRINTF(Commit,
                "[tid:%i] [sn:%llu] Return Instruction Committed PC %s \n",
                tid, head_inst->seqNum, head_inst->pcState());
    }

    // 更新 rename 表
    for (int i = 0; i < head_inst->numDestRegs(); i++) {
        renameMap[tid]->setEntry(head_inst->flattenedDestIdx(i),
                                 head_inst->renamedDestIdx(i));
    }

    // 硬件事务内存相关
    if (head_inst->isHtmStart())
        iewStage->setLastRetiredHtmUid(tid, head_inst->getHtmTransactionUid());

    // 正式提交指令
    rob->retireHead(tid);


    // store 类型指令由 store queue 去做提交
    if (head_inst->isStore() || head_inst->isAtomic())
        committedStores[tid] = true;

    // 返回提交成功
    return true;
}

commit阶段总体功能框图

what commit do

精确异常的产生和处理

精确异常的产生在尝试从头部提交指令(Commit::commitHead)的时候,提交的时候发现了指令执行产生了异常,于是进行异常的处理,将自身状态设置为 TrapPending,进行异常的处理,然后建模异常事件,在异常事件触发的时候设置 trapSquash[tid]。在下一次进入到 commit 阶段(Commit::commit)的时候,由于检测到 trapSquash[tid],开始进行指令排空的标记,以此实现精确的异常。

有关向流水线前面阶段的信息传递

在 commit 阶段会向前面阶段传递大量信息,这篇文章讲传递信息的发生时机。