Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / unittest / JitHarness.cpp
blobda52347d941779a969df23ccb318c8306ba52a63
1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18 #include <algorithm>
20 #include "base/timeutil.h"
21 #include "input/input_state.h"
22 #include "Core/MIPS/JitCommon/JitCommon.h"
23 #include "Core/MIPS/JitCommon/NativeJit.h"
24 #include "Core/MIPS/MIPSCodeUtils.h"
25 #include "Core/MIPS/MIPSDebugInterface.h"
26 #include "Core/MIPS/MIPSAsm.h"
27 #include "Core/MIPS/MIPSTables.h"
28 #include "Core/MemMap.h"
29 #include "Core/Core.h"
30 #include "Core/CoreTiming.h"
31 #include "Core/HLE/HLE.h"
33 struct InputState;
34 // Temporary hacks around annoying linking errors. Copied from Headless.
35 void D3D9_SwapBuffers() { }
36 void GL_SwapBuffers() { }
37 void NativeUpdate(InputState &input_state) { }
38 void NativeRender() { }
39 void NativeResized() { }
41 void System_SendMessage(const char *command, const char *parameter) {}
42 bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultvalue, std::wstring &outvalue) { return false; }
44 #ifndef _WIN32
45 InputState input_state;
46 #endif
48 void UnitTestTerminator() {
49 // Bails out of jit so we can time things.
50 coreState = CORE_POWERDOWN;
51 hleSkipDeadbeef();
54 HLEFunction UnitTestFakeSyscalls[] = {
55 {0x1234BEEF, &UnitTestTerminator, "UnitTestTerminator"},
58 double ExecCPUTest() {
59 int blockTicks = 1000000;
60 int total = 0;
61 double st = real_time_now();
62 do {
63 for (int j = 0; j < 1000; ++j) {
64 currentMIPS->pc = PSP_GetUserMemoryBase();
65 coreState = CORE_RUNNING;
67 while (coreState == CORE_RUNNING) {
68 mipsr4k.RunLoopUntil(blockTicks);
70 ++total;
73 while (real_time_now() - st < 0.5);
74 double elapsed = real_time_now() - st;
76 return total / elapsed;
79 static void SetupJitHarness() {
80 // We register a syscall so we have an easy way to finish the test.
81 RegisterModule("UnitTestFakeSyscalls", ARRAY_SIZE(UnitTestFakeSyscalls), UnitTestFakeSyscalls);
83 // This is pretty much the bare minimum required to setup jit.
84 coreState = CORE_POWERUP;
85 currentMIPS = &mipsr4k;
86 Memory::g_MemorySize = Memory::RAM_NORMAL_SIZE;
87 PSP_CoreParameter().cpuCore = CPU_INTERPRETER;
88 PSP_CoreParameter().unthrottle = true;
90 Memory::Init();
91 mipsr4k.Reset();
92 CoreTiming::Init();
95 static void DestroyJitHarness() {
96 // Clear our custom module out to be safe.
97 HLEShutdown();
98 CoreTiming::Shutdown();
99 Memory::Shutdown();
100 mipsr4k.Shutdown();
101 coreState = CORE_POWERDOWN;
102 currentMIPS = nullptr;
105 bool TestJit() {
106 SetupJitHarness();
108 currentMIPS->pc = PSP_GetUserMemoryBase();
109 u32 *p = (u32 *)Memory::GetPointer(currentMIPS->pc);
111 // TODO: Smarter way of seeding in the code sequence.
112 static const char *lines[] = {
113 //"vcrsp.t C000, C100, C200",
114 //"vdot.q C000, C100, C200",
115 //"vmmul.q M000, M100, M200",
116 "lui r1, 0x8910",
117 "vmmul.q M000, M100, M200",
118 "sv.q C000, 0(r1)",
119 "sv.q C000, 16(r1)",
120 "sv.q C000, 32(r1)",
121 "sv.q C000, 48(r1)",
123 "abs.s f1, f1",
124 "cvt.w.s f1, f1",
125 "cvt.w.s f3, f1",
126 "cvt.w.s f0, f2",
127 "cvt.w.s f5, f1",
128 "cvt.w.s f6, f5",
132 bool compileSuccess = true;
133 u32 addr = currentMIPS->pc;
134 DebugInterface *dbg = currentDebugMIPS;
135 for (int i = 0; i < 100; ++i) {
137 // VFPU ops aren't supported by MIPSAsm yet.
138 *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8);
139 *p++ = 0xD03C0000 | (1 << 7) | (1 << 15);
140 *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8);
141 *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8);
142 *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8);
143 *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8);
144 *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8);
146 for (size_t j = 0; j < ARRAY_SIZE(lines); ++j) {
147 p++;
148 if (!MIPSAsm::MipsAssembleOpcode(lines[j], currentDebugMIPS, addr)) {
149 printf("ERROR: %ls\n", MIPSAsm::GetAssembleError().c_str());
150 compileSuccess = false;
152 addr += 4;
156 *p++ = MIPS_MAKE_SYSCALL("UnitTestFakeSyscalls", "UnitTestTerminator");
157 *p++ = MIPS_MAKE_BREAK(1);
159 // Dogfood.
160 addr = currentMIPS->pc;
161 for (size_t j = 0; j < ARRAY_SIZE(lines); ++j) {
162 char line[512];
163 MIPSDisAsm(Memory::Read_Instruction(addr), addr, line, true);
164 addr += 4;
165 printf("%s\n", line);
168 printf("\n");
170 double jit_speed = 0.0, interp_speed = 0.0;
171 if (compileSuccess) {
172 interp_speed = ExecCPUTest();
173 mipsr4k.UpdateCore(CPU_JIT);
174 jit_speed = ExecCPUTest();
176 // Disassemble
177 JitBlockCache *cache = MIPSComp::jit->GetBlockCache();
178 JitBlock *block = cache->GetBlock(0); // Should only be one block.
179 #if defined(ARM)
180 std::vector<std::string> lines = DisassembleArm2(block->normalEntry, block->codeSize);
181 #elif defined(ARM64)
182 std::vector<std::string> lines = DisassembleArm64(block->normalEntry, block->codeSize);
183 #else
184 std::vector<std::string> lines = DisassembleX86(block->normalEntry, block->codeSize);
185 #endif
186 // Cut off at 25 due to the repetition above. Might need tweaking for large instructions.
187 const int cutoff = 25;
188 for (int i = 0; i < std::min((int)lines.size(), cutoff); i++) {
189 printf("%s\n", lines[i].c_str());
191 if (lines.size() > cutoff)
192 printf("...\n");
193 printf("Jit was %fx faster than interp.\n\n", jit_speed / interp_speed);
196 printf("\n");
198 DestroyJitHarness();
200 return jit_speed >= interp_speed;