Merge pull request #18282 from unknownbrackets/ir-compiling

Improve IR compilation performance
This commit is contained in:
Henrik Rydgård 2023-10-01 11:34:27 +02:00 committed by GitHub
commit db805cc4cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 127 additions and 113 deletions

View file

@ -93,12 +93,11 @@ bool Arm64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
regs_.Start(block);
std::map<const u8 *, int> addresses;
std::vector<const u8 *> addresses;
for (int i = 0; i < block->GetNumInstructions(); ++i) {
const IRInst &inst = block->GetInstructions()[i];
regs_.SetIRIndex(i);
// TODO: This might be a little wasteful when compiling if we're not debugging jit...
addresses[GetCodePtr()] = i;
addresses.push_back(GetCodePtr());
CompileIRInst(inst);
@ -150,10 +149,14 @@ bool Arm64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
if (logBlocks_ > 0) {
--logBlocks_;
std::map<const u8 *, int> addressesLookup;
for (int i = 0; i < (int)addresses.size(); ++i)
addressesLookup[addresses[i]] = i;
INFO_LOG(JIT, "=============== ARM64 (%08x, %d bytes) ===============", startPC, len);
for (const u8 *p = blockStart; p < GetCodePointer(); ) {
auto it = addresses.find(p);
if (it != addresses.end()) {
auto it = addressesLookup.find(p);
if (it != addressesLookup.end()) {
const IRInst &inst = block->GetInstructions()[it->second];
char temp[512];
@ -162,7 +165,7 @@ bool Arm64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
}
auto next = std::next(it);
const u8 *nextp = next == addresses.end() ? GetCodePointer() : next->first;
const u8 *nextp = next == addressesLookup.end() ? GetCodePointer() : next->first;
auto lines = DisassembleArm64(p, (int)(nextp - p));
for (const auto &line : lines)

View file

@ -21,20 +21,18 @@
#include <algorithm>
static bool IRReadsFrom(const IRInst &inst, int reg, char type, bool *directly) {
const IRMeta *m = GetIRMeta(inst.op);
if (m->types[1] == type && inst.src1 == reg) {
static bool IRReadsFrom(const IRInstMeta &inst, int reg, char type, bool *directly) {
if (inst.m.types[1] == type && inst.src1 == reg) {
if (directly)
*directly = true;
return true;
}
if (m->types[2] == type && inst.src2 == reg) {
if (inst.m.types[2] == type && inst.src2 == reg) {
if (directly)
*directly = true;
return true;
}
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == type && inst.src3 == reg) {
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && inst.m.types[0] == type && inst.src3 == reg) {
if (directly)
*directly = true;
return true;
@ -42,44 +40,41 @@ static bool IRReadsFrom(const IRInst &inst, int reg, char type, bool *directly)
if (directly)
*directly = false;
if ((m->flags & (IRFLAG_EXIT | IRFLAG_BARRIER)) != 0)
if ((inst.m.flags & (IRFLAG_EXIT | IRFLAG_BARRIER)) != 0)
return true;
return false;
}
bool IRReadsFromFPR(const IRInst &inst, int reg, bool *directly) {
bool IRReadsFromFPR(const IRInstMeta &inst, int reg, bool *directly) {
if (IRReadsFrom(inst, reg, 'F', directly))
return true;
const IRMeta *m = GetIRMeta(inst.op);
// We also need to check V and 2. Indirect reads already checked, don't check again.
if (m->types[1] == 'V' && reg >= inst.src1 && reg < inst.src1 + 4)
if (inst.m.types[1] == 'V' && reg >= inst.src1 && reg < inst.src1 + 4)
return true;
if (m->types[1] == '2' && reg >= inst.src1 && reg < inst.src1 + 2)
if (inst.m.types[1] == '2' && reg >= inst.src1 && reg < inst.src1 + 2)
return true;
if (m->types[2] == 'V' && reg >= inst.src2 && reg < inst.src2 + 4)
if (inst.m.types[2] == 'V' && reg >= inst.src2 && reg < inst.src2 + 4)
return true;
if (m->types[2] == '2' && reg >= inst.src2 && reg < inst.src2 + 2)
if (inst.m.types[2] == '2' && reg >= inst.src2 && reg < inst.src2 + 2)
return true;
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) {
if (m->types[0] == 'V' && reg >= inst.src3 && reg <= inst.src3 + 4)
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) {
if (inst.m.types[0] == 'V' && reg >= inst.src3 && reg <= inst.src3 + 4)
return true;
if (m->types[0] == '2' && reg >= inst.src3 && reg <= inst.src3 + 2)
if (inst.m.types[0] == '2' && reg >= inst.src3 && reg <= inst.src3 + 2)
return true;
}
return false;
}
static int IRReadsFromList(const IRInst &inst, IRReg regs[4], char type) {
const IRMeta *m = GetIRMeta(inst.op);
static int IRReadsFromList(const IRInstMeta &inst, IRReg regs[4], char type) {
int c = 0;
if (m->types[1] == type)
if (inst.m.types[1] == type)
regs[c++] = inst.src1;
if (m->types[2] == type)
if (inst.m.types[2] == type)
regs[c++] = inst.src2;
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == type)
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && inst.m.types[0] == type)
regs[c++] = inst.src3;
if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break)
@ -90,56 +85,50 @@ static int IRReadsFromList(const IRInst &inst, IRReg regs[4], char type) {
return c;
}
bool IRReadsFromGPR(const IRInst &inst, int reg, bool *directly) {
bool IRReadsFromGPR(const IRInstMeta &inst, int reg, bool *directly) {
return IRReadsFrom(inst, reg, 'G', directly);
}
int IRDestGPR(const IRInst &inst) {
const IRMeta *m = GetIRMeta(inst.op);
if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G') {
int IRDestGPR(const IRInstMeta &inst) {
if ((inst.m.flags & IRFLAG_SRC3) == 0 && inst.m.types[0] == 'G') {
return inst.dest;
}
return -1;
}
bool IRWritesToGPR(const IRInst &inst, int reg) {
bool IRWritesToGPR(const IRInstMeta &inst, int reg) {
return IRDestGPR(inst) == reg;
}
bool IRWritesToFPR(const IRInst &inst, int reg) {
const IRMeta *m = GetIRMeta(inst.op);
bool IRWritesToFPR(const IRInstMeta &inst, int reg) {
// Doesn't write to anything.
if ((m->flags & IRFLAG_SRC3) != 0)
if ((inst.m.flags & IRFLAG_SRC3) != 0)
return false;
if (m->types[0] == 'F' && reg == inst.dest)
if (inst.m.types[0] == 'F' && reg == inst.dest)
return true;
if (m->types[0] == 'V' && reg >= inst.dest && reg < inst.dest + 4)
if (inst.m.types[0] == 'V' && reg >= inst.dest && reg < inst.dest + 4)
return true;
if (m->types[0] == '2' && reg >= inst.dest && reg < inst.dest + 2)
if (inst.m.types[0] == '2' && reg >= inst.dest && reg < inst.dest + 2)
return true;
return false;
}
int IRDestFPRs(const IRInst &inst, IRReg regs[4]) {
const IRMeta *m = GetIRMeta(inst.op);
int IRDestFPRs(const IRInstMeta &inst, IRReg regs[4]) {
// Doesn't write to anything.
if ((m->flags & IRFLAG_SRC3) != 0)
if ((inst.m.flags & IRFLAG_SRC3) != 0)
return 0;
if (m->types[0] == 'F') {
if (inst.m.types[0] == 'F') {
regs[0] = inst.dest;
return 1;
}
if (m->types[0] == 'V') {
if (inst.m.types[0] == 'V') {
for (int i = 0; i < 4; ++i)
regs[i] = inst.dest + i;
return 4;
}
if (m->types[0] == '2') {
if (inst.m.types[0] == '2') {
for (int i = 0; i < 2; ++i)
regs[i] = inst.dest + i;
return 2;
@ -147,29 +136,27 @@ int IRDestFPRs(const IRInst &inst, IRReg regs[4]) {
return 0;
}
int IRReadsFromGPRs(const IRInst &inst, IRReg regs[4]) {
int IRReadsFromGPRs(const IRInstMeta &inst, IRReg regs[4]) {
return IRReadsFromList(inst, regs, 'G');
}
int IRReadsFromFPRs(const IRInst &inst, IRReg regs[16]) {
int IRReadsFromFPRs(const IRInstMeta &inst, IRReg regs[16]) {
int c = IRReadsFromList(inst, regs, 'F');
if (c != 0)
return c;
const IRMeta *m = GetIRMeta(inst.op);
// We also need to check V and 2. Indirect reads already checked, don't check again.
if (m->types[1] == 'V' || m->types[1] == '2') {
for (int i = 0; i < (m->types[1] == 'V' ? 4 : 2); ++i)
if (inst.m.types[1] == 'V' || inst.m.types[1] == '2') {
for (int i = 0; i < (inst.m.types[1] == 'V' ? 4 : 2); ++i)
regs[c++] = inst.src1 + i;
}
if (m->types[2] == 'V' || m->types[2] == '2') {
for (int i = 0; i < (m->types[2] == 'V' ? 4 : 2); ++i)
if (inst.m.types[2] == 'V' || inst.m.types[2] == '2') {
for (int i = 0; i < (inst.m.types[2] == 'V' ? 4 : 2); ++i)
regs[c++] = inst.src2 + i;
}
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) {
if (m->types[0] == 'V' || m->types[0] == '2') {
for (int i = 0; i < (m->types[0] == 'V' ? 4 : 2); ++i)
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) {
if (inst.m.types[0] == 'V' || inst.m.types[0] == '2') {
for (int i = 0; i < (inst.m.types[0] == 'V' ? 4 : 2); ++i)
regs[c++] = inst.src3 + i;
}
}
@ -183,7 +170,7 @@ IRUsage IRNextGPRUsage(int gpr, const IRSituation &info) {
int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount);
for (int i = 0; i < count; ++i) {
const IRInst inst = info.instructions[info.currentIndex + i];
const IRInstMeta inst = GetIRMeta(info.instructions[info.currentIndex + i]);
if (IRReadsFromGPR(inst, gpr))
return IRUsage::READ;
// We say WRITE when the current instruction writes. It's not useful for spilling.
@ -202,7 +189,7 @@ IRUsage IRNextFPRUsage(int fpr, const IRSituation &info) {
int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount);
for (int i = 0; i < count; ++i) {
const IRInst inst = info.instructions[info.currentIndex + i];
const IRInstMeta inst = GetIRMeta(info.instructions[info.currentIndex + i]);
if (IRReadsFromFPR(inst, fpr)) {
// Special case a broadcast that clobbers it.

View file

@ -19,14 +19,37 @@
#include "Core/MIPS/IR/IRInst.h"
bool IRReadsFromFPR(const IRInst &inst, int reg, bool *directly = nullptr);
bool IRReadsFromGPR(const IRInst &inst, int reg, bool *directly = nullptr);
bool IRWritesToGPR(const IRInst &inst, int reg);
bool IRWritesToFPR(const IRInst &inst, int reg);
int IRDestGPR(const IRInst &inst);
int IRDestFPRs(const IRInst &inst, IRReg regs[4]);
int IRReadsFromGPRs(const IRInst &inst, IRReg regs[4]);
int IRReadsFromFPRs(const IRInst &inst, IRReg regs[16]);
struct IRInstMeta {
union {
IRInst i;
struct {
IROp op;
union {
IRReg dest;
IRReg src3;
};
IRReg src1;
IRReg src2;
u32 constant;
};
};
IRMeta m;
};
static_assert(offsetof(IRInst, src2) == offsetof(IRInstMeta, src2));
inline IRInstMeta GetIRMeta(const IRInst &inst) {
return { { inst }, *GetIRMeta(inst.op) };
}
bool IRReadsFromFPR(const IRInstMeta &inst, int reg, bool *directly = nullptr);
bool IRReadsFromGPR(const IRInstMeta &inst, int reg, bool *directly = nullptr);
bool IRWritesToGPR(const IRInstMeta &inst, int reg);
bool IRWritesToFPR(const IRInstMeta &inst, int reg);
int IRDestGPR(const IRInstMeta &inst);
int IRDestFPRs(const IRInstMeta &inst, IRReg regs[4]);
int IRReadsFromGPRs(const IRInstMeta &inst, IRReg regs[4]);
int IRReadsFromFPRs(const IRInstMeta &inst, IRReg regs[16]);
struct IRSituation {
int lookaheadCount;

View file

@ -341,7 +341,7 @@ enum IRFlags {
struct IRMeta {
IROp op;
const char *name;
const char types[5]; // GGG
char types[5]; // GGG
u32 flags;
};

View file

@ -919,40 +919,37 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
return logBlocks;
}
IRInst IRReplaceSrcGPR(const IRInst &inst, int fromReg, int toReg) {
IRInst newInst = inst;
const IRMeta *m = GetIRMeta(inst.op);
IRInstMeta IRReplaceSrcGPR(const IRInstMeta &inst, int fromReg, int toReg) {
IRInstMeta newInst = inst;
if (m->types[1] == 'G' && inst.src1 == fromReg) {
if (inst.m.types[1] == 'G' && inst.src1 == fromReg) {
newInst.src1 = toReg;
}
if (m->types[2] == 'G' && inst.src2 == fromReg) {
if (inst.m.types[2] == 'G' && inst.src2 == fromReg) {
newInst.src2 = toReg;
}
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == 'G' && inst.src3 == fromReg) {
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && inst.m.types[0] == 'G' && inst.src3 == fromReg) {
newInst.src3 = toReg;
}
return newInst;
}
IRInst IRReplaceDestGPR(const IRInst &inst, int fromReg, int toReg) {
IRInst newInst = inst;
const IRMeta *m = GetIRMeta(inst.op);
IRInstMeta IRReplaceDestGPR(const IRInstMeta &inst, int fromReg, int toReg) {
IRInstMeta newInst = inst;
if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G' && inst.dest == fromReg) {
if ((inst.m.flags & IRFLAG_SRC3) == 0 && inst.m.types[0] == 'G' && inst.dest == fromReg) {
newInst.dest = toReg;
}
return newInst;
}
bool IRMutatesDestGPR(const IRInst &inst, int reg) {
const IRMeta *m = GetIRMeta(inst.op);
return (m->flags & IRFLAG_SRC3DST) != 0 && m->types[0] == 'G' && inst.src3 == reg;
bool IRMutatesDestGPR(const IRInstMeta &inst, int reg) {
return (inst.m.flags & IRFLAG_SRC3DST) != 0 && inst.m.types[0] == 'G' && inst.src3 == reg;
}
bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
CONDITIONAL_DISABLE;
std::vector<IRInst> insts;
std::vector<IRInstMeta> insts;
insts.reserve(in.GetInstructions().size());
// We track writes both to rename regs and to purge dead stores.
@ -979,7 +976,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
memset(lastWrittenTo, -1, sizeof(lastWrittenTo));
memset(lastReadFrom, -1, sizeof(lastReadFrom));
auto readsFromFPRCheck = [](IRInst &inst, Check &check, bool *directly) {
auto readsFromFPRCheck = [](IRInstMeta &inst, Check &check, bool *directly) {
if (check.reg < 32)
return false;
@ -1001,8 +998,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
bool logBlocks = false;
size_t firstCheck = 0;
for (int i = 0, n = (int)in.GetInstructions().size(); i < n; i++) {
IRInst inst = in.GetInstructions()[i];
const IRMeta *m = GetIRMeta(inst.op);
IRInstMeta inst = GetIRMeta(in.GetInstructions()[i]);
// It helps to skip through rechecking ones we already discarded.
for (size_t ch = firstCheck; ch < checks.size(); ++ch) {
@ -1060,14 +1056,13 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
} else {
// Legitimately read from, so we can't optimize out.
// Unless this is an exit and a temp not read directly by the exit.
if ((m->flags & IRFLAG_EXIT) == 0 || check.readByExit || readsDirectly)
if ((inst.m.flags & IRFLAG_EXIT) == 0 || check.readByExit || readsDirectly)
check.reg = 0;
}
} else if (check.fplen >= 1 && readsFromFPRCheck(inst, check, &readsDirectly)) {
// If one or the other is a Vec, they must match.
bool lenMismatch = false;
const IRMeta *m = GetIRMeta(inst.op);
auto checkMismatch = [&check, &lenMismatch](IRReg src, char type) {
int srclen = 1;
if (type == 'V')
@ -1083,10 +1078,10 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
}
};
checkMismatch(inst.src1, m->types[1]);
checkMismatch(inst.src2, m->types[2]);
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0)
checkMismatch(inst.src3, m->types[3]);
checkMismatch(inst.src1, inst.m.types[1]);
checkMismatch(inst.src2, inst.m.types[2]);
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0)
checkMismatch(inst.src3, inst.m.types[3]);
bool cannotReplace = !readsDirectly || lenMismatch;
if (!cannotReplace && check.srcReg >= 32 && lastWrittenTo[check.srcReg] < check.index) {
@ -1137,10 +1132,10 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
}
} else {
// Legitimately read from, so we can't optimize out.
if ((m->flags & IRFLAG_EXIT) == 0 || check.readByExit || readsDirectly)
if ((inst.m.flags & IRFLAG_EXIT) == 0 || check.readByExit || readsDirectly)
check.reg = 0;
}
} else if (check.readByExit && (m->flags & IRFLAG_EXIT) != 0) {
} else if (check.readByExit && (inst.m.flags & IRFLAG_EXIT) != 0) {
// This is an exit, and the reg is read by any exit. Clear it.
check.reg = 0;
} else if (IRDestGPR(inst) == check.reg) {
@ -1261,10 +1256,10 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
}
}
for (const IRInst &inst : insts) {
for (const IRInstMeta &inst : insts) {
// Simply skip any Mov 0, 0 instructions, since that's how we nuke one.
if (inst.op != IROp::Mov || inst.dest != 0 || inst.src1 != 0) {
out.Write(inst);
out.Write(inst.i);
}
}
@ -1282,12 +1277,11 @@ bool ReduceLoads(const IRWriter &in, IRWriter &out, const IROptions &opts) {
IRInst inst = in.GetInstructions()[i];
if (inst.op == IROp::Load32 || inst.op == IROp::Load16 || inst.op == IROp::Load16Ext) {
int dest = IRDestGPR(inst);
int dest = IRDestGPR(GetIRMeta(inst));
for (int j = i + 1; j < n; j++) {
const IRInst &laterInst = in.GetInstructions()[j];
const IRMeta *m = GetIRMeta(laterInst.op);
const IRInstMeta laterInst = GetIRMeta(in.GetInstructions()[j]);
if ((m->flags & (IRFLAG_EXIT | IRFLAG_BARRIER)) != 0) {
if ((laterInst.m.flags & (IRFLAG_EXIT | IRFLAG_BARRIER)) != 0) {
// Exit, so we can't do the optimization.
break;
}

View file

@ -82,12 +82,11 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
regs_.Start(block);
std::map<const u8 *, int> addresses;
std::vector<const u8 *> addresses;
for (int i = 0; i < block->GetNumInstructions(); ++i) {
const IRInst &inst = block->GetInstructions()[i];
regs_.SetIRIndex(i);
// TODO: This might be a little wasteful when compiling if we're not debugging jit...
addresses[GetCodePtr()] = i;
addresses.push_back(GetCodePtr());
CompileIRInst(inst);
@ -137,10 +136,14 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
if (logBlocks_ > 0) {
--logBlocks_;
std::map<const u8 *, int> addressesLookup;
for (int i = 0; i < (int)addresses.size(); ++i)
addressesLookup[addresses[i]] = i;
INFO_LOG(JIT, "=============== RISCV (%08x, %d bytes) ===============", startPC, len);
for (const u8 *p = blockStart; p < GetCodePointer(); ) {
auto it = addresses.find(p);
if (it != addresses.end()) {
auto it = addressesLookup.find(p);
if (it != addressesLookup.end()) {
const IRInst &inst = block->GetInstructions()[it->second];
char temp[512];
@ -149,7 +152,7 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
}
auto next = std::next(it);
const u8 *nextp = next == addresses.end() ? GetCodePointer() : next->first;
const u8 *nextp = next == addressesLookup.end() ? GetCodePointer() : next->first;
#if PPSSPP_ARCH(RISCV64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
auto lines = DisassembleRV64(p, (int)(nextp - p));

View file

@ -86,12 +86,12 @@ bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) {
regs_.Start(block);
std::map<const u8 *, int> addresses;
std::vector<const u8 *> addresses;
addresses.reserve(block->GetNumInstructions());
for (int i = 0; i < block->GetNumInstructions(); ++i) {
const IRInst &inst = block->GetInstructions()[i];
regs_.SetIRIndex(i);
// TODO: This might be a little wasteful when compiling if we're not debugging jit...
addresses[GetCodePtr()] = i;
addresses.push_back(GetCodePtr());
CompileIRInst(inst);
@ -140,10 +140,14 @@ bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) {
if (logBlocks_ > 0) {
--logBlocks_;
std::map<const u8 *, int> addressesLookup;
for (int i = 0; i < (int)addresses.size(); ++i)
addressesLookup[addresses[i]] = i;
INFO_LOG(JIT, "=============== x86 (%08x, %d bytes) ===============", startPC, len);
for (const u8 *p = blockStart; p < GetCodePointer(); ) {
auto it = addresses.find(p);
if (it != addresses.end()) {
auto it = addressesLookup.find(p);
if (it != addressesLookup.end()) {
const IRInst &inst = block->GetInstructions()[it->second];
char temp[512];
@ -152,7 +156,7 @@ bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) {
}
auto next = std::next(it);
const u8 *nextp = next == addresses.end() ? GetCodePointer() : next->first;
const u8 *nextp = next == addressesLookup.end() ? GetCodePointer() : next->first;
auto lines = DisassembleX86(p, (int)(nextp - p));
for (const auto &line : lines)