Instruction情報について
sniper/common/performance_model/instruction.h class Instruction { public: Instruction(InstructionType type, OperandList &operands); Instruction(InstructionType type); virtual ~Instruction() { }; virtual SubsecondTime getCost(Core *core) const; InstructionType getType() const; String getTypeName() const; bool isPseudo() const { return getType() >= INST_PSEUDO_MISC; } bool isIdle() const { return getType() == INST_SYNC || getType() == INST_DELAY || getType() == INST_RECV; } static void initializeStaticInstructionModel(); const OperandList& getOperands() const { return m_operands; } void setAddress(IntPtr addr) { m_addr = addr; } IntPtr getAddress() const { return m_addr; } void setSize(UInt32 size) { m_size = size; } UInt32 getSize() const { return m_size; } void setAtomic(bool atomic) { m_atomic = atomic; } bool isAtomic() const { return m_atomic; } void setDisassembly(String str) { m_disas = str; }
DynamicInstruction情報について
Instructionが命令の情報そのものなのに対し、DynamicInstructionはクラス情報で補助的な情報を含んでいる。
sniper/common/performance_model/dynamic_instruction.h class DynamicInstruction { Instruction* instruction; IntPtr eip; // Can be physical address, so different from instruction->getAddress() which is always virtual BranchInfo branch_info; unsigned int num_memory; MemoryInfo memory_info[MAX_MEMORY]; ...
レジスタ書き込みの情報はどこに発生するのだろうか?
ベクトル命令が命令分解をするための方法について
Instructionクラスにm_uopsメンバがついている。これを実装する必要があるっぽい?
sniper/common/performance_model/instruction.h void setMicroOps(const std::vector<const MicroOp *> *uops) { m_uops = uops; }
このuopsはデコード時に設定するものらしい。
Instruction* TraceThread::decode(Sift::Instruction &inst) { /* ... 途中省略 ... */ printf("disassembly : %s\n", instruction->getDisassembly().c_str()); const std::vector<const MicroOp*> *uops = InstructionDecoder::decode(inst.sinst->addr, &dec_inst, instruction); instruction->setMicroOps(uops); return instruction; }
void TraceThread::handleInstructionDetailed(Sift::Instruction &inst, Sift::Instruction &next_inst, PerformanceModel *prfmdl) Instruction inst = Siftからデコードする decode(inst); デコードした情報をもとにDynamicInstructionを作成する PerformanceModel::iterate() InstructionQueueからDynamicInstructionを1つ取り出す MicroOpPerformanceModel::handleInstruction(DynamicInstruction *dynins)を実行して命令を動かす RobPerformanceModel::simulate(const std::vector<DynamicMicroOp*>& insts)で命令をシミュレートする RobTimer::simulate(const std::vector<DynamicMicroOp*>& insts)で命令をシミュレートする
void MicroOpPerformanceModel::handleInstruction(DynamicInstruction *dynins)
では、MicroOpsは1つしか生成されていなかった。
if (dynins->instruction->getMicroOps()) { for(std::vector<const MicroOp*>::const_iterator it = dynins->instruction->getMicroOps()->begin(); it != dynins->instruction->getMicroOps()->end(); it++) { m_current_uops.push_back(m_core_model->createDynamicMicroOp(m_allocator, *it, insn_period)); } }
1命令の中に複数のメモリアクセスが格納されているということになる。
rvv_memcpyの場合は無事に検出できているようだった。
disassembly : vle8.v v24, a0 m_current_uops.size() = 128, num_loads = 128, ops_size = 128, num_memory = 128
m_current_uops
は128個生成されているという前提になる。
VLE32.v
の場合は32個 (1レジスタ4個 $\times$ 8レジスタ)で動いてほしい。
これを実現するためにいろいろ悩んだのだが、結果的にSpikeでの実行中にログを8つに分割する方法が最も現実的な気がしてきた。
メモリアクセスとレジスタアクセスを検査して、8つのレジスタ書き込みがあれば対応するメモリアクセスを収集し、それを1つの命令としてまとめ上げる。
static void log_print_sift_trace(processor_t* p, reg_t pc, insn_t insn) ... if (vreg_num > 0) { for (reg_t vreg_idx = 0; vreg_idx < vreg_num; vreg_idx++) { uint64_t uop_addresses[128]; uint64_t num_mem_addr = 0; for (uint64_t addr_i = 0; addr_i < num_addresses; addr_i++) { if (vreg_wr_array[vreg_idx] == wr_regs[addr_i]) { uop_addresses[num_mem_addr++] = addresses[addr_i]; } } p->get_state()->log_writer->Instruction(addr, size, num_mem_addr, uop_addresses, is_branch, taken, 0 /*is_predicate*/, 1 /*executed*/); }