跳转至

GEM5 O3 CPU ROB(重排序缓冲区)详细代码解读

概述

ROB (Reorder Buffer) 是GEM5 O3 CPU中的关键组件,负责维护指令的程序顺序并支持乱序执行。ROB确保指令按照程序顺序提交,同时支持精确异常处理和流水线冲刷。在香山的实现中,ROB还支持指令组压缩优化,提高存储效率和提交带宽。

核心数据结构

1. ROB类定义 (rob.hh:75-355)

class ROB
{
  public:
    typedef std::pair<RegIndex, RegIndex> UnmapInfo;
    typedef typename std::list<DynInstPtr>::iterator InstIt;

    enum Status { Running, Idle, ROBSquashing };
    // ...
};

ROB类管理所有在途指令的顺序,提供插入、退休、冲刷等关键操作。

2. 关键成员变量

状态管理变量

  • Status robStatus[MaxThreads] (rob.hh:91): 每个线程的ROB状态
  • bool doneSquashing[MaxThreads] (rob.hh:338): 线程冲刷完成标记
  • InstSeqNum squashedSeqNum[MaxThreads] (rob.hh:335): 冲刷目标序列号

指令存储结构

  • std::list<DynInstPtr> instList[MaxThreads] (rob.hh:292): 每个线程的指令列表
  • std::deque<unsigned> threadGroups[MaxThreads] (rob.hh:284): 指令组大小记录
  • InstIt head/tail (rob.hh:310,314): 全局头尾指针
  • InstIt squashIt[MaxThreads] (rob.hh:324): 冲刷迭代器

配置参数

  • unsigned numEntries (rob.hh:280): ROB总容量
  • unsigned instsPerGroup (rob.hh:282): 每组最大指令数
  • unsigned maxEntries[MaxThreads] (rob.hh:289): 每线程最大条目数
  • SMTQueuePolicy robPolicy (rob.hh:94): SMT资源分配策略

策略函数指针

  • bool (ROB::*allocateNewGroup)(const DynInstPtr, ThreadID) (rob.hh:261): 指令组分配策略函数

3. 指令组压缩策略 (rob.cc:58-130)

ROB支持多种指令组压缩策略以提高存储效率:

3.1 无压缩策略 (none)

bool ROB::allocateGroup_none(const DynInstPtr inst, ThreadID tid)
{
    return true; // 每条指令独占一组
}

3.2 昆明湖V2策略 (kmhv2)

bool ROB::allocateGroup_kmhv2(const DynInstPtr inst, ThreadID tid)
{
    auto& groups = threadGroups[tid];
    auto& prev = instList[tid].back();

    bool alloc = false;
    if (groups.empty()) { // 第一条指令
        alloc = true;
    } else if (inst->isMemRef() || inst->isControl() || inst->isNonSpeculative()) {
        alloc = true; // 内存访问、控制流、非推测指令独占组
    } else if (prev->isMemRef() || prev->isControl() || prev->isNonSpeculative()) {
        alloc = true; // 前一条是特殊指令则新建组
    } else if (prev->ftqId != inst->ftqId) {
        alloc = true; // 不同FTQ组
    } else if (lastInsertCycle != cpu->curCycle()) {
        alloc = true; // 不同周期
    } else if (groups.back() >= instsPerGroup) {
        alloc = true; // 组已满
    }
    return alloc;
}

3.3 昆明湖V3策略 (kmhv3)

bool ROB::allocateGroup_kmhv3(const DynInstPtr inst, ThreadID tid)
{
    // 最宽松的分组策略,主要按周期和组大小限制
    return (groups.empty() || 
            lastInsertCycle != cpu->curCycle() || 
            groups.back() >= instsPerGroup);
}

3.4 MohBoE策略

bool ROB::allocateGroup_MohBoE(const DynInstPtr inst, ThreadID tid)
{
    // 内存指令在组头,控制指令在组尾的策略
    return (groups.empty() || 
            inst->isMemRef() || inst->isNonSpeculative() ||
            prev->isControl() ||
            lastInsertCycle != cpu->curCycle() ||
            groups.back() >= instsPerGroup);
}

核心函数解读

1. 构造函数 (rob.cc:132-205)

ROB::ROB(CPU *_cpu, const BaseO3CPUParams &params)

功能: 初始化ROB,设置SMT策略和指令组压缩策略

关键逻辑: - SMT资源分配策略: - Dynamic: 所有线程共享全部ROB容量 - Partitioned: 平均分配ROB容量给各线程 - Threshold: 基于阈值的资源分配

  • 指令组压缩策略选择:
    switch(params.RobCompressPolicy) {
        case ROBCompressPolicy::none:
            allocateNewGroup = &ROB::allocateGroup_none;
            instsPerGroup = 1;
            break;
        case ROBCompressPolicy::kmhv3:
            allocateNewGroup = &ROB::allocateGroup_kmhv3;
            break;
        // ... 其他策略
    }
    

2. insertInst()函数 - 指令插入 (rob.cc:338-384)

void ROB::insertInst(const DynInstPtr &inst)

功能: 将新指令插入ROB尾部,根据压缩策略决定是否创建新组

执行流程:

2.1 基本检查和准备

assert(inst);
assert(numInstsInROB <= numEntries * instsPerGroup);

ThreadID tid = inst->threadNumber;
stats.writes++; // 更新统计

2.2 指令组分配逻辑

bool alloc = (this->*allocateNewGroup)(inst, tid);
lastInsertCycle = cpu->curCycle();

if (alloc) {
    // 完成当前组的统计
    if (!threadGroups[tid].empty()) {
        stats.instPergroup.sample(threadGroups[tid].back());
    }
    // 创建新组
    threadGroups[tid].push_back(1);
} else {
    // 添加到当前组
    assert(threadGroups[tid].back() < instsPerGroup);
    threadGroups[tid].back()++;
}

2.3 指令链表插入

instList[tid].push_back(inst);

// 更新全局头指针(第一条指令时)
if (numInstsInROB == 0) {
    head = instList[tid].begin();
    assert((*head) == inst);
}

// 更新全局尾指针
tail = instList[tid].end();
tail--;

inst->setInROB();
++numInstsInROB;

3. retireHead()函数 - 头部指令退休 (rob.cc:387-421)

void ROB::retireHead(ThreadID tid)

功能: 退休指定线程的头部指令,更新指令组计数

执行流程:

3.1 获取并移除头部指令

assert(numInstsInROB > 0);

InstIt head_it = instList[tid].begin();
DynInstPtr head_inst = std::move(*head_it);
instList[tid].erase(head_it);

3.2 状态验证和更新

assert(head_inst->readyToCommit());
assert(!head_inst->isSquashed());

--numInstsInROB;

// 更新指令组大小
commitGroup(head_inst, tid);

// 清理指令状态
head_inst->clearInROB();
head_inst->setCommitted();

3.3 更新全局状态

// 更新全局头指针
updateHead();

// 通知CPU移除已提交指令
cpu->removeFrontInst(head_inst);

4. isHeadGroupReady()函数 - 头部组就绪检查 (rob.cc:424-448)

bool ROB::isHeadGroupReady(ThreadID tid)

功能: 检查指定线程的头部指令组是否全部准备好提交

检查逻辑:

if (!threadGroups[tid].empty() && threadGroups[tid].front() != 0) {
    auto it = instList[tid].begin();

    // 遍历当前组的所有指令
    for (int i = 0; i < threadGroups[tid].front(); i++, it++) {
        auto& inst = *it;

        // 所有指令必须准备好提交
        if (!inst->readyToCommit()) {
            return false;
        }

        // 特殊指令(屏障、异常)会立即返回true
        if (inst->readyToCommit() && 
            (!inst->isExecuted() || inst->faulted())) {
            return true;
        }
    }
    return true;
}
return false;

5. 冲刷相关函数

5.1 squash()函数 - 启动冲刷 (rob.cc:655-693)

void ROB::squash(InstSeqNum squash_num, ThreadID tid)

功能: 启动对指定线程的指令冲刷,冲刷序列号大于squash_num的所有指令

执行流程:

if (isEmpty(tid)) {
    return; // 空ROB无需冲刷
}

// 设置冲刷状态
robStatus[tid] = ROBSquashing;
doneSquashing[tid] = false;
squashedSeqNum[tid] = squash_num;

// 计算动态冲刷带宽
unsigned total_inst_to_squash = 0;
for (auto it = instList[tid].begin(); it != instList[tid].end(); ++it) {
    if ((*it)->seqNum > squash_num) {
        total_inst_to_squash++;
    }
}
unsigned num_uncommited_inst = instList[tid].size() - total_inst_to_squash;

dynSquashWidth = computeDynSquashWidth(num_uncommited_inst, total_inst_to_squash);

// 从尾部开始冲刷
if (!instList[tid].empty()) {
    InstIt tail_thread = instList[tid].end();
    tail_thread--;
    squashIt[tid] = tail_thread;
    doSquash(tid);
}

5.2 doSquash()函数 - 执行冲刷 (rob.cc:475-573)

void ROB::doSquash(ThreadID tid)

功能: 实际执行指令冲刷,按带宽限制逐条处理

冲刷循环核心逻辑:

unsigned int num_insts_to_squash = dynSquashWidth;

// 如果CPU正在退出,冲刷所有指令
if (cpu->isThreadExiting(tid)) {
    num_insts_to_squash = numEntries * instsPerGroup;
}

for (int numSquashed = 0;
     numSquashed < num_insts_to_squash &&
     squashIt[tid] != instList[tid].end() &&
     (*squashIt[tid])->seqNum > squashedSeqNum[tid];
     ++numSquashed)
{
    // 标记指令为已冲刷
    (*squashIt[tid])->setSquashed();
    (*squashIt[tid])->setCanCommit();

    auto prevIt = std::prev(squashIt[tid]);
    --numInstsInROB;

    // 更新指令组大小
    squashGroup(*squashIt[tid], tid);

    // 清理指令状态
    (*squashIt[tid])->clearInROB();
    cpu->removeFrontInst(*squashIt[tid]);

    // 从列表中移除
    instList[tid].erase(squashIt[tid]);
    squashIt[tid] = prevIt;
}

5.3 computeDynSquashWidth()函数 - 动态冲刷带宽计算 (rob.cc:696-727)

unsigned ROB::computeDynSquashWidth(unsigned uncommitted_insts, unsigned to_squash)

功能: 根据冲刷策略计算每周期冲刷指令数量

策略类型: - Rollback: 固定冲刷带宽 rollbackWidth - Replay: 基于未提交指令数量动态调整

expected_cycles = std::max(2.0, ((double)uncommitted_insts / replayWidth));
dyn_squash_width = ceil((double)to_squash / expected_cycles);
- ConstCycle: 固定周期数内完成冲刷
dyn_squash_width = ceil((double)to_squash / (double)constSquashCycle);

6. 头尾指针维护函数

6.1 updateHead()函数 - 更新全局头指针 (rob.cc:577-614)

void ROB::updateHead()

功能: 在多线程环境下找到序列号最小的指令作为全局头部

InstSeqNum lowest_num = 0;
bool first_valid = true;

for (ThreadID tid : *activeThreads) {
    if (instList[tid].empty()) continue;

    if (first_valid) {
        head = instList[tid].begin();
        lowest_num = (*head)->seqNum;
        first_valid = false;
        continue;
    }

    InstIt head_thread = instList[tid].begin();
    DynInstPtr head_inst = (*head_thread);

    if (head_inst->seqNum < lowest_num) {
        head = head_thread;
        lowest_num = head_inst->seqNum;
    }
}

6.2 updateTail()函数 - 更新全局尾指针 (rob.cc:618-651)

功能: 在多线程环境下找到序列号最大的指令作为全局尾部

void ROB::updateTail()
{
    tail = instList[0].end();
    bool first_valid = true;

    for (ThreadID tid : *activeThreads) {
        if (instList[tid].empty()) continue;

        InstIt tail_thread = instList[tid].end();
        tail_thread--;

        if (first_valid) {
            tail = tail_thread;
            first_valid = false;
        } else if ((*tail_thread)->seqNum > (*tail)->seqNum) {
            tail = tail_thread;
        }
    }
}

7. 指令组管理函数

7.1 commitGroup()函数 - 提交组计数更新 (rob.cc:314-323)

void ROB::commitGroup(const DynInstPtr inst, ThreadID tid)
{
    assert(!threadGroups[tid].empty());

    if (threadGroups[tid].front() == 1) {
        threadGroups[tid].pop_front(); // 组内最后一条指令
    } else {
        threadGroups[tid].front()--;   // 减少组内指令计数
    }
}

7.2 squashGroup()函数 - 冲刷组计数更新 (rob.cc:326-335)

void ROB::squashGroup(const DynInstPtr inst, ThreadID tid)
{
    assert(!threadGroups[tid].empty());

    if (threadGroups[tid].back() == 1) {
        threadGroups[tid].pop_back();  // 组内最后一条指令
    } else {
        threadGroups[tid].back()--;    // 减少组内指令计数
    }
}

8. 容量管理和查询函数

8.1 numFreeEntries()函数 - 空闲条目查询 (rob.cc:469-472)

unsigned ROB::numFreeEntries(ThreadID tid)
{
    return maxEntries[tid] - threadGroups[tid].size();
}

8.2 countInstsOfGroups()函数 - 指令组计数 (rob.cc:301-311)

uint32_t ROB::countInstsOfGroups(int groups)
{
    int sum = 0;
    for (ThreadID tid = 0; tid < numThreads; tid++) {
        auto it = threadGroups[tid].begin();
        for (int i = 0; i < groups && it != threadGroups[tid].end(); i++, it++) {
            sum += *it;
        }
    }
    return sum;
}

关键设计特点

1. 指令组压缩优化

  • 存储效率: 通过指令分组减少ROB条目数量
  • 灵活策略: 支持多种分组策略适应不同微架构需求
  • 动态调整: 根据指令类型和时序动态决定分组

2. 多线程支持

  • 资源分配: 支持Dynamic、Partitioned、Threshold三种SMT策略
  • 全局一致性: 维护跨线程的全局头尾指针
  • 公平调度: 确保多线程间的公平性和正确性

3. 高效冲刷机制

  • 动态带宽: 根据不同策略动态调整冲刷速度
  • 渐进式处理: 支持多周期完成大规模冲刷
  • 状态维护: 精确维护冲刷过程中的ROB状态

4. 精确异常支持

  • 顺序提交: 严格按程序顺序提交指令
  • 异常传播: 支持精确异常的正确传播
  • 状态回滚: 支持异常时的精确状态恢复

与其他组件的交互

1. 与Commit阶段

  • 指令提交: readHeadInst(), retireHead(), isHeadGroupReady()
  • 容量查询: numFreeEntries(), isEmpty(), isFull()
  • 冲刷协作: squash(), doSquash(), isDoneSquashing()

2. 与Rename阶段

  • 指令插入: insertInst()接收重命名后的指令
  • 容量检查: 检查ROB是否有足够空间接受新指令

3. 与IEW阶段

  • 执行结果: 接收指令执行完成的通知
  • 异常处理: 处理执行阶段发现的异常

4. 与CPU核心

  • 统计信息: 提供详细的性能统计数据
  • 状态查询: 提供ROB当前状态给CPU调度器

性能优化特性

1. 统计信息收集 (rob.hh:344-354)

struct ROBStats : public statistics::Group
{
    statistics::Scalar reads;           // ROB读取次数
    statistics::Scalar writes;          // ROB写入次数  
    statistics::Distribution instPergroup; // 每组指令数分布
};

2. 调试和诊断支持

  • DPRINTF支持: 详细的调试输出
  • 状态断言: 关键状态的一致性检查
  • 性能分析: 支持指令组大小分布统计

总结

ROB是O3 CPU的核心组件,负责维持指令的程序顺序并支持乱序执行。香山的ROB实现在标准功能基础上增加了指令组压缩优化,显著提高了存储效率和提交带宽。通过精心设计的冲刷机制、多线程支持和动态资源管理,ROB确保了乱序执行CPU的正确性和高性能。其模块化的设计使得不同的压缩策略可以灵活配置,为不同的微架构需求提供了优化空间。