diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt
index 61bf4de6b0..526a1947ea 100644
--- a/Core/CMakeLists.txt
+++ b/Core/CMakeLists.txt
@@ -38,6 +38,17 @@ list(APPEND CoreExtra
MIPS/ARM/ArmRegCache.h
MIPS/ARM/ArmRegCacheFPU.cpp
MIPS/ARM/ArmRegCacheFPU.h
+ MIPS/ARM/ArmIRAsm.cpp
+ MIPS/ARM/ArmIRCompALU.cpp
+ MIPS/ARM/ArmIRCompBranch.cpp
+ MIPS/ARM/ArmIRCompFPU.cpp
+ MIPS/ARM/ArmIRCompLoadStore.cpp
+ MIPS/ARM/ArmIRCompSystem.cpp
+ MIPS/ARM/ArmIRCompVec.cpp
+ MIPS/ARM/ArmIRJit.cpp
+ MIPS/ARM/ArmIRRegCache.cpp
+ MIPS/ARM/ArmIRJit.h
+ MIPS/ARM/ArmIRRegCache.h
)
list(APPEND CoreExtra
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 192b4c0ef7..46c048b232 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -742,6 +742,15 @@
true
+
+
+
+
+
+
+
+
+
true
true
@@ -1097,6 +1106,8 @@
true
+
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 57782a769b..5711fbc2a4 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -450,6 +450,33 @@
MIPS\ARM
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
Font
@@ -1731,6 +1758,12 @@
MIPS\ARM
+
+ MIPS\ARM
+
+
+ MIPS\ARM
+
Font
diff --git a/Core/MIPS/ARM/ArmIRAsm.cpp b/Core/MIPS/ARM/ArmIRAsm.cpp
new file mode 100644
index 0000000000..d8755a52bd
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRAsm.cpp
@@ -0,0 +1,223 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include "Common/Log.h"
+#include "Common/MemoryUtil.h"
+#include "Core/CoreTiming.h"
+#include "Core/MemMap.h"
+#include "Core/MIPS/ARM/ArmIRJit.h"
+#include "Core/MIPS/ARM/ArmIRRegCache.h"
+#include "Core/MIPS/JitCommon/JitCommon.h"
+#include "Core/MIPS/JitCommon/JitState.h"
+#include "Core/Core.h"
+
+std::vector DisassembleArm2(const u8 *data, int size);
+
+namespace MIPSComp {
+
+using namespace ArmGen;
+using namespace ArmIRJitConstants;
+
+static const bool enableDisasm = false;
+
+void ArmJitBackend::GenerateFixedCode(MIPSState *mipsState) {
+ // This will be used as a writable scratch area, always 32-bit accessible.
+ const u8 *start = AlignCodePage();
+ if (DebugProfilerEnabled()) {
+ ProtectMemoryPages(start, GetMemoryProtectPageSize(), MEM_PROT_READ | MEM_PROT_WRITE);
+ hooks_.profilerPC = (uint32_t *)GetWritableCodePtr();
+ Write32(0);
+ hooks_.profilerStatus = (IRProfilerStatus *)GetWritableCodePtr();
+ Write32(0);
+ }
+
+ const u8 *disasmStart = AlignCodePage();
+ BeginWrite(GetMemoryProtectPageSize());
+
+ // These are called with BL, so LR has to survive.
+ restoreRoundingMode_ = AlignCode16();
+ {
+ VMRS(SCRATCH2);
+ // Outside the JIT we run with round-to-nearest and flush-to-zero off.
+ BIC(SCRATCH2, SCRATCH2, AssumeMakeOperand2((3 | 4) << 22));
+ VMSR(SCRATCH2);
+ B(R_LR);
+ }
+
+ // Must preserve SCRATCH1, since it may hold the PC. Destroys SCRATCH2 and R12.
+ applyRoundingMode_ = AlignCode16();
+ {
+ LDR(SCRATCH2, CTXREG, offsetof(MIPSState, fcr31));
+
+ TST(SCRATCH2, AssumeMakeOperand2(1 << 24));
+ AND(SCRATCH2, SCRATCH2, Operand2(3));
+ SetCC(CC_NEQ);
+ ADD(SCRATCH2, SCRATCH2, Operand2(4));
+ SetCC(CC_AL);
+
+ // We can skip if the rounding mode is nearest (0) and flush is not set.
+ // (as restoreRoundingMode cleared it out anyway)
+ CMP(SCRATCH2, Operand2(0));
+ FixupBranch skip = B_CC(CC_EQ);
+
+ // MIPS Rounding Mode: ARM Rounding Mode
+ // 0: Round nearest 0
+ // 1: Round to zero 3
+ // 2: Round up (ceil) 1
+ // 3: Round down (floor) 2
+ AND(R12, SCRATCH2, Operand2(3));
+ CMP(R12, Operand2(1));
+
+ SetCC(CC_EQ); ADD(SCRATCH2, SCRATCH2, Operand2(2));
+ SetCC(CC_GT); SUB(SCRATCH2, SCRATCH2, Operand2(1));
+ SetCC(CC_AL);
+
+ VMRS(R12);
+ // Assume we're always in round-to-nearest mode beforehand.
+ // But we need to clear flush to zero in this case anyway.
+ BIC(R12, R12, AssumeMakeOperand2((3 | 4) << 22));
+ ORR(R12, R12, Operand2(SCRATCH2, ST_LSL, 22));
+ VMSR(R12);
+
+ SetJumpTarget(skip);
+ B(R_LR);
+ }
+
+ hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16();
+
+ // Nine registers, so we're misaligned by 4 for calls, which the SUB fixes.
+ PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, R_LR);
+ SUB(R_SP, R_SP, 4);
+ VPUSH(D8, 8);
+
+ // Fixed registers, these are always kept when in Jit context.
+ MOVP2R(MEMBASEREG, Memory::base);
+ MOVP2R(CTXREG, mipsState);
+
+ LoadStaticRegisters();
+ WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
+ MovFromPC(SCRATCH1);
+ WriteDebugPC(SCRATCH1);
+ outerLoopPCInSCRATCH1_ = GetCodePtr();
+ MovToPC(SCRATCH1);
+ outerLoop_ = GetCodePtr();
+ SaveStaticRegisters(); // Advance can change the downcount, so must save/restore
+ RestoreRoundingMode(true);
+ WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE);
+ QuickCallFunctionR(R12, &CoreTiming::Advance, CTXREG);
+ WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
+ ApplyRoundingMode(true);
+ LoadStaticRegisters();
+
+ dispatcherCheckCoreState_ = GetCodePtr();
+
+ MOVP2R(SCRATCH1, &coreState);
+ LDR(SCRATCH1, SCRATCH1, 0);
+ CMP(SCRATCH1, Operand2(0));
+ FixupBranch badCoreState = B_CC(CC_NEQ);
+
+ // Check downcount.
+ CMP(DOWNCOUNTREG, Operand2(0));
+ B_CC(CC_MI, outerLoop_);
+ FixupBranch skipToRealDispatch = B();
+
+ dispatcherPCInSCRATCH1_ = GetCodePtr();
+ MovToPC(SCRATCH1);
+
+ hooks_.dispatcher = GetCodePtr();
+
+ CMP(DOWNCOUNTREG, Operand2(0));
+ FixupBranch bail = B_CC(CC_MI);
+ SetJumpTarget(skipToRealDispatch);
+
+ dispatcherNoCheck_ = GetCodePtr();
+
+ MovFromPC(SCRATCH1);
+ WriteDebugPC(SCRATCH1);
+ static_assert(Memory::BLOCK_SHADOW_ENTRY_MASK == 0x0FFFFFFC, "BICs below assume the 32-bit shadow mask");
+ BIC(SCRATCH1, SCRATCH1, Operand2(0xF0, 4)); // &= 0x0FFFFFFF. Rotation is to the right, in 2-bit increments.
+ BIC(SCRATCH1, SCRATCH1, Operand2(3));
+ MOVP2R(SCRATCH2, Memory::blockShadow);
+ hooks_.dispatchFetch = GetCodePtr();
+ LDR(SCRATCH1, SCRATCH2, SCRATCH1);
+ // The entry is the block's offset into the code space, or 0 for none.
+ CMP(SCRATCH1, Operand2(0));
+ FixupBranch noBlock = B_CC(CC_EQ);
+ MOVP2R(SCRATCH2, GetBasePtr());
+ ADD(SCRATCH1, SCRATCH1, SCRATCH2);
+ B(SCRATCH1);
+ SetJumpTarget(noBlock);
+
+ // No block found, let's jit. We don't need to save static regs, they're all callee saved.
+ // A replacement hook's block shadow entry jumps here too (GetBlockShadowHookValue()). JitAt
+ // replaces it with the new block's entry, and destroying that block puts the hook value back.
+ hooks_.blockShadowHook = GetCodePtr();
+ RestoreRoundingMode(true);
+ WriteDebugProfilerStatus(IRProfilerStatus::COMPILING);
+ QuickCallFunctionR(R12, &MIPSComp::JitAt, CTXREG);
+ WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
+ ApplyRoundingMode(true);
+
+ // Let's just dispatch again, we'll enter the block since we know it's there.
+ B(dispatcherNoCheck_);
+
+ SetJumpTarget(bail);
+
+ MOVP2R(SCRATCH1, &coreState);
+ LDR(SCRATCH1, SCRATCH1, 0);
+ CMP(SCRATCH1, Operand2(0));
+ B_CC(CC_EQ, outerLoop_);
+
+ const uint8_t *quitLoop = GetCodePtr();
+ SetJumpTarget(badCoreState);
+
+ WriteDebugProfilerStatus(IRProfilerStatus::NOT_RUNNING);
+ SaveStaticRegisters();
+ RestoreRoundingMode(true);
+
+ VPOP(D8, 8);
+ ADD(R_SP, R_SP, 4);
+ POP(9, R4, R5, R6, R7, R8, R9, R10, R11, R_PC); // Returns
+
+ hooks_.crashHandler = GetCodePtr();
+ MOVP2R(SCRATCH1, &coreState);
+ MOVI2R(SCRATCH2, CORE_RUNTIME_ERROR);
+ STR(SCRATCH2, SCRATCH1, 0);
+ B(quitLoop);
+
+ // Leave this at the end, add more stuff above.
+ if (enableDisasm) {
+ std::vector lines = DisassembleArm2(disasmStart, (int)(GetCodePtr() - disasmStart));
+ for (auto s : lines) {
+ INFO_LOG(Log::JIT, "%s", s.c_str());
+ }
+ }
+
+ // Let's spare the pre-generated code from unprotect-reprotect.
+ AlignCodePage();
+ jitStartOffset_ = (int)(GetCodePtr() - start);
+ // Don't forget to zap the instruction cache! This must stay at the end of this function.
+ FlushIcache();
+ EndWrite();
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/ARM/ArmIRCompALU.cpp b/Core/MIPS/ARM/ArmIRCompALU.cpp
new file mode 100644
index 0000000000..2826a6e3ab
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRCompALU.cpp
@@ -0,0 +1,141 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include "Core/MIPS/ARM/ArmIRJit.h"
+#include "Core/MIPS/ARM/ArmIRRegCache.h"
+
+// This file contains compilation for integer arithmetic, logic, shifts, compares, and multiplies.
+//
+// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
+// Currently known non working ones should have DISABLE. No flags because that's in IR already.
+
+// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
+#define CONDITIONAL_DISABLE {}
+#define DISABLE { CompIR_Generic(inst); return; }
+#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
+
+namespace MIPSComp {
+
+using namespace ArmGen;
+using namespace ArmIRJitConstants;
+
+void ArmJitBackend::CompIR_Arith(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Assign(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Bits(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Compare(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_CondAssign(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Div(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_HiLo(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Logic(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Mult(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Shift(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/ARM/ArmIRCompBranch.cpp b/Core/MIPS/ARM/ArmIRCompBranch.cpp
new file mode 100644
index 0000000000..aa0336ad77
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRCompBranch.cpp
@@ -0,0 +1,134 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include "Core/MIPS/ARM/ArmIRJit.h"
+#include "Core/MIPS/ARM/ArmIRRegCache.h"
+
+// This file contains compilation for exits.
+//
+// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
+// Currently known non working ones should have DISABLE. No flags because that's in IR already.
+
+// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
+#define CONDITIONAL_DISABLE {}
+#define DISABLE { CompIR_Generic(inst); return; }
+#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
+
+namespace MIPSComp {
+
+using namespace ArmGen;
+using namespace ArmIRJitConstants;
+
+void ArmJitBackend::CompIR_Exit(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ ARMReg exitReg = INVALID_REG;
+ switch (inst.op) {
+ case IROp::ExitToConst:
+ FlushAll();
+ WriteConstExit(inst.constant);
+ break;
+
+ case IROp::ExitToReg:
+ exitReg = regs_.MapGPR(inst.src1);
+ FlushAll();
+ MOV(SCRATCH1, exitReg);
+ B(dispatcherPCInSCRATCH1_);
+ break;
+
+ case IROp::ExitToPC:
+ FlushAll();
+ B(dispatcherCheckCoreState_);
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_ExitIf(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ ARMReg lhs = INVALID_REG;
+ ARMReg rhs = INVALID_REG;
+ FixupBranch fixup;
+ switch (inst.op) {
+ case IROp::ExitToConstIfEq:
+ case IROp::ExitToConstIfNeq:
+ if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {
+ lhs = regs_.MapGPR(inst.src2);
+ FlushAll();
+ CMP(lhs, Operand2(0));
+ } else if (regs_.IsGPRImm(inst.src2) && regs_.GetGPRImm(inst.src2) == 0) {
+ lhs = regs_.MapGPR(inst.src1);
+ FlushAll();
+ CMP(lhs, Operand2(0));
+ } else {
+ regs_.Map(inst);
+ lhs = regs_.R(inst.src1);
+ rhs = regs_.R(inst.src2);
+ FlushAll();
+ CMP(lhs, rhs);
+ }
+
+ fixup = B_CC(inst.op == IROp::ExitToConstIfEq ? CC_NEQ : CC_EQ);
+ WriteConstExit(inst.constant);
+ SetJumpTarget(fixup);
+ break;
+
+ case IROp::ExitToConstIfGtZ:
+ case IROp::ExitToConstIfGeZ:
+ case IROp::ExitToConstIfLtZ:
+ case IROp::ExitToConstIfLeZ:
+ {
+ lhs = regs_.MapGPR(inst.src1);
+ FlushAll();
+ CMP(lhs, Operand2(0));
+ // Branch over the exit when the condition fails.
+ CCFlags skip = CC_AL;
+ switch (inst.op) {
+ case IROp::ExitToConstIfGtZ: skip = CC_LE; break;
+ case IROp::ExitToConstIfGeZ: skip = CC_LT; break;
+ case IROp::ExitToConstIfLtZ: skip = CC_GE; break;
+ case IROp::ExitToConstIfLeZ: skip = CC_GT; break;
+ default: break;
+ }
+ fixup = B_CC(skip);
+ WriteConstExit(inst.constant);
+ SetJumpTarget(fixup);
+ break;
+ }
+
+ case IROp::ExitToConstIfFpTrue:
+ case IROp::ExitToConstIfFpFalse:
+ // Note: not used.
+ DISABLE;
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/ARM/ArmIRCompFPU.cpp b/Core/MIPS/ARM/ArmIRCompFPU.cpp
new file mode 100644
index 0000000000..117477a1f6
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRCompFPU.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include "Core/MIPS/ARM/ArmIRJit.h"
+#include "Core/MIPS/ARM/ArmIRRegCache.h"
+
+// This file contains compilation for floating point arithmetic, compares, and conversions.
+//
+// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
+// Currently known non working ones should have DISABLE. No flags because that's in IR already.
+
+// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
+#define CONDITIONAL_DISABLE {}
+#define DISABLE { CompIR_Generic(inst); return; }
+#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
+
+namespace MIPSComp {
+
+using namespace ArmGen;
+using namespace ArmIRJitConstants;
+
+void ArmJitBackend::CompIR_FArith(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FAssign(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FCompare(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FCondAssign(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FCvt(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FRound(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FSat(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FSpecial(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_RoundingMode(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ // The helpers are called with BL and use R12, so those mustn't hold anything.
+ switch (inst.op) {
+ case IROp::RestoreRoundingMode:
+ regs_.FlushBeforeCall();
+ RestoreRoundingMode();
+ break;
+
+ case IROp::ApplyRoundingMode:
+ regs_.FlushBeforeCall();
+ ApplyRoundingMode();
+ break;
+
+ case IROp::UpdateRoundingMode:
+ UpdateRoundingMode();
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/ARM/ArmIRCompLoadStore.cpp b/Core/MIPS/ARM/ArmIRCompLoadStore.cpp
new file mode 100644
index 0000000000..343461b593
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRCompLoadStore.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include "Core/MIPS/ARM/ArmIRJit.h"
+#include "Core/MIPS/ARM/ArmIRRegCache.h"
+
+// This file contains compilation for memory loads and stores.
+//
+// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
+// Currently known non working ones should have DISABLE. No flags because that's in IR already.
+
+// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
+#define CONDITIONAL_DISABLE {}
+#define DISABLE { CompIR_Generic(inst); return; }
+#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
+
+namespace MIPSComp {
+
+using namespace ArmGen;
+using namespace ArmIRJitConstants;
+
+void ArmJitBackend::CompIR_CondStore(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FLoad(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_FStore(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Load(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_LoadShift(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Store(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_StoreShift(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_VecLoad(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_VecStore(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/ARM/ArmIRCompSystem.cpp b/Core/MIPS/ARM/ArmIRCompSystem.cpp
new file mode 100644
index 0000000000..129632f7d8
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRCompSystem.cpp
@@ -0,0 +1,427 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include "Common/Profiler/Profiler.h"
+#include "Core/Core.h"
+#include "Core/Debugger/Breakpoints.h"
+#include "Core/HLE/HLE.h"
+#include "Core/HLE/ReplaceTables.h"
+#include "Core/MemMap.h"
+#include "Core/MIPS/MIPSAnalyst.h"
+#include "Core/MIPS/IR/IRInterpreter.h"
+#include "Core/MIPS/ARM/ArmIRJit.h"
+#include "Core/MIPS/ARM/ArmIRRegCache.h"
+
+// This file contains compilation for basic PC/downcount accounting, syscalls, debug funcs, etc.
+//
+// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
+// Currently known non working ones should have DISABLE. No flags because that's in IR already.
+
+// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
+#define CONDITIONAL_DISABLE {}
+#define DISABLE { CompIR_Generic(inst); return; }
+#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
+
+namespace MIPSComp {
+
+using namespace ArmGen;
+using namespace ArmIRJitConstants;
+
+void ArmJitBackend::CompIR_Basic(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ case IROp::Downcount:
+ SUBI2R(DOWNCOUNTREG, DOWNCOUNTREG, inst.constant, SCRATCH1);
+ break;
+
+ case IROp::SetConst:
+ regs_.SetGPRImm(inst.dest, inst.constant);
+ break;
+
+ case IROp::SetConstF:
+ {
+ regs_.Map(inst);
+ float f;
+ memcpy(&f, &inst.constant, sizeof(f));
+ MOVI2F(regs_.F(inst.dest), f, SCRATCH1);
+ break;
+ }
+
+ case IROp::SetPC:
+ regs_.Map(inst);
+ MovToPC(regs_.R(inst.src1));
+ break;
+
+ case IROp::SetPCConst:
+ lastConstPC_ = inst.constant;
+ MOVI2R(SCRATCH1, inst.constant);
+ MovToPC(SCRATCH1);
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Breakpoint(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ case IROp::Breakpoint:
+ FlushAll();
+ // Note: the constant could be a delay slot.
+ MOVI2R(R0, inst.constant);
+ QuickCallFunction(R12, &IRRunBreakpoint);
+ CMP(R0, Operand2(0));
+ B_CC(CC_NEQ, dispatcherCheckCoreState_);
+ break;
+
+ case IROp::MemoryCheck:
+ if (regs_.IsGPRImm(inst.src1)) {
+ uint32_t iaddr = regs_.GetGPRImm(inst.src1) + inst.constant;
+ uint32_t checkedPC = lastConstPC_ + inst.dest;
+ int size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
+ if (size == 0) {
+ checkedPC += 4;
+ size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
+ }
+ bool isWrite = MIPSAnalyst::IsOpMemoryWrite(checkedPC);
+
+ MemCheck check;
+ if (g_breakpoints.GetMemCheckInRange(iaddr, size, &check)) {
+ if (!(check.cond & MEMCHECK_READ) && !isWrite)
+ break;
+ if (!(check.cond & (MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE)) && isWrite)
+ break;
+
+ // We need to flush, or conditions and log expressions will see old register values.
+ FlushAll();
+
+ MOVI2R(R0, checkedPC);
+ MOVI2R(R1, iaddr);
+ QuickCallFunction(R12, &IRRunMemCheck);
+ CMP(R0, Operand2(0));
+ B_CC(CC_NEQ, dispatcherCheckCoreState_);
+ }
+ } else {
+ uint32_t checkedPC = lastConstPC_ + inst.dest;
+ int size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
+ if (size == 0) {
+ checkedPC += 4;
+ size = MIPSAnalyst::OpMemoryAccessSize(checkedPC);
+ }
+ bool isWrite = MIPSAnalyst::IsOpMemoryWrite(checkedPC);
+
+ const auto memchecks = g_breakpoints.GetMemCheckRanges(isWrite);
+ // We can trivially skip if there are no checks for this type (i.e. read vs write.)
+ if (memchecks.empty())
+ break;
+
+ // We need to flush, or conditions and log expressions will see old register values.
+ // The flush may use SCRATCH1, so read the address after it. R12 is free after it too.
+ FlushAll();
+ LDR(SCRATCH1, CTXREG, inst.src1 * 4);
+ ADDI2R(SCRATCH1, SCRATCH1, inst.constant, SCRATCH2);
+
+ std::vector hitChecks;
+ for (auto it : memchecks) {
+ if (it.end != 0) {
+ // A hit when start - size < addr < end, as an unsigned range check.
+ uint32_t lo = it.start - size + 1;
+ SUBI2R(R12, SCRATCH1, lo, SCRATCH2);
+ CMPI2R(R12, it.end - lo, SCRATCH2);
+ hitChecks.push_back(B_CC(CC_LO));
+ } else {
+ CMPI2R(SCRATCH1, it.start, SCRATCH2);
+ hitChecks.push_back(B_CC(CC_EQ));
+ }
+ }
+
+ FixupBranch noHits = B();
+
+ // Okay, now land any hit here.
+ for (auto &fixup : hitChecks)
+ SetJumpTarget(fixup);
+ hitChecks.clear();
+
+ MOV(R1, SCRATCH1);
+ MOVI2R(R0, checkedPC);
+ QuickCallFunction(R12, &IRRunMemCheck);
+ CMP(R0, Operand2(0));
+ B_CC(CC_NEQ, dispatcherCheckCoreState_);
+
+ SetJumpTarget(noHits);
+ }
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_System(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ case IROp::Syscall:
+ FlushAll();
+ SaveStaticRegisters();
+
+ WriteDebugProfilerStatus(IRProfilerStatus::SYSCALL);
+#ifdef USE_PROFILER
+ // When profiling, we can't skip CallSyscall, since it times syscalls.
+ MOVI2R(R0, inst.constant);
+ QuickCallFunction(R12, &CallSyscall);
+#else
+ // Skip the CallSyscall where possible.
+ {
+ MIPSOpcode op(inst.constant);
+ const HLEFunction *func = GetSyscallFunctionData(op, 0);
+ if (func) {
+ void *quickFunc = GetQuickSyscallFunc(func, op);
+ if (quickFunc) {
+ MOVP2R(R0, func);
+ QuickCallFunction(R12, (const u8 *)quickFunc);
+ } else {
+ MOVI2R(R0, inst.constant);
+ QuickCallFunction(R12, &CallSyscall);
+ }
+ } else {
+ // Shouldn't get here.
+ MOVI2R(R0, 0);
+ QuickCallFunction(R12, &CallSyscallUnresolvedAtPC);
+ }
+ }
+#endif
+
+ WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
+ LoadStaticRegisters();
+ // This is always followed by an ExitToPC, where we check coreState.
+ break;
+
+ case IROp::SyscallUnresolved:
+ FlushAll();
+ SaveStaticRegisters();
+ WriteDebugProfilerStatus(IRProfilerStatus::SYSCALL);
+ MOVI2R(R0, inst.constant);
+ QuickCallFunction(R12, &CallSyscallUnresolvedAtPC);
+ WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
+ LoadStaticRegisters();
+ break;
+
+ case IROp::CallReplacement:
+ FlushAll();
+ SaveStaticRegisters();
+ WriteDebugProfilerStatus(IRProfilerStatus::REPLACEMENT);
+ QuickCallFunction(R12, GetReplacementFunc(inst.constant)->replaceFunc);
+ WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
+ LoadStaticRegisters();
+
+ // Absolute value the result and subtract.
+ CMP(R0, Operand2(0));
+ SetCC(CC_MI);
+ RSB(SCRATCH2, R0, Operand2(0));
+ SetCC(CC_PL);
+ MOV(SCRATCH2, R0);
+ SetCC(CC_AL);
+ SUB(DOWNCOUNTREG, DOWNCOUNTREG, SCRATCH2);
+
+ // Everything was flushed, so this can't touch R0.
+ // Set dest reg to the sign of the result.
+ regs_.Map(inst);
+ ASR(regs_.R(inst.dest), R0, 31);
+ break;
+
+ case IROp::Break:
+ FlushAll();
+ // This doesn't naturally have restore/apply around it.
+ RestoreRoundingMode(true);
+ SaveStaticRegisters();
+ MovFromPC(R0);
+ QuickCallFunction(R12, &Core_BreakException);
+ LoadStaticRegisters();
+ ApplyRoundingMode(true);
+ MovFromPC(SCRATCH1);
+ ADDI2R(SCRATCH1, SCRATCH1, 4, SCRATCH2);
+ B(dispatcherPCInSCRATCH1_);
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_Transfer(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ case IROp::SetCtrlVFPU:
+ regs_.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, inst.constant);
+ break;
+
+ case IROp::SetCtrlVFPUReg:
+ regs_.Map(inst);
+ MOV(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.R(inst.src1));
+ break;
+
+ case IROp::SetCtrlVFPUFReg:
+ regs_.Map(inst);
+ VMOV(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.F(inst.src1));
+ break;
+
+ case IROp::FpCondFromReg:
+ regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });
+ MOV(regs_.R(IRREG_FPCOND), regs_.R(inst.src1));
+ break;
+
+ case IROp::FpCondToReg:
+ regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } });
+ MOV(regs_.R(inst.dest), regs_.R(IRREG_FPCOND));
+ break;
+
+ case IROp::FpCtrlFromReg:
+ regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });
+ ANDI2R(SCRATCH1, regs_.R(inst.src1), 0x0181FFFF, SCRATCH2);
+ // Extract the new fpcond value.
+ UBFX(regs_.R(IRREG_FPCOND), SCRATCH1, 23, 1);
+ STR(SCRATCH1, CTXREG, IRREG_FCR31 * 4);
+ break;
+
+ case IROp::FpCtrlToReg:
+ regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } });
+ // Load fcr31 and clear the fpcond bit.
+ LDR(regs_.R(inst.dest), CTXREG, IRREG_FCR31 * 4);
+ BFI(regs_.R(inst.dest), regs_.R(IRREG_FPCOND), 23, 1);
+ // Also update mips->fcr31 while we're here.
+ STR(regs_.R(inst.dest), CTXREG, IRREG_FCR31 * 4);
+ break;
+
+ case IROp::VfpuCtrlToReg:
+ regs_.Map(inst);
+ MOV(regs_.R(inst.dest), regs_.R(IRREG_VFPU_CTRL_BASE + inst.src1));
+ break;
+
+ case IROp::FMovFromGPR:
+ if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {
+ regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
+ MOVI2F(regs_.F(inst.dest), 0.0f, SCRATCH1);
+ } else {
+ regs_.Map(inst);
+ VMOV(regs_.F(inst.dest), regs_.R(inst.src1));
+ }
+ break;
+
+ case IROp::FMovToGPR:
+ regs_.Map(inst);
+ VMOV(regs_.R(inst.dest), regs_.F(inst.src1));
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_ValidateAddress(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ bool isWrite = inst.src2 & 1;
+ int alignment = 0;
+ switch (inst.op) {
+ case IROp::ValidateAddress8:
+ alignment = 1;
+ break;
+
+ case IROp::ValidateAddress16:
+ alignment = 2;
+ break;
+
+ case IROp::ValidateAddress32:
+ alignment = 4;
+ break;
+
+ case IROp::ValidateAddress128:
+ alignment = 16;
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+
+ regs_.Map(inst);
+ ADDI2R(SCRATCH1, regs_.R(inst.src1), inst.constant, SCRATCH2);
+ BIC(SCRATCH1, SCRATCH1, Operand2(0xC0, 4));
+
+ std::vector validJumps;
+
+ FixupBranch unaligned;
+ if (alignment != 1) {
+ TST(SCRATCH1, Operand2(alignment - 1));
+ unaligned = B_CC(CC_NEQ);
+ }
+
+ CMPI2R(SCRATCH1, PSP_GetUserMemoryEnd() - alignment, SCRATCH2);
+ FixupBranch tooHighRAM = B_CC(CC_HI);
+ CMPI2R(SCRATCH1, PSP_GetKernelMemoryBase(), SCRATCH2);
+ validJumps.push_back(B_CC(CC_HS));
+
+ CMPI2R(SCRATCH1, PSP_GetVidMemEnd() - alignment, SCRATCH2);
+ FixupBranch tooHighVid = B_CC(CC_HI);
+ CMPI2R(SCRATCH1, PSP_GetVidMemBase(), SCRATCH2);
+ validJumps.push_back(B_CC(CC_HS));
+
+ CMPI2R(SCRATCH1, PSP_GetScratchpadMemoryEnd() - alignment, SCRATCH2);
+ FixupBranch tooHighScratch = B_CC(CC_HI);
+ CMPI2R(SCRATCH1, PSP_GetScratchpadMemoryBase(), SCRATCH2);
+ validJumps.push_back(B_CC(CC_HS));
+
+ if (alignment != 1)
+ SetJumpTarget(unaligned);
+ SetJumpTarget(tooHighRAM);
+ SetJumpTarget(tooHighVid);
+ SetJumpTarget(tooHighScratch);
+
+ // If we got here, something unusual and bad happened, so we'll always go back to the dispatcher.
+ // Because of that, we can avoid flushing outside this case. The flush may use SCRATCH1, so the
+ // address waits on the stack (two regs, to keep it aligned for the call).
+ PUSH(2, SCRATCH1, SCRATCH2);
+ auto regsCopy = regs_;
+ regsCopy.FlushAll();
+ POP(2, SCRATCH1, SCRATCH2);
+
+ // Ignores the return value, always returns to the dispatcher.
+ // Otherwise would need a thunk to restore regs.
+ MOVI2R(R1, alignment);
+ MOVI2R(R2, isWrite ? 1 : 0);
+ QuickCallFunction(R12, &ReportBadAddress);
+ B(dispatcherCheckCoreState_);
+
+ for (FixupBranch &b : validJumps)
+ SetJumpTarget(b);
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/ARM/ArmIRCompVec.cpp b/Core/MIPS/ARM/ArmIRCompVec.cpp
new file mode 100644
index 0000000000..ed3ab062c8
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRCompVec.cpp
@@ -0,0 +1,81 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include "Core/MIPS/ARM/ArmIRJit.h"
+#include "Core/MIPS/ARM/ArmIRRegCache.h"
+
+// This file contains compilation for Vec4 operations.
+//
+// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
+// Currently known non working ones should have DISABLE. No flags because that's in IR already.
+
+// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
+#define CONDITIONAL_DISABLE {}
+#define DISABLE { CompIR_Generic(inst); return; }
+#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
+
+namespace MIPSComp {
+
+using namespace ArmGen;
+using namespace ArmIRJitConstants;
+
+void ArmJitBackend::CompIR_VecArith(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_VecAssign(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_VecHoriz(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+void ArmJitBackend::CompIR_VecPack(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ switch (inst.op) {
+ default:
+ CompIR_Generic(inst);
+ break;
+ }
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/ARM/ArmIRJit.cpp b/Core/MIPS/ARM/ArmIRJit.cpp
new file mode 100644
index 0000000000..605c39a180
--- /dev/null
+++ b/Core/MIPS/ARM/ArmIRJit.cpp
@@ -0,0 +1,375 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "ppsspp_config.h"
+#if PPSSPP_ARCH(ARM)
+
+#include
+#include
+#include