IR: Keep IRWriter's instructions in a FastVec, and inline Write

The compiler didn't inline std::vector<IRInst>::push_back into IRWriter::Write,
which the frontend and every pass call for each instruction. FastVec's append
is a compare and a store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-11-10 20:26:45 +01:00
1 parent 235c5ba87b
commit dcc51d8b18
4 files changed
+26 -17

No files matched your search

+2 -2
View File
@@ -387,10 +387,10 @@ void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &m
}
if (!mipsTracer.tracing_enabled) {
instructions = code->GetInstructions();
instructions.assign(code->GetInstructions().begin(), code->GetInstructions().end());
}
else {
std::vector<IRInst> block_instructions = code->GetInstructions();
std::vector<IRInst> block_instructions(code->GetInstructions().begin(), code->GetInstructions().end());
instructions.reserve(block_instructions.size() + 2); // +2 for Downcount and LogIRBlock
// The first instruction is "Downcount"
instructions.push_back(block_instructions.front());
-10
View File
@@ -231,16 +231,6 @@ void InitIR() {
}
}
void IRWriter::Write(IROp op, u8 dst, u8 src1, u8 src2, u32 constant) {
IRInst inst;
inst.op = op;
inst.dest = dst;
inst.src1 = src1;
inst.src2 = src2;
inst.constant = constant;
insts_.push_back(inst);
}
void IRWriter::WriteFC(IROp op, u8 dst, u8 src1, u8 src2, float fconstant) {
u32 constant;
memcpy(&constant, &fconstant, sizeof(u32));
+23 -4
View File
@@ -5,6 +5,7 @@
#include <utility>
#include "Common/CommonTypes.h"
#include "Common/Data/Collections/FastVec.h"
#include "Core/MIPS/MIPS.h"
// Basic IR
@@ -424,8 +425,17 @@ u32 IRInterpret(MIPSState *ms, const IRInst *inst, const IRInst *chainArena = nu
// Each IR block gets a constant pool.
class IRWriter {
public:
IRWriter() {}
IRWriter(const IRWriter &w) {
*this = w;
}
IRWriter(IRWriter &&w) noexcept : insts_(std::move(w.insts_)) {}
IRWriter &operator =(const IRWriter &w) {
insts_ = w.insts_;
if (this != &w) {
insts_.clear();
if (!w.insts_.empty())
insts_.extend(w.insts_.data(), w.insts_.size());
}
return *this;
}
IRWriter &operator =(IRWriter &&w) noexcept {
@@ -433,7 +443,16 @@ public:
return *this;
}
void Write(IROp op, u8 dst = 0, u8 src1 = 0, u8 src2 = 0, u32 constant = 0);
// Inline (with FastVec rather than std::vector, which the compiler didn't inline), since the
// frontend and every pass write each instruction through these.
void Write(IROp op, u8 dst = 0, u8 src1 = 0, u8 src2 = 0, u32 constant = 0) {
IRInst &inst = insts_.push_uninitialized();
inst.op = op;
inst.dest = dst;
inst.src1 = src1;
inst.src2 = src2;
inst.constant = constant;
}
void WriteFC(IROp op, u8 dst, u8 src1, u8 src2, float fconstant);
void WriteSetConstant(u8 dst, u32 value);
@@ -450,10 +469,10 @@ public:
}
void ReplaceConstant(size_t instNumber, u32 newConstant);
const std::vector<IRInst> &GetInstructions() const { return insts_; }
const FastVec<IRInst> &GetInstructions() const { return insts_; }
private:
std::vector<IRInst> insts_;
FastVec<IRInst> insts_;
};
// How far IRFrontend compiles on past conditional branches. The interpreter ends its blocks in the
+1 -1
View File
@@ -57,7 +57,7 @@ static bool VerifyPass(const IRVerification &v) {
return false;
}
const std::vector<IRInst> actual = out.GetInstructions();
const std::vector<IRInst> actual(out.GetInstructions().begin(), out.GetInstructions().end());
if (actual.size() != v.expected.size()) {
printf("%s FAILED: produced %d instructions, expected %d\n", v.name, (int)actual.size(), (int)v.expected.size());
printf("Actual:\n");