diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 9444b2fe41..fcb56a6c32 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(common PRIVATE EventSource.cpp Exceptions.cpp FastFormatString.cpp + FastJmp.cpp IniInterface.cpp Mutex.cpp PathUtils.cpp @@ -63,6 +64,7 @@ target_sources(common PRIVATE Dependencies.h EventSource.h Exceptions.h + FastJmp.h General.h MemcpyFast.h MemsetFast.inl @@ -114,6 +116,8 @@ if(USE_VTUNE) endif() if(WIN32) + enable_language(ASM_MASM) + target_sources(common PRIVATE FastJmp.asm) target_link_libraries(common PUBLIC pthreads4w Winmm.lib) endif() diff --git a/common/FastJmp.asm b/common/FastJmp.asm new file mode 100644 index 0000000000..743f798ef4 --- /dev/null +++ b/common/FastJmp.asm @@ -0,0 +1,129 @@ +; PCSX2 - PS2 Emulator for PCs +; Copyright (C) 2002-2021 PCSX2 Dev Team +; +; PCSX2 is free software: you can redistribute it and/or modify it under the terms +; of the GNU Lesser General Public License as published by the Free Software Found- +; ation, either version 3 of the License, or (at your option) any later version. +; +; PCSX2 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 for more details. +; +; You should have received a copy of the GNU General Public License along with PCSX2. +; If not, see . + +IFDEF _M_X86_32 + +; ----------------------------------------- +; 32-bit X86 +; ----------------------------------------- + .386 + .model flat + +_TEXT SEGMENT + +PUBLIC @fastjmp_set@4 +PUBLIC @fastjmp_jmp@8 + +; void fastjmp_set(fastjmp_buf*) +@fastjmp_set@4 PROC + mov eax, dword ptr [esp] + mov edx, esp ; fixup stack pointer, so it doesn't include the call to fastjmp_set + add edx, 4 + mov dword ptr [ecx], eax ; actually eip + mov dword ptr [ecx + 4], ebx + mov dword ptr [ecx + 8], edx ; actually esp + mov dword ptr [ecx + 12], ebp + mov dword ptr [ecx + 16], esi + mov dword ptr [ecx + 20], edi + xor eax, eax + ret +@fastjmp_set@4 ENDP + +; void __fastcall fastjmp_jmp(fastjmp_buf*, int) +@fastjmp_jmp@8 PROC + mov eax, edx ; return code + mov edx, dword ptr [ecx + 0] + mov ebx, dword ptr [ecx + 4] + mov esp, dword ptr [ecx + 8] + mov ebp, dword ptr [ecx + 12] + mov esi, dword ptr [ecx + 16] + mov edi, dword ptr [ecx + 20] + jmp edx +@fastjmp_jmp@8 ENDP + +_TEXT ENDS + +ENDIF ; _M_X86_32 + +IFDEF _M_X86_64 +; ----------------------------------------- +; 64-bit X86 +; ----------------------------------------- +_TEXT SEGMENT + +PUBLIC fastjmp_set +PUBLIC fastjmp_jmp + +; void fastjmp_set(fastjmp_buf*) +fastjmp_set PROC + mov rax, qword ptr [rsp] + mov rdx, rsp ; fixup stack pointer, so it doesn't include the call to fastjmp_set + add rdx, 8 + mov qword ptr [rcx], rax ; actually rip + mov qword ptr [rcx + 8], rbx + mov qword ptr [rcx + 16], rdx ; actually rsp + mov qword ptr [rcx + 24], rbp + mov qword ptr [rcx + 32], rsi + mov qword ptr [rcx + 40], rdi + mov qword ptr [rcx + 48], r12 + mov qword ptr [rcx + 56], r13 + mov qword ptr [rcx + 64], r14 + mov qword ptr [rcx + 72], r15 + movaps xmmword ptr [rcx + 80], xmm6 + movaps xmmword ptr [rcx + 96], xmm7 + movaps xmmword ptr [rcx + 112], xmm8 + add rcx, 112 ; split to two batches to fit displacement in a single byte + movaps xmmword ptr [rcx + 16], xmm9 + movaps xmmword ptr [rcx + 32], xmm10 + movaps xmmword ptr [rcx + 48], xmm11 + movaps xmmword ptr [rcx + 64], xmm12 + movaps xmmword ptr [rcx + 80], xmm13 + movaps xmmword ptr [rcx + 96], xmm14 + movaps xmmword ptr [rcx + 112], xmm15 + xor eax, eax + ret +fastjmp_set ENDP + +; void fastjmp_jmp(fastjmp_buf*, int) +fastjmp_jmp PROC + mov eax, edx ; return code + mov rdx, qword ptr [rcx + 0] ; actually rip + mov rbx, qword ptr [rcx + 8] + mov rsp, qword ptr [rcx + 16] + mov rbp, qword ptr [rcx + 24] + mov rsi, qword ptr [rcx + 32] + mov rdi, qword ptr [rcx + 40] + mov r12, qword ptr [rcx + 48] + mov r13, qword ptr [rcx + 56] + mov r14, qword ptr [rcx + 64] + mov r15, qword ptr [rcx + 72] + movaps xmm6, xmmword ptr [rcx + 80] + movaps xmm7, xmmword ptr [rcx + 96] + movaps xmm8, xmmword ptr [rcx + 112] + add rcx, 112 ; split to two batches to fit displacement in a single byte + movaps xmm9, xmmword ptr [rcx + 16] + movaps xmm10, xmmword ptr [rcx + 32] + movaps xmm11, xmmword ptr [rcx + 48] + movaps xmm12, xmmword ptr [rcx + 64] + movaps xmm13, xmmword ptr [rcx + 80] + movaps xmm14, xmmword ptr [rcx + 96] + movaps xmm15, xmmword ptr [rcx + 112] + jmp rdx +fastjmp_jmp ENDP + +_TEXT ENDS + +ENDIF ; _M_X86_64 + +END diff --git a/common/FastJmp.cpp b/common/FastJmp.cpp new file mode 100644 index 0000000000..7ec395eb50 --- /dev/null +++ b/common/FastJmp.cpp @@ -0,0 +1,92 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "FastJmp.h" + +#ifndef _WIN32 + +#if defined(__APPLE__) +#define PREFIX "_" +#else +#define PREFIX "" +#endif + +#if defined(_M_X86_64) + +asm( + "\t.global " PREFIX "fastjmp_set\n" + "\t.global " PREFIX "fastjmp_jmp\n" + "\t.text\n" + "\t" PREFIX "fastjmp_set:" R"( + movq 0(%rsp), %rax + movq %rsp, %rdx # fixup stack pointer, so it doesn't include the call to fastjmp_set + addq $8, %rdx + movq %rax, 0(%rdi) # actually rip + movq %rbx, 8(%rdi) + movq %rdx, 16(%rdi) # actually rsp + movq %rbp, 24(%rdi) + movq %r12, 32(%rdi) + movq %r13, 40(%rdi) + movq %r14, 48(%rdi) + movq %r15, 56(%rdi) + xorl %eax, %eax + ret +)" + "\t" PREFIX "fastjmp_jmp:" R"( + movl %esi, %eax + movq 0(%rdi), %rdx # actually rip + movq 8(%rdi), %rbx + movq 16(%rdi), %rsp # actually rsp + movq 24(%rdi), %rbp + movq 32(%rdi), %r12 + movq 40(%rdi), %r13 + movq 48(%rdi), %r14 + movq 56(%rdi), %r15 + jmp *%rdx +)"); + +#elif defined(_M_X86_32) + +asm( + "\t.global " PREFIX "fastjmp_set\n" + "\t.global " PREFIX "fastjmp_jmp\n" + "\t.text\n" + "\t" PREFIX "fastjmp_set:" R"( + movl %eax, 0(%esp) + movl %esp, %edx # fixup stack pointer, so it doesn't include the call to fastjmp_set + addl $4, %edx + movl %eax, 0(%ecx) # actually eip + movl %ebx, 4(%ecx) + movl %edx, 8(%ecx) # actually esp + movl %ebp, 12(%ecx) + movl %esi, 16(%ecx) + movl %edi, 20(%ecx) + xorl %eax, %eax + ret +)" + "\t" PREFIX "fastjmp_jmp:" R"( + movl %edx, %eax + movl 0(%ecx), %edx # actually eip + movl 4(%ecx), %ebx + movl 8(%ecx), %esp # actually esp + movl 12(%ecx), %ebp + movl 16(%ecx), %esi + movl 20(%ecx), %edi + jmp *%edx +)"); + +#endif + +#endif // __WIN32 diff --git a/common/FastJmp.h b/common/FastJmp.h new file mode 100644 index 0000000000..099b8f57a2 --- /dev/null +++ b/common/FastJmp.h @@ -0,0 +1,39 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once +#include "Pcsx2Defs.h" +#include +#include + +struct fastjmp_buf +{ +#if defined(_M_X86_64) && defined(_WIN32) + static constexpr std::size_t BUF_SIZE = 240; +#elif defined(_M_X86_64) + static constexpr std::size_t BUF_SIZE = 64; +#elif defined(_M_X86_32) + static constexpr std::size_t BUF_SIZE = 24; +#else +#error Unknown architecture. +#endif + + alignas(16) std::uint8_t buf[BUF_SIZE]; +}; + +extern "C" { +int __fastcall fastjmp_set(fastjmp_buf* buf); +__noreturn void __fastcall fastjmp_jmp(const fastjmp_buf* buf, int ret); +} diff --git a/common/Pcsx2Defs.h b/common/Pcsx2Defs.h index ccb2cd4f92..2e68b39a6c 100644 --- a/common/Pcsx2Defs.h +++ b/common/Pcsx2Defs.h @@ -118,6 +118,7 @@ static const int __pagesize = PCSX2_PAGESIZE; #define __pagealigned __declspec(align(PCSX2_PAGESIZE)) #define __noinline __declspec(noinline) +#define __noreturn __declspec(noreturn) // Don't know if there are Visual C++ equivalents of these. #define likely(x) (!!(x)) @@ -161,6 +162,9 @@ static const int __pagesize = PCSX2_PAGESIZE; #ifndef __noinline #define __noinline __attribute__((noinline)) #endif +#ifndef __noreturn +#define __noreturn __attribute__((noreturn)) +#endif #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #endif diff --git a/common/common.vcxproj b/common/common.vcxproj index 7006422fd0..7c943449ae 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -15,7 +15,9 @@ false - + + + @@ -43,6 +45,9 @@ + + true + @@ -83,10 +88,16 @@ + + Document + _M_X86_32;%(PreprocessorDefinitions) + _M_X86_64;%(PreprocessorDefinitions) + + @@ -146,5 +157,7 @@ - - \ No newline at end of file + + + + diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters index e4415b41aa..cce1969f44 100644 --- a/common/common.vcxproj.filters +++ b/common/common.vcxproj.filters @@ -118,6 +118,9 @@ Source Files + + Source Files + @@ -264,6 +267,9 @@ Header Files + + Header Files + @@ -278,4 +284,9 @@ Source Files - \ No newline at end of file + + + Source Files + + +