Initial implementation of unary operation table

This commit is contained in:
Mr-Wiseguy 2024-05-23 00:51:33 -04:00
parent 347a792f80
commit 81f95a42e4

View file

@ -31,6 +31,7 @@ enum class UnaryOpType {
Lui,
Mask5, // Mask to 5 bits
Mask6, // Mask to 5 bits
ToInt32, // Functionally equivalent to ToS32, only exists for parity with old codegen
};
enum class BinaryOpType {
@ -78,6 +79,8 @@ enum class Operand {
ImmS16, // 16-bit immediate, signed
Sa, // Shift amount
Sa32, // Shift amount plus 32
Hi,
Lo,
Base = Rs, // Alias for Rs for loads
};
@ -101,6 +104,10 @@ struct BinaryOp {
const std::unordered_map<InstrId, UnaryOp> unary_ops {
{ InstrId::cpu_lui, { UnaryOpType::Lui, Operand::Rt, Operand::ImmU16 } },
{ InstrId::cpu_mthi, { UnaryOpType::None, Operand::Hi, Operand::Rs } },
{ InstrId::cpu_mtlo, { UnaryOpType::None, Operand::Lo, Operand::Rs } },
{ InstrId::cpu_mfhi, { UnaryOpType::None, Operand::Rd, Operand::Hi } },
{ InstrId::cpu_mflo, { UnaryOpType::None, Operand::Rd, Operand::Lo } },
};
const std::unordered_map<InstrId, BinaryOp> binary_ops {
@ -188,6 +195,7 @@ class CGenerator {
public:
CGenerator() = default;
void process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx);
void process_unary_op(std::ostream& output_file, const UnaryOp& op, const InstructionContext& ctx);
private:
void get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string);
void get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string);
@ -283,6 +291,12 @@ void CGenerator::get_operand_string(Operand operand, UnaryOpType operation, cons
case Operand::Sa32:
operand_string = fmt::format("({} + 32)", context.sa);
break;
case Operand::Hi:
operand_string = "hi";
break;
case Operand::Lo:
operand_string = "lo";
break;
}
switch (operation) {
case UnaryOpType::None:
@ -306,8 +320,7 @@ void CGenerator::get_operand_string(Operand operand, UnaryOpType operation, cons
assert(false);
break;
case UnaryOpType::Lui:
assert(false);
// operand_string = "S32(" + operand_string + ")";
operand_string = "S32(" + operand_string + " << 16)";
break;
case UnaryOpType::Mask5:
operand_string = "(" + operand_string + " & 31)";
@ -315,6 +328,9 @@ void CGenerator::get_operand_string(Operand operand, UnaryOpType operation, cons
case UnaryOpType::Mask6:
operand_string = "(" + operand_string + " & 63)";
break;
case UnaryOpType::ToInt32:
operand_string = "(int32_t)" + operand_string;
break;
}
}
@ -358,6 +374,17 @@ void CGenerator::process_binary_op(std::ostream& output_file, const BinaryOp& op
}
}
void CGenerator::process_unary_op(std::ostream& output_file, const UnaryOp& op, const InstructionContext& ctx) {
// Thread local variables to prevent allocations when possible.
// TODO these thread locals probably don't actually help right now, so figure out a better way to prevent allocations.
thread_local std::string output{};
thread_local std::string input{};
bool is_infix;
get_operand_string(op.output, UnaryOpType::None, ctx, output);
get_operand_string(op.input, op.operation, ctx, input);
fmt::print(output_file, "{} = {};\n", output, input);
}
// Major TODO, this function grew very organically and needs to be cleaned up. Ideally, it'll get split up into some sort of lookup table grouped by similar instruction types.
bool process_instruction(const RecompPort::Context& context, const RecompPort::Config& config, const RecompPort::Function& func, const RecompPort::FunctionStats& stats, const std::unordered_set<uint32_t>& skipped_insns, size_t instr_index, const std::vector<rabbitizer::InstructionCpu>& instructions, std::ofstream& output_file, bool indent, bool emit_link_branch, int link_branch_index, size_t reloc_index, bool& needs_link_branch, bool& is_branch_likely, std::span<std::vector<uint32_t>> static_funcs_out) {
const auto& section = context.sections[func.section_index];
@ -694,9 +721,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
break;
}
// Arithmetic
case InstrId::cpu_lui:
print_line("{}{} = S32({} << 16)", ctx_gpr_prefix(rt), rt, unsigned_imm_string);
break;
case InstrId::cpu_add:
case InstrId::cpu_addu:
{
@ -737,18 +761,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
case InstrId::cpu_ddivu:
print_line("DDIVU(U64({}{}), U64({}{}), &lo, &hi)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_mflo:
print_line("{}{} = lo", ctx_gpr_prefix(rd), rd);
break;
case InstrId::cpu_mfhi:
print_line("{}{} = hi", ctx_gpr_prefix(rd), rd);
break;
case InstrId::cpu_mtlo:
print_line("lo = {}{}", ctx_gpr_prefix(rs), rs);
break;
case InstrId::cpu_mthi:
print_line("hi = {}{}", ctx_gpr_prefix(rs), rs);
break;
// Stores
case InstrId::cpu_sd:
print_line("SD({}{}, {}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
@ -1367,9 +1379,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
break;
}
auto find_it = binary_ops.find(instr.getUniqueId());
if (find_it != binary_ops.end()) {
CGenerator generator{};
InstructionContext instruction_context{};
instruction_context.rd = rd;
instruction_context.rs = rs;
@ -1380,8 +1389,20 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
instruction_context.ft = ft;
instruction_context.cop1_cs = cop1_cs;
instruction_context.imm16 = imm;
auto find_binary_it = binary_ops.find(instr.getUniqueId());
if (find_binary_it != binary_ops.end()) {
CGenerator generator{};
print_indent();
generator.process_binary_op(output_file, find_it->second, instruction_context);
generator.process_binary_op(output_file, find_binary_it->second, instruction_context);
handled = true;
}
auto find_unary_it = unary_ops.find(instr.getUniqueId());
if (find_unary_it != unary_ops.end()) {
CGenerator generator{};
print_indent();
generator.process_unary_op(output_file, find_unary_it->second, instruction_context);
handled = true;
}