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/.
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"
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; }
45 InputState input_state
;
48 void UnitTestTerminator() {
49 // Bails out of jit so we can time things.
50 coreState
= CORE_POWERDOWN
;
54 HLEFunction UnitTestFakeSyscalls
[] = {
55 {0x1234BEEF, &UnitTestTerminator
, "UnitTestTerminator"},
58 double ExecCPUTest() {
59 int blockTicks
= 1000000;
61 double st
= real_time_now();
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
);
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;
95 static void DestroyJitHarness() {
96 // Clear our custom module out to be safe.
98 CoreTiming::Shutdown();
101 coreState
= CORE_POWERDOWN
;
102 currentMIPS
= nullptr;
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",
117 "vmmul.q M000, M100, M200",
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
) {
148 if (!MIPSAsm::MipsAssembleOpcode(lines
[j
], currentDebugMIPS
, addr
)) {
149 printf("ERROR: %ls\n", MIPSAsm::GetAssembleError().c_str());
150 compileSuccess
= false;
156 *p
++ = MIPS_MAKE_SYSCALL("UnitTestFakeSyscalls", "UnitTestTerminator");
157 *p
++ = MIPS_MAKE_BREAK(1);
160 addr
= currentMIPS
->pc
;
161 for (size_t j
= 0; j
< ARRAY_SIZE(lines
); ++j
) {
163 MIPSDisAsm(Memory::Read_Instruction(addr
), addr
, line
, true);
165 printf("%s\n", line
);
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();
177 JitBlockCache
*cache
= MIPSComp::jit
->GetBlockCache();
178 JitBlock
*block
= cache
->GetBlock(0); // Should only be one block.
180 std::vector
<std::string
> lines
= DisassembleArm2(block
->normalEntry
, block
->codeSize
);
182 std::vector
<std::string
> lines
= DisassembleArm64(block
->normalEntry
, block
->codeSize
);
184 std::vector
<std::string
> lines
= DisassembleX86(block
->normalEntry
, block
->codeSize
);
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
)
193 printf("Jit was %fx faster than interp.\n\n", jit_speed
/ interp_speed
);
200 return jit_speed
>= interp_speed
;