a32_get_set_elimination_pass: New algorithm

This commit is contained in:
Merry 2023-01-19 18:52:41 +00:00
parent 76aa4dd665
commit 33fa44acf6

View file

@ -17,59 +17,58 @@
namespace Dynarmic::Optimization {
void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt) {
using Iterator = IR::Block::iterator;
void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions) {
using Iterator = std::reverse_iterator<IR::Block::iterator>;
struct RegisterInfo {
IR::Value register_value;
bool set_instruction_present = false;
Iterator last_set_instruction;
bool set_not_required = false;
bool has_value_request = false;
Iterator value_request = {};
};
struct ValuelessRegisterInfo {
bool set_not_required = false;
};
std::array<RegisterInfo, 15> reg_info;
std::array<RegisterInfo, 64> ext_reg_singles_info;
std::array<RegisterInfo, 32> ext_reg_doubles_info;
std::array<RegisterInfo, 32> ext_reg_vector_double_info;
std::array<RegisterInfo, 16> ext_reg_vector_quad_info;
struct CpsrInfo {
RegisterInfo nz;
RegisterInfo c;
RegisterInfo nzc;
RegisterInfo nzcv;
ValuelessRegisterInfo nzcvq;
ValuelessRegisterInfo nzcv;
ValuelessRegisterInfo nz;
RegisterInfo c_flag;
RegisterInfo ge;
} cpsr_info;
const auto do_delete_last_set = [&block](RegisterInfo& info) {
if (info.set_instruction_present) {
info.set_instruction_present = false;
info.last_set_instruction->Invalidate();
block.Instructions().erase(info.last_set_instruction);
auto do_set = [&](RegisterInfo& info, IR::Value value, Iterator inst) {
if (info.has_value_request) {
info.value_request->ReplaceUsesWith(value);
}
info = {};
};
info.has_value_request = false;
const auto do_set = [&do_delete_last_set](RegisterInfo& info, IR::Value value, Iterator set_inst) {
do_delete_last_set(info);
info.register_value = value;
info.set_instruction_present = true;
info.last_set_instruction = set_inst;
};
const auto do_set_without_inst = [&do_delete_last_set](RegisterInfo& info, IR::Value value) {
do_delete_last_set(info);
info.register_value = value;
};
const auto do_get = [](RegisterInfo& info, Iterator get_inst) {
if (info.register_value.IsEmpty()) {
info.register_value = IR::Value(&*get_inst);
return;
if (info.set_not_required) {
inst->Invalidate();
}
get_inst->ReplaceUsesWith(info.register_value);
info.set_not_required = true;
};
auto do_set_valueless = [&](ValuelessRegisterInfo& info, Iterator inst) {
if (info.set_not_required) {
inst->Invalidate();
}
info.set_not_required = true;
};
auto do_get = [](RegisterInfo& info, Iterator inst) {
if (info.has_value_request) {
info.value_request->ReplaceUsesWith(IR::Value{&*inst});
}
info.has_value_request = true;
info.value_request = inst;
};
// Location and version don't matter here.
A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}};
for (auto inst = block.begin(); inst != block.end(); ++inst) {
for (auto inst = block.rbegin(); inst != block.rend(); ++inst) {
switch (inst->GetOpcode()) {
case IR::Opcode::A32SetRegister: {
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
@ -101,10 +100,6 @@ void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt) {
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
const size_t reg_index = A32::RegNumber(reg);
do_get(ext_reg_singles_info[reg_index], inst);
ext_reg_doubles_info[reg_index / 2] = {};
ext_reg_vector_double_info[reg_index / 2] = {};
ext_reg_vector_quad_info[reg_index / 4] = {};
break;
}
case IR::Opcode::A32SetExtendedRegister64: {
@ -122,11 +117,6 @@ void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt) {
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
const size_t reg_index = A32::RegNumber(reg);
do_get(ext_reg_doubles_info[reg_index], inst);
ext_reg_singles_info[reg_index * 2 + 0] = {};
ext_reg_singles_info[reg_index * 2 + 1] = {};
ext_reg_vector_double_info[reg_index] = {};
ext_reg_vector_quad_info[reg_index / 2] = {};
break;
}
case IR::Opcode::A32SetVector: {
@ -160,100 +150,123 @@ void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt) {
const size_t reg_index = A32::RegNumber(reg);
if (A32::IsDoubleExtReg(reg)) {
do_get(ext_reg_vector_double_info[reg_index], inst);
ext_reg_singles_info[reg_index * 2 + 0] = {};
ext_reg_singles_info[reg_index * 2 + 1] = {};
ext_reg_doubles_info[reg_index] = {};
ext_reg_vector_quad_info[reg_index / 2] = {};
} else {
DEBUG_ASSERT(A32::IsQuadExtReg(reg));
do_get(ext_reg_vector_quad_info[reg_index], inst);
ext_reg_singles_info[reg_index * 4 + 0] = {};
ext_reg_singles_info[reg_index * 4 + 1] = {};
ext_reg_singles_info[reg_index * 4 + 2] = {};
ext_reg_singles_info[reg_index * 4 + 3] = {};
ext_reg_doubles_info[reg_index * 2 + 0] = {};
ext_reg_doubles_info[reg_index * 2 + 1] = {};
ext_reg_vector_double_info[reg_index * 2 + 0] = {};
ext_reg_vector_double_info[reg_index * 2 + 1] = {};
}
break;
}
case IR::Opcode::A32GetCFlag: {
if (cpsr_info.c.register_value.IsEmpty() && cpsr_info.nzcv.register_value.GetType() == IR::Type::NZCVFlags) {
ir.SetInsertionPointBefore(inst);
IR::U1 c = ir.GetCFlagFromNZCV(IR::NZCV{cpsr_info.nzcv.register_value});
inst->ReplaceUsesWith(c);
cpsr_info.c.register_value = c;
do_get(c_flag, inst);
break;
}
case IR::Opcode::A32SetCpsrNZCV: {
if (c_flag.has_value_request) {
ir.SetInsertionPointBefore(inst.base()); // base is one ahead
IR::U1 c = ir.GetCFlagFromNZCV(IR::NZCV{inst->GetArg(0)});
c_flag.value_request->ReplaceUsesWith(c);
c_flag.has_value_request = false;
break; // This case will be executed again because of the above
}
do_get(cpsr_info.c, inst);
// ensure source is not deleted
cpsr_info.nzc = {};
cpsr_info.nzcv = {};
do_set_valueless(nzcv, inst);
nz = {.set_not_required = true};
c_flag = {.set_not_required = true};
break;
}
case IR::Opcode::A32SetCpsrNZCV:
case IR::Opcode::A32SetCpsrNZCVRaw: {
do_delete_last_set(cpsr_info.nz);
do_delete_last_set(cpsr_info.c);
do_delete_last_set(cpsr_info.nzc);
do_set(cpsr_info.nzcv, inst->GetArg(0), inst);
if (c_flag.has_value_request) {
nzcv.set_not_required = false;
}
do_set_valueless(nzcv, inst);
nzcvq = {};
nz = {.set_not_required = true};
c_flag = {.set_not_required = true};
break;
}
case IR::Opcode::A32SetCpsrNZCVQ: {
do_delete_last_set(cpsr_info.nz);
do_delete_last_set(cpsr_info.c);
do_delete_last_set(cpsr_info.nzc);
do_delete_last_set(cpsr_info.nzcv);
if (c_flag.has_value_request) {
nzcvq.set_not_required = false;
}
do_set_valueless(nzcvq, inst);
nzcv = {.set_not_required = true};
nz = {.set_not_required = true};
c_flag = {.set_not_required = true};
break;
}
case IR::Opcode::A32SetCpsrNZ: {
if (cpsr_info.nzc.set_instruction_present) {
cpsr_info.nzc.last_set_instruction->SetArg(0, IR::Value::EmptyNZCVImmediateMarker());
}
do_set_valueless(nz, inst);
if (opt.convert_nz_to_nzc && !cpsr_info.c.register_value.IsEmpty()) {
ir.SetInsertionPointAfter(inst);
ir.SetCpsrNZC(IR::NZCV{inst->GetArg(0)}, ir.GetCFlag());
inst->Invalidate();
break;
}
// cpsr_info.c remains valid
cpsr_info.nzc = {};
cpsr_info.nzcv = {};
do_set(cpsr_info.nz, inst->GetArg(0), inst);
nzcvq = {};
nzcv = {};
break;
}
case IR::Opcode::A32SetCpsrNZC: {
if (opt.convert_nzc_to_nz && !inst->GetArg(1).IsImmediate() && inst->GetArg(1).GetInstRecursive()->GetOpcode() == IR::Opcode::A32GetCFlag) {
ir.SetInsertionPointAfter(inst);
ir.SetCpsrNZ(IR::NZCV{inst->GetArg(0)});
if (c_flag.has_value_request) {
c_flag.value_request->ReplaceUsesWith(inst->GetArg(1));
c_flag.has_value_request = false;
}
if (!inst->GetArg(1).IsImmediate() && inst->GetArg(1).GetInstRecursive()->GetOpcode() == IR::Opcode::A32GetCFlag) {
const auto nz_value = inst->GetArg(0);
inst->Invalidate();
ir.SetInsertionPointBefore(inst.base());
ir.SetCpsrNZ(IR::NZCV{nz_value});
nzcvq = {};
nzcv = {};
nz = {.set_not_required = true};
break;
}
cpsr_info.nzcv = {};
do_set(cpsr_info.nzc, {}, inst);
do_set_without_inst(cpsr_info.nz, inst->GetArg(0));
do_set_without_inst(cpsr_info.c, inst->GetArg(1));
if (nz.set_not_required && c_flag.set_not_required) {
inst->Invalidate();
} else if (nz.set_not_required) {
inst->SetArg(0, IR::Value::EmptyNZCVImmediateMarker());
}
nz.set_not_required = true;
c_flag.set_not_required = true;
nzcv = {};
nzcvq = {};
break;
}
case IR::Opcode::A32SetGEFlags: {
do_set(cpsr_info.ge, inst->GetArg(0), inst);
do_set(ge, inst->GetArg(0), inst);
break;
}
case IR::Opcode::A32GetGEFlags: {
do_get(cpsr_info.ge, inst);
do_get(ge, inst);
break;
}
case IR::Opcode::A32SetGEFlagsCompressed: {
ge = {.set_not_required = true};
break;
}
case IR::Opcode::A32OrQFlag: {
break;
}
default: {
if (inst->ReadsFromCPSR() || inst->WritesToCPSR()) {
cpsr_info = {};
nzcvq = {};
nzcv = {};
nz = {};
c_flag = {};
ge = {};
}
if (inst->ReadsFromCoreRegister() || inst->WritesToCoreRegister()) {
reg_info = {};
ext_reg_singles_info = {};
ext_reg_doubles_info = {};
ext_reg_vector_double_info = {};
ext_reg_vector_quad_info = {};
}
break;
}