#pragma once using IMLRegID = uint16; // 16 bit ID using IMLPhysReg = sint32; // arbitrary value that is up to the architecture backend, usually this will be the register index. A value of -1 is reserved and means not assigned // format of IMLReg: // 0-15 (16 bit) IMLRegID // 19-23 (5 bit) Offset In elements, for SIMD registers // 24-27 (4 bit) IMLRegFormat RegFormat // 28-31 (4 bit) IMLRegFormat BaseFormat enum class IMLRegFormat : uint8 { INVALID_FORMAT, I64, I32, I16, I8, // I1 ? F64, F32, TYPE_COUNT, }; class IMLReg { public: IMLReg() { m_raw = 0; // 0 is invalid } IMLReg(IMLRegFormat baseRegFormat, IMLRegFormat regFormat, uint8 viewOffset, IMLRegID regId) { m_raw = 0; m_raw |= ((uint8)baseRegFormat << 28); m_raw |= ((uint8)regFormat << 24); m_raw |= (uint32)regId; } IMLReg(IMLReg&& baseReg, IMLRegFormat viewFormat, uint8 viewOffset, IMLRegID regId) { DEBUG_BREAK; //m_raw = 0; //m_raw |= ((uint8)baseRegFormat << 28); //m_raw |= ((uint8)viewFormat << 24); //m_raw |= (uint32)regId; } IMLReg(const IMLReg& other) : m_raw(other.m_raw) {} IMLRegFormat GetBaseFormat() const { return (IMLRegFormat)((m_raw >> 28) & 0xF); } IMLRegFormat GetRegFormat() const { return (IMLRegFormat)((m_raw >> 24) & 0xF); } IMLRegID GetRegID() const { cemu_assert_debug(GetBaseFormat() != IMLRegFormat::INVALID_FORMAT); cemu_assert_debug(GetRegFormat() != IMLRegFormat::INVALID_FORMAT); return (IMLRegID)(m_raw & 0xFFFF); } void SetRegID(IMLRegID regId) { cemu_assert_debug(regId <= 0xFFFF); m_raw &= ~0xFFFF; m_raw |= (uint32)regId; } bool IsInvalid() const { return GetBaseFormat() == IMLRegFormat::INVALID_FORMAT; } bool IsValid() const { return GetBaseFormat() != IMLRegFormat::INVALID_FORMAT; } bool IsValidAndSameRegID(IMLRegID regId) const { return IsValid() && GetRegID() == regId; } // compare all fields bool operator==(const IMLReg& other) const { return m_raw == other.m_raw; } private: uint32 m_raw; }; static const IMLReg IMLREG_INVALID(IMLRegFormat::INVALID_FORMAT, IMLRegFormat::INVALID_FORMAT, 0, 0); static const IMLRegID IMLRegID_INVALID(0xFFFF); using IMLName = uint32; enum { PPCREC_IML_OP_ASSIGN, // '=' operator PPCREC_IML_OP_ENDIAN_SWAP, // '=' operator with 32bit endian swap PPCREC_IML_OP_MULTIPLY_SIGNED, // '*' operator (signed multiply) PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, // unsigned 64bit multiply, store only high 32bit-word of result PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, // signed 64bit multiply, store only high 32bit-word of result PPCREC_IML_OP_DIVIDE_SIGNED, // '/' operator (signed divide) PPCREC_IML_OP_DIVIDE_UNSIGNED, // '/' operator (unsigned divide) // binary operation PPCREC_IML_OP_OR, // '|' operator PPCREC_IML_OP_AND, // '&' operator PPCREC_IML_OP_XOR, // '^' operator PPCREC_IML_OP_LEFT_ROTATE, // left rotate operator PPCREC_IML_OP_LEFT_SHIFT, // shift left operator PPCREC_IML_OP_RIGHT_SHIFT_U, // right shift operator (unsigned) PPCREC_IML_OP_RIGHT_SHIFT_S, // right shift operator (signed) // ppc PPCREC_IML_OP_RLWIMI, // RLWIMI instruction (rotate, merge based on mask) PPCREC_IML_OP_SLW, // SLW (shift based on register by up to 63 bits) PPCREC_IML_OP_SRW, // SRW (shift based on register by up to 63 bits) PPCREC_IML_OP_CNTLZW, PPCREC_IML_OP_DCBZ, // clear 32 bytes aligned to 0x20 // FPU PPCREC_IML_OP_FPR_ADD_BOTTOM, PPCREC_IML_OP_FPR_ADD_PAIR, PPCREC_IML_OP_FPR_SUB_PAIR, PPCREC_IML_OP_FPR_SUB_BOTTOM, PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM, PPCREC_IML_OP_FPR_MULTIPLY_PAIR, PPCREC_IML_OP_FPR_DIVIDE_BOTTOM, PPCREC_IML_OP_FPR_DIVIDE_PAIR, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP, PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM, PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_TOP, // leave bottom of destination untouched PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP, // leave bottom of destination untouched PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM, // leave top of destination untouched PPCREC_IML_OP_FPR_COPY_BOTTOM_AND_TOP_SWAPPED, PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64, // expand bottom f32 to f64 in bottom and top half PPCREC_IML_OP_FPR_BOTTOM_FRES_TO_BOTTOM_AND_TOP, // calculate reciprocal with Espresso accuracy of source bottom half and write result to destination bottom and top half PPCREC_IML_OP_FPR_FCMPO_BOTTOM, // deprecated PPCREC_IML_OP_FPR_FCMPU_BOTTOM, // deprecated PPCREC_IML_OP_FPR_FCMPU_TOP, // deprecated PPCREC_IML_OP_FPR_NEGATE_BOTTOM, PPCREC_IML_OP_FPR_NEGATE_PAIR, PPCREC_IML_OP_FPR_ABS_BOTTOM, // abs(fp0) PPCREC_IML_OP_FPR_ABS_PAIR, PPCREC_IML_OP_FPR_FRES_PAIR, // 1.0/fp approx (Espresso accuracy) PPCREC_IML_OP_FPR_FRSQRTE_PAIR, // 1.0/sqrt(fp) approx (Espresso accuracy) PPCREC_IML_OP_FPR_NEGATIVE_ABS_BOTTOM, // -abs(fp0) PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM, // round 64bit double to 64bit double with 32bit float precision (in bottom half of xmm register) PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR, // round two 64bit doubles to 64bit double with 32bit float precision PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT, PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ, PPCREC_IML_OP_FPR_SELECT_BOTTOM, // selectively copy bottom value from operand B or C based on value in operand A PPCREC_IML_OP_FPR_SELECT_PAIR, // selectively copy top/bottom from operand B or C based on value in top/bottom of operand A // PS PPCREC_IML_OP_FPR_SUM0, PPCREC_IML_OP_FPR_SUM1, // R_R_R only // R_R_S32 only // R_R_R + R_R_S32 PPCREC_IML_OP_ADD, // also R_R_R_CARRY PPCREC_IML_OP_SUB, // R_R only PPCREC_IML_OP_NOT, PPCREC_IML_OP_NEG, PPCREC_IML_OP_ASSIGN_S16_TO_S32, PPCREC_IML_OP_ASSIGN_S8_TO_S32, // R_R_R_carry PPCREC_IML_OP_ADD_WITH_CARRY, // similar to ADD but also adds carry bit (0 or 1) // X86 extension PPCREC_IML_OP_X86_CMP, // R_R and R_S32 PPCREC_IML_OP_INVALID }; #define PPCREC_IML_OP_FPR_COPY_PAIR (PPCREC_IML_OP_ASSIGN) enum { PPCREC_IML_MACRO_B_TO_REG, // branch to PPC address in register (used for BCCTR, BCLR) PPCREC_IML_MACRO_BL, // call to different function (can be within same function) PPCREC_IML_MACRO_B_FAR, // branch to different function PPCREC_IML_MACRO_COUNT_CYCLES, // decrease current remaining thread cycles by a certain amount PPCREC_IML_MACRO_HLE, // HLE function call PPCREC_IML_MACRO_MFTB, // get TB register value (low or high) PPCREC_IML_MACRO_LEAVE, // leaves recompiler and switches to interpeter // debugging PPCREC_IML_MACRO_DEBUGBREAK, // throws a debugbreak }; enum class IMLCondition : uint8 { EQ, NEQ, SIGNED_GT, SIGNED_LT, UNSIGNED_GT, UNSIGNED_LT, // floating point conditions UNORDERED_GT, // a > b, false if either is NaN UNORDERED_LT, // a < b, false if either is NaN UNORDERED_EQ, // a == b, false if either is NaN UNORDERED_U, // unordered (true if either operand is NaN) ORDERED_GT, ORDERED_LT, ORDERED_EQ, ORDERED_U }; enum { PPCREC_IML_TYPE_NONE, PPCREC_IML_TYPE_NO_OP, // no-op instruction PPCREC_IML_TYPE_R_R, // r* = (op) *r (can also be r* (op) *r) PPCREC_IML_TYPE_R_R_R, // r* = r* (op) r* PPCREC_IML_TYPE_R_R_R_CARRY, // r* = r* (op) r* (reads and/or updates carry) PPCREC_IML_TYPE_R_R_S32, // r* = r* (op) s32* PPCREC_IML_TYPE_R_R_S32_CARRY, // r* = r* (op) s32* (reads and/or updates carry) PPCREC_IML_TYPE_LOAD, // r* = [r*+s32*] PPCREC_IML_TYPE_LOAD_INDEXED, // r* = [r*+r*] PPCREC_IML_TYPE_STORE, // [r*+s32*] = r* PPCREC_IML_TYPE_STORE_INDEXED, // [r*+r*] = r* PPCREC_IML_TYPE_R_NAME, // r* = name PPCREC_IML_TYPE_NAME_R, // name* = r* PPCREC_IML_TYPE_R_S32, // r* (op) imm PPCREC_IML_TYPE_MACRO, PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK, // jumps only if remaining thread cycles < 0 // conditions and branches PPCREC_IML_TYPE_COMPARE, // r* = r* CMP[cond] r* PPCREC_IML_TYPE_COMPARE_S32, // r* = r* CMP[cond] imm PPCREC_IML_TYPE_JUMP, // jump always PPCREC_IML_TYPE_CONDITIONAL_JUMP, // jump conditionally based on boolean value in register // atomic PPCREC_IML_TYPE_ATOMIC_CMP_STORE, // conditional (legacy) PPCREC_IML_TYPE_CONDITIONAL_R_S32, // function call PPCREC_IML_TYPE_CALL_IMM, // call to fixed immediate address // FPR PPCREC_IML_TYPE_FPR_LOAD, // r* = (bitdepth) [r*+s32*] (single or paired single mode) PPCREC_IML_TYPE_FPR_LOAD_INDEXED, // r* = (bitdepth) [r*+r*] (single or paired single mode) PPCREC_IML_TYPE_FPR_STORE, // (bitdepth) [r*+s32*] = r* (single or paired single mode) PPCREC_IML_TYPE_FPR_STORE_INDEXED, // (bitdepth) [r*+r*] = r* (single or paired single mode) PPCREC_IML_TYPE_FPR_R_R, PPCREC_IML_TYPE_FPR_R_R_R, PPCREC_IML_TYPE_FPR_R_R_R_R, PPCREC_IML_TYPE_FPR_R, PPCREC_IML_TYPE_FPR_COMPARE, // r* = r* CMP[cond] r* // X86 specific PPCREC_IML_TYPE_X86_EFLAGS_JCC, }; enum // IMLName { PPCREC_NAME_NONE, PPCREC_NAME_TEMPORARY = 1000, PPCREC_NAME_R0 = 2000, PPCREC_NAME_SPR0 = 3000, PPCREC_NAME_FPR0 = 4000, PPCREC_NAME_TEMPORARY_FPR0 = 5000, // 0 to 7 PPCREC_NAME_XER_CA = 6000, // carry bit from XER PPCREC_NAME_XER_OV = 6001, // overflow bit from XER PPCREC_NAME_XER_SO = 6002, // summary overflow bit from XER PPCREC_NAME_CR = 7000, // CR register bits (31 to 0) PPCREC_NAME_CR_LAST = PPCREC_NAME_CR+31, PPCREC_NAME_CPU_MEMRES_EA = 8000, PPCREC_NAME_CPU_MEMRES_VAL = 8001 }; #define PPC_REC_INVALID_REGISTER 0xFF // deprecated. Use IMLREG_INVALID instead enum { // fpr load PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0, PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1, PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0, PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0, PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1, PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0, PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1, PPCREC_FPR_LD_MODE_PSQ_S16_PS0, PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1, PPCREC_FPR_LD_MODE_PSQ_U16_PS0, PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1, PPCREC_FPR_LD_MODE_PSQ_S8_PS0, PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1, PPCREC_FPR_LD_MODE_PSQ_U8_PS0, PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1, // fpr store PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, // store 1 single precision float from ps0 PPCREC_FPR_ST_MODE_DOUBLE_FROM_PS0, // store 1 double precision float from ps0 PPCREC_FPR_ST_MODE_UI32_FROM_PS0, // store raw low-32bit of PS0 PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1, PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0, PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1, PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0, PPCREC_FPR_ST_MODE_PSQ_S8_PS0, PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1, PPCREC_FPR_ST_MODE_PSQ_U8_PS0, PPCREC_FPR_ST_MODE_PSQ_U8_PS0_PS1, PPCREC_FPR_ST_MODE_PSQ_U16_PS0, PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1, PPCREC_FPR_ST_MODE_PSQ_S16_PS0, PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1, }; struct IMLUsedRegisters { IMLUsedRegisters() {}; // GPR union { struct { IMLReg readGPR1; IMLReg readGPR2; IMLReg readGPR3; IMLReg writtenGPR1; IMLReg writtenGPR2; }; }; // FPR union { struct { // note: If destination operand is not fully written (PS0 and PS1) it will be added to the read registers IMLReg readFPR1; IMLReg readFPR2; IMLReg readFPR3; IMLReg readFPR4; IMLReg writtenFPR1; }; }; bool IsWrittenByRegId(IMLRegID regId) const { if (writtenGPR1.IsValid() && writtenGPR1.GetRegID() == regId) return true; if (writtenGPR2.IsValid() && writtenGPR2.GetRegID() == regId) return true; return false; } bool IsBaseGPRWritten(IMLReg imlReg) const { cemu_assert_debug(imlReg.IsValid()); auto regId = imlReg.GetRegID(); return IsWrittenByRegId(regId); } bool IsRegIdRead(IMLRegID regId) const { if (readGPR1.IsValid() && readGPR1.GetRegID() == regId) return true; if (readGPR2.IsValid() && readGPR2.GetRegID() == regId) return true; if (readGPR3.IsValid() && readGPR3.GetRegID() == regId) return true; return false; } template void ForEachWrittenGPR(Fn F) const { if (writtenGPR1.IsValid()) F(writtenGPR1); if (writtenGPR2.IsValid()) F(writtenGPR2); } template void ForEachReadGPR(Fn F) const { if (readGPR1.IsValid()) F(readGPR1); if (readGPR2.IsValid()) F(readGPR2); if (readGPR3.IsValid()) F(readGPR3); } template void ForEachAccessedGPR(Fn F) const { // GPRs if (readGPR1.IsValid()) F(readGPR1, false); if (readGPR2.IsValid()) F(readGPR2, false); if (readGPR3.IsValid()) F(readGPR3, false); if (writtenGPR1.IsValid()) F(writtenGPR1, true); if (writtenGPR2.IsValid()) F(writtenGPR2, true); // FPRs if (readFPR1.IsValid()) F(readFPR1, false); if (readFPR2.IsValid()) F(readFPR2, false); if (readFPR3.IsValid()) F(readFPR3, false); if (readFPR4.IsValid()) F(readFPR4, false); if (writtenFPR1.IsValid()) F(writtenFPR1, true); } }; struct IMLInstruction { IMLInstruction() {} IMLInstruction(const IMLInstruction& other) { memcpy(this, &other, sizeof(IMLInstruction)); } uint8 type; uint8 operation; union { struct { uint8 _padding[7]; }padding; struct { IMLReg regR; IMLReg regA; }op_r_r; struct { IMLReg regR; IMLReg regA; IMLReg regB; }op_r_r_r; struct { IMLReg regR; IMLReg regA; IMLReg regB; IMLReg regCarry; }op_r_r_r_carry; struct { IMLReg regR; IMLReg regA; sint32 immS32; }op_r_r_s32; struct { IMLReg regR; IMLReg regA; IMLReg regCarry; sint32 immS32; }op_r_r_s32_carry; struct { IMLReg regR; IMLName name; }op_r_name; // alias op_name_r struct { IMLReg regR; sint32 immS32; }op_r_immS32; struct { uint32 param; uint32 param2; uint16 paramU16; IMLReg paramReg; }op_macro; struct { IMLReg registerData; IMLReg registerMem; IMLReg registerMem2; IMLReg registerGQR; uint8 copyWidth; struct { bool swapEndian : 1; bool signExtend : 1; bool notExpanded : 1; // for floats }flags2; uint8 mode; // transfer mode (copy width, ps0/ps1 behavior) sint32 immS32; }op_storeLoad; struct { uintptr_t callAddress; IMLReg regParam0; IMLReg regParam1; IMLReg regParam2; IMLReg regReturn; }op_call_imm; struct { IMLReg regR; IMLReg regA; }op_fpr_r_r; struct { IMLReg regR; IMLReg regA; IMLReg regB; }op_fpr_r_r_r; struct { IMLReg regR; IMLReg regA; IMLReg regB; IMLReg regC; }op_fpr_r_r_r_r; struct { IMLReg regR; }op_fpr_r; struct { IMLReg regR; // stores the boolean result of the comparison IMLReg regA; IMLReg regB; IMLCondition cond; }op_fpr_compare; struct { IMLReg regR; // stores the boolean result of the comparison IMLReg regA; IMLReg regB; IMLCondition cond; }op_compare; struct { IMLReg regR; // stores the boolean result of the comparison IMLReg regA; sint32 immS32; IMLCondition cond; }op_compare_s32; struct { IMLReg registerBool; bool mustBeTrue; }op_conditional_jump; struct { IMLReg regEA; IMLReg regCompareValue; IMLReg regWriteValue; IMLReg regBoolOut; }op_atomic_compare_store; // conditional operations (emitted if supported by target platform) struct { // r_s32 IMLReg regR; sint32 immS32; // condition uint8 crRegisterIndex; uint8 crBitIndex; bool bitMustBeSet; }op_conditional_r_s32; // X86 specific struct { IMLCondition cond; bool invertedCondition; }op_x86_eflags_jcc; }; bool IsSuffixInstruction() const { if (type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_BL || type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_B_FAR || type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_B_TO_REG || type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_LEAVE || type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_HLE || type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_MFTB || type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK || type == PPCREC_IML_TYPE_JUMP || type == PPCREC_IML_TYPE_CONDITIONAL_JUMP || type == PPCREC_IML_TYPE_X86_EFLAGS_JCC) return true; return false; } // instruction setters void make_no_op() { type = PPCREC_IML_TYPE_NO_OP; operation = 0; } void make_r_name(IMLReg regR, IMLName name) { cemu_assert_debug(regR.GetBaseFormat() == regR.GetRegFormat()); // for name load/store instructions the register must match the base format type = PPCREC_IML_TYPE_R_NAME; operation = PPCREC_IML_OP_ASSIGN; op_r_name.regR = regR; op_r_name.name = name; } void make_name_r(IMLName name, IMLReg regR) { cemu_assert_debug(regR.GetBaseFormat() == regR.GetRegFormat()); // for name load/store instructions the register must match the base format type = PPCREC_IML_TYPE_NAME_R; operation = PPCREC_IML_OP_ASSIGN; op_r_name.regR = regR; op_r_name.name = name; } void make_debugbreak(uint32 currentPPCAddress = 0) { make_macro(PPCREC_IML_MACRO_DEBUGBREAK, 0, currentPPCAddress, 0, IMLREG_INVALID); } void make_macro(uint32 macroId, uint32 param, uint32 param2, uint16 paramU16, IMLReg regParam) { this->type = PPCREC_IML_TYPE_MACRO; this->operation = macroId; this->op_macro.param = param; this->op_macro.param2 = param2; this->op_macro.paramU16 = paramU16; this->op_macro.paramReg = regParam; } void make_cjump_cycle_check() { this->type = PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK; this->operation = 0; } void make_r_r(uint32 operation, IMLReg regR, IMLReg regA) { this->type = PPCREC_IML_TYPE_R_R; this->operation = operation; this->op_r_r.regR = regR; this->op_r_r.regA = regA; } void make_r_s32(uint32 operation, IMLReg regR, sint32 immS32) { this->type = PPCREC_IML_TYPE_R_S32; this->operation = operation; this->op_r_immS32.regR = regR; this->op_r_immS32.immS32 = immS32; } void make_r_r_r(uint32 operation, IMLReg regR, IMLReg regA, IMLReg regB) { this->type = PPCREC_IML_TYPE_R_R_R; this->operation = operation; this->op_r_r_r.regR = regR; this->op_r_r_r.regA = regA; this->op_r_r_r.regB = regB; } void make_r_r_r_carry(uint32 operation, IMLReg regR, IMLReg regA, IMLReg regB, IMLReg regCarry) { this->type = PPCREC_IML_TYPE_R_R_R_CARRY; this->operation = operation; this->op_r_r_r_carry.regR = regR; this->op_r_r_r_carry.regA = regA; this->op_r_r_r_carry.regB = regB; this->op_r_r_r_carry.regCarry = regCarry; } void make_r_r_s32(uint32 operation, IMLReg regR, IMLReg regA, sint32 immS32) { this->type = PPCREC_IML_TYPE_R_R_S32; this->operation = operation; this->op_r_r_s32.regR = regR; this->op_r_r_s32.regA = regA; this->op_r_r_s32.immS32 = immS32; } void make_r_r_s32_carry(uint32 operation, IMLReg regR, IMLReg regA, sint32 immS32, IMLReg regCarry) { this->type = PPCREC_IML_TYPE_R_R_S32_CARRY; this->operation = operation; this->op_r_r_s32_carry.regR = regR; this->op_r_r_s32_carry.regA = regA; this->op_r_r_s32_carry.immS32 = immS32; this->op_r_r_s32_carry.regCarry = regCarry; } void make_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond) { this->type = PPCREC_IML_TYPE_COMPARE; this->operation = PPCREC_IML_OP_INVALID; this->op_compare.regR = regR; this->op_compare.regA = regA; this->op_compare.regB = regB; this->op_compare.cond = cond; } void make_compare_s32(IMLReg regA, sint32 immS32, IMLReg regR, IMLCondition cond) { this->type = PPCREC_IML_TYPE_COMPARE_S32; this->operation = PPCREC_IML_OP_INVALID; this->op_compare_s32.regR = regR; this->op_compare_s32.regA = regA; this->op_compare_s32.immS32 = immS32; this->op_compare_s32.cond = cond; } void make_conditional_jump(IMLReg regBool, bool mustBeTrue) { this->type = PPCREC_IML_TYPE_CONDITIONAL_JUMP; this->operation = PPCREC_IML_OP_INVALID; this->op_conditional_jump.registerBool = regBool; this->op_conditional_jump.mustBeTrue = mustBeTrue; } void make_jump() { this->type = PPCREC_IML_TYPE_JUMP; this->operation = PPCREC_IML_OP_INVALID; } // load from memory void make_r_memory(IMLReg regD, IMLReg regMem, sint32 immS32, uint32 copyWidth, bool signExtend, bool switchEndian) { this->type = PPCREC_IML_TYPE_LOAD; this->operation = 0; this->op_storeLoad.registerData = regD; this->op_storeLoad.registerMem = regMem; this->op_storeLoad.immS32 = immS32; this->op_storeLoad.copyWidth = copyWidth; this->op_storeLoad.flags2.swapEndian = switchEndian; this->op_storeLoad.flags2.signExtend = signExtend; } // store to memory void make_memory_r(IMLReg regS, IMLReg regMem, sint32 immS32, uint32 copyWidth, bool switchEndian) { this->type = PPCREC_IML_TYPE_STORE; this->operation = 0; this->op_storeLoad.registerData = regS; this->op_storeLoad.registerMem = regMem; this->op_storeLoad.immS32 = immS32; this->op_storeLoad.copyWidth = copyWidth; this->op_storeLoad.flags2.swapEndian = switchEndian; this->op_storeLoad.flags2.signExtend = false; } void make_atomic_cmp_store(IMLReg regEA, IMLReg regCompareValue, IMLReg regWriteValue, IMLReg regSuccessOutput) { this->type = PPCREC_IML_TYPE_ATOMIC_CMP_STORE; this->operation = 0; this->op_atomic_compare_store.regEA = regEA; this->op_atomic_compare_store.regCompareValue = regCompareValue; this->op_atomic_compare_store.regWriteValue = regWriteValue; this->op_atomic_compare_store.regBoolOut = regSuccessOutput; } void make_call_imm(uintptr_t callAddress, IMLReg param0, IMLReg param1, IMLReg param2, IMLReg regReturn) { this->type = PPCREC_IML_TYPE_CALL_IMM; this->operation = 0; this->op_call_imm.callAddress = callAddress; this->op_call_imm.regParam0 = param0; this->op_call_imm.regParam1 = param1; this->op_call_imm.regParam2 = param2; this->op_call_imm.regReturn = regReturn; } void make_fpr_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond) { this->type = PPCREC_IML_TYPE_FPR_COMPARE; this->operation = -999; this->op_fpr_compare.regR = regR; this->op_fpr_compare.regA = regA; this->op_fpr_compare.regB = regB; this->op_fpr_compare.cond = cond; } /* X86 specific */ void make_x86_eflags_jcc(IMLCondition cond, bool invertedCondition) { this->type = PPCREC_IML_TYPE_X86_EFLAGS_JCC; this->operation = -999; this->op_x86_eflags_jcc.cond = cond; this->op_x86_eflags_jcc.invertedCondition = invertedCondition; } void CheckRegisterUsage(IMLUsedRegisters* registersUsed) const; void RewriteGPR(const std::unordered_map& translationTable); void ReplaceFPRs(IMLReg fprRegisterSearched[4], IMLReg fprRegisterReplaced[4]); void ReplaceFPR(IMLRegID fprRegisterSearched, IMLRegID fprRegisterReplaced); }; // architecture specific constants namespace IMLArchX86 { static constexpr int PHYSREG_GPR_BASE = 0; static constexpr int PHYSREG_FPR_BASE = 16; };