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)
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,设置SMT策略和指令组压缩策略
关键逻辑:
- SMT资源分配策略:
- Dynamic: 所有线程共享全部ROB容量
- Partitioned: 平均分配ROB容量给各线程
- Threshold: 基于阈值的资源分配
- 指令组压缩策略选择:
2. insertInst()函数 - 指令插入 (rob.cc:338-384)
功能: 将新指令插入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)
功能: 退休指定线程的头部指令,更新指令组计数
执行流程:
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 更新全局状态
4. isHeadGroupReady()函数 - 头部组就绪检查 (rob.cc:424-448)
功能: 检查指定线程的头部指令组是否全部准备好提交
检查逻辑:
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)
功能: 启动对指定线程的指令冲刷,冲刷序列号大于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)
功能: 实际执行指令冲刷,按带宽限制逐条处理
冲刷循环核心逻辑:
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)
功能: 根据冲刷策略计算每周期冲刷指令数量
策略类型:
- Rollback: 固定冲刷带宽 rollbackWidth
- Replay: 基于未提交指令数量动态调整
expected_cycles = std::max(2.0, ((double)uncommitted_insts / replayWidth));
dyn_squash_width = ceil((double)to_squash / expected_cycles);
6. 头尾指针维护函数
6.1 updateHead()函数 - 更新全局头指针 (rob.cc:577-614)
功能: 在多线程环境下找到序列号最小的指令作为全局头部
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)
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的正确性和高性能。其模块化的设计使得不同的压缩策略可以灵活配置,为不同的微架构需求提供了优化空间。