Compare commits

..
453 Commits
Author SHA1 Message Date
Ryan Houdek 6a1ca654db Docs: Update for release FEX-2105 2021-05-05 17:53:55 -07:00
Ryan Houdek 623b29de8c Merge pull request #1014 from Sonicadvance1/fix_host_stacks
Fixes host thread stacks ending up in lower 32-bit VA
2021-05-05 15:51:40 -07:00
Ryan Houdek 9be67fb22a Merge pull request #1009 from Sonicadvance1/handle_self
Handle more cases of an application pinging self
2021-05-05 14:51:11 -07:00
Ryan Houdek cb9286c11e Merge pull request #1012 from Sonicadvance1/fix_ioctl_errors
Fixes ioctl returning error
2021-05-05 14:50:46 -07:00
Ryan Houdek 1e40148f60 Fixes host thread stacks ending up in lower 32-bit VA
Overriding glibc's mmap and munmap functions don't encapsulate that functions they use
for allocating stack data so it was falling down the system mmap/munmap path.
This was causing host side stacks to end up in the 32-bit VA space in 32-bit applications.
2021-05-04 22:15:46 -07:00
Ryan Houdek d9d5303aa0 Fixes ioctl returning error
Fixes evdev device description at the very least
2021-05-04 18:45:25 -07:00
Ryan Houdek 517b575783 Merge pull request #1010 from FEX-Emu/skmp/aotupdate-install-cmake
AOTIR: Add install rule for FEXUpdateAOTIRCache
2021-05-04 18:11:27 -07:00
Ryan Houdek 605994dca2 Merge pull request #1011 from FEX-Emu/skmp/fix-0xcd-breaks
JIT: Add reason 1 (0xCD) to OP_BREAK implementations
2021-05-04 18:11:17 -07:00
Ryan Houdek ac97c05271 Handle more cases of an application pinging self
AppImage uses this to inspect its own executable to ensure it is an AppImage file.
2021-05-04 18:09:15 -07:00
Stefanos Kornilios Mitsis Poiitidis c8bb02f3a6 JIT: Add reason 1 (0xCD) to OP_BREAK implementations 2021-05-04 13:22:00 +03:00
Stefanos Kornilios Mitsis Poiitidis 6b12c1768f AOTIR: Add install rule for FEXUpdateAOTIRCache 2021-05-04 12:33:59 +03:00
Stefanos Kornilios Mitsis Poiitidis 693097c5b1 Merge pull request #1008 from Sonicadvance1/fix_binfmt_misc
Updates binfmt_misc files to support AppImage
2021-05-04 11:14:49 +03:00
Ryan Houdek 47e823b773 Updates binfmt_misc files to support AppImage
AppImage files stick additional data in the ABI Version and PAD areas that it was getting blocked on.
2021-05-03 18:08:00 -07:00
Ryan Houdek fd6d33d197 Merge pull request #996 from FEX-Emu/skmp/aotirgen
AOTIR: Offline Generation
2021-05-03 15:58:57 -07:00
Stefanos Kornilios Mitsis Poiitidis d8b583bc51 Remove assert that was used in debugging 2021-05-04 01:47:39 +03:00
Stefanos Kornilios Mitsis Poiitidis be41f14452 Fix generation script 2021-05-03 18:16:43 +03:00
Stefanos Kornilios Mitsis Poiitidis cf5bd202d4 AOTIR: Update gen script to default to global fex installation 2021-05-03 06:15:25 +03:00
Stefanos Kornilios Mitsis Poiitidis 8893cc6a7d Undo xbyak update 2021-05-03 05:48:00 +03:00
Stefanos Kornilios Mitsis Poiitidis 1d82e9e59e JIT: Stub Break reasons 2, 3 2021-05-03 05:42:46 +03:00
Stefanos Kornilios Mitsis Poiitidis 5b017f9a66 Merge pull request #1007 from Sonicadvance1/more_ioctl32_emulation2
Adds Nouveau and Lima ioctl32 handlers
2021-05-03 05:10:21 +03:00
Ryan Houdek 3399052eaa Make sure to setup ioctl32 handler table 2021-05-02 14:30:23 -07:00
Ryan Houdek 3df951695e Adds Nouveau and Lima ioctl32 handlers 2021-05-02 13:05:46 -07:00
Ryan Houdek 8d1f9a9081 Merge pull request #1006 from Sonicadvance1/implement_clflush
Implements support for CLFLUSH
2021-05-02 12:27:54 -07:00
Ryan Houdek 68adb1ae12 Basic clflush test 2021-05-02 12:18:35 -07:00
Ryan Houdek 4961604807 Implements support for CLFLUSH
JRE requires this to run
2021-05-02 12:18:35 -07:00
Stefanos Kornilios Mitsis Poiitidis d6b7f32d33 AOTGen: Cleanup unwind symbol logic, add to x86-32 binaries 2021-05-02 11:08:12 +03:00
Stefanos Kornilios Mitsis Poiitidis 6dc69063f3 Merge pull request #1005 from Sonicadvance1/remove_numa
Remove reliance on librt and libnuma
2021-05-02 09:50:10 +03:00
Stefanos Kornilios Mitsis Poiitidis 2916c4f34c Merge pull request #1004 from Sonicadvance1/redirect_self
Redirect applications using execve with self
2021-05-02 09:49:55 +03:00
Stefanos Kornilios Mitsis Poiitidis 04137d0da5 Merge pull request #1002 from Sonicadvance1/sigtimedwait
Initial base implementation of sigtimedwait
2021-05-02 09:49:49 +03:00
Stefanos Kornilios Mitsis Poiitidis afa81294c9 Merge pull request #1001 from Sonicadvance1/more_ioctl32_emulation
More ioctl32 emulation
2021-05-02 09:49:44 +03:00
Ryan Houdek 86bdbe65e5 Implements missed x86-32 message queue syscalls
Nothing hit these but good to have them implemented
2021-05-01 17:40:33 -07:00
Ryan Houdek 63dee0e132 Remove reliance on librt and libnuma
These can fall down the raw syscall path
2021-05-01 17:40:33 -07:00
Ryan Houdek 6618bb809b Redirect applications using execve with self
Fixes a step in the Java JRE and shapez.io
2021-05-01 11:09:44 -07:00
Ryan Houdek d2b19c0b1c Disable gvisor sigtimedwait test 2021-05-01 10:19:13 -07:00
Ryan Houdek 90a8c4f114 Shuffle timed sigwait unit tests that have changed behaviour 2021-05-01 10:11:20 -07:00
Ryan Houdek 866baf66db Merge pull request #1003 from Sonicadvance1/cleaned_sendrecvmsg
Workaround fixes in sendmsg/recvmsg
2021-05-01 10:06:41 -07:00
Ryan Houdek 9498a41f9b More ioctl32 emulation
More DRM and i915 things, then wireless for some reason renderdoc relies on it
2021-05-01 09:40:02 -07:00
Ryan Houdek 1340ffabe4 Workaround fixes in sendmsg/recvmsg
Rebased and cleaned up #933
Needs to be larger allocations than what was originally proposed by #933
2021-05-01 09:37:01 -07:00
Ryan Houdek b5115e096f Initial base implementation of sigtimedwait 2021-05-01 09:35:29 -07:00
Ryan Houdek 235e05e8b9 Merge pull request #978 from Sonicadvance1/wip_allocator
64-bit allocator and ioctl emulation
2021-05-01 09:29:46 -07:00
Ryan Houdek 26b4bd80bf Force 32-bit allocator for CI 2021-05-01 09:19:55 -07:00
Ryan Houdek a8881e8835 Adds option to force 32-bit allocator 2021-05-01 09:19:55 -07:00
Ryan Houdek d1da1b0be5 Disables 20080723-1.c.gcc-target-test-32 CI test
This test consistently fails on the solid run board.
This requires some deep dive investigation
2021-05-01 09:06:39 -07:00
Ryan Houdek a19c0184a6 Keep using the 32-bit allocator if the kernel is old
Works around the Xavier boards running kernel 4.9
2021-05-01 09:06:39 -07:00
Ryan Houdek 2d0dced9e7 Work around old asound headers 2021-05-01 09:06:39 -07:00
Ryan Houdek 52cb96d630 Work around old DRM headers for i915 2021-05-01 09:06:39 -07:00
Ryan Houdek 94017d2e65 On old kernels, steal the 32-bit address space before the 64-bit
Increases speed of startup
2021-05-01 09:06:39 -07:00
Ryan Houdek 13b93ef6b5 Allow memory allocation to go through if the base was above the lower bound 2021-05-01 09:06:39 -07:00
Ryan Houdek c83a5d1bc3 Allow some more clone flags through with info log 2021-05-01 09:06:39 -07:00
Ryan Houdek c20777d558 Change these log messages so we know which one failed 2021-05-01 09:06:38 -07:00
Ryan Houdek 9a0fc4e7e8 Workaround quick with memory allocator. Aligned 4GB regions are hard to allocate 2021-05-01 07:39:14 -07:00
Ryan Houdek 417e8836a4 Arm64: Fixes some wrong sign extensions 2021-05-01 07:39:14 -07:00
Ryan Houdek 237865e064 Adds ioctl verification to struct verifier 2021-05-01 07:39:14 -07:00
Ryan Houdek 45223cc7e7 Adds initial ioctl emulation code 2021-05-01 07:39:14 -07:00
Ryan Houdek 8d34222556 Add vardecl type matching for ioctl verification 2021-05-01 07:39:14 -07:00
Ryan Houdek b6344c4753 Removes old 32-bit allocator usage
Replaces with using host side allocations since we always have a 64-bit allocator in this case
2021-05-01 07:39:14 -07:00
Ryan Houdek c5bda47b2a Adds more allocator hooking 2021-05-01 07:39:14 -07:00
Ryan Houdek 5067737052 Adds 64-bit allocator 2021-05-01 07:39:14 -07:00
Ryan Houdek 5143100a73 Switches more usages of memory allocation routines over to FEX internal 2021-05-01 07:39:14 -07:00
Stefanos Kornilios Mitsis Poiitidis 8b6e767742 Frontend: Slightly more resilient max instruction size handling 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 331549c64a RLSE: Use Constant, not inline constant, let ConstProp generate all inline consts 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis be0b29bbc6 OpDisp: Convert more asserts to soft-errors 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis ee8f8ed176 Improve error handling 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 5b8cbae1e1 Core/Frontend: Improve error handling 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis b98ef9e48b Core/Frontend: Improve error handling 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis fd724448ab Front End: Fix an error check 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis f4083883ca Frontend: Fix ExternalBranches initialization 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 9a4fc758c1 AOTIR: FEXUpdateAOTIRCache.sh now passes compilation modifiers 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 40d2cd8992 Fix asserts 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis ab44f800f7 AOTgen: Cleaner interfaces, use LogMan 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 1a5bc39d09 Rebase fixes 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 4bd997adaa AOT: Postfix ir files with .aotir 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 244ef94e9b AOTIR: Generate list of files with code, add Scripts/FEXUpdateAOTIRCache.sh to generate all files 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis a85ef061d1 AOITR: Add AOTIRGenerate fo FEXLoader option that does generation 2021-05-01 09:40:50 +03:00
Stefanos Kornilios Mitsis Poiitidis 4e847d03b7 Remove more assertions for invalid code 2021-05-01 09:40:49 +03:00
Stefanos Kornilios Mitsis Poiitidis ce9324f50f Kinda-parse unwind table 2021-05-01 09:40:49 +03:00
Stefanos Kornilios Mitsis Poiitidis 0897fc4a0a pretrans wip 2021-05-01 09:40:49 +03:00
Ryan Houdek d256bc59fd Override glibc allocator functions 2021-04-30 18:28:45 -07:00
Ryan Houdek e0d6f4bc34 Make sure link jemalloc in FEXCore 2021-04-30 18:28:45 -07:00
Ryan Houdek 09284331a7 Adds pregen include directory for jemalloc 2021-04-30 18:28:45 -07:00
Ryan Houdek 73e61b0c8e Switches default logging output to stderr
stdout is too likely to break something
2021-04-30 18:28:45 -07:00
Ryan Houdek a30866a886 Merge pull request #976 from FEX-Emu/skmp/aotir-mmap
AOTIR: Switch over to mmap-based loading
2021-04-30 18:24:54 -07:00
Stefanos Kornilios Mitsis Poiitidis 3e1da51cae Merge pull request #998 from Sonicadvance1/remove_xxhash_check
Removes xxhash version check
2021-04-30 12:05:18 +03:00
Stefanos Kornilios Mitsis Poiitidis 4efe973b65 Merge pull request #999 from Sonicadvance1/fix_sdl2_interface_Again
Fixes SDL2 interface check...again.
2021-04-30 12:04:50 +03:00
Ryan Houdek f6cc75de87 Fixes SDL2 interface check...again. 2021-04-30 01:55:08 -07:00
Ryan Houdek 6b082f4ef7 Removes xxhash version check
xxhash doesn't yet have a version of the library with pkg-config giving
a version.
Debian has backported a change for this but Arch hasn't.
https://github.com/Cyan4973/xxHash/issues/524
2021-04-30 01:35:03 -07:00
Stefanos Kornilios Mitsis Poiitidis 8a73783a07 AOTIR: Use FEXCore::Allocator 2021-04-29 08:16:21 +03:00
Stefanos Kornilios Mitsis Poiitidis 32b4b02302 AOTIR: mmap based loading 2021-04-28 13:08:43 +03:00
Stefanos Kornilios Mitsis Poiitidis 528b01ad7a Merge pull request #988 from Sonicadvance1/memory_layout_optimizations
Memory layout optimizations
2021-04-28 12:31:53 +03:00
Stefanos Kornilios Mitsis Poiitidis cd0492a143 Merge pull request #994 from Sonicadvance1/enable_armv8.4
Enables ARMv8.4 CI runner
2021-04-28 09:53:47 +03:00
Ryan Houdek 26bf903b93 Enables ARMv8.4 CI runner
Requires #990 and #993 to be merged first
2021-04-27 23:16:39 -07:00
Stefanos Kornilios Mitsis Poiitidis aae4de106a Merge pull request #992 from Sonicadvance1/optimize_cpuid
Optimizes CPUID generation a bit
2021-04-28 09:12:09 +03:00
Stefanos Kornilios Mitsis Poiitidis c495d8120d Merge pull request #993 from Sonicadvance1/fix_sdl_missed_line
Oops: Missed line on FEXConfig SDL2
2021-04-28 09:10:52 +03:00
Stefanos Kornilios Mitsis Poiitidis a6d9425091 Merge pull request #990 from Sonicadvance1/fix_native_m1
Work around M1 Parallels hypervisor not showing CPU type
2021-04-28 09:09:08 +03:00
Ryan Houdek b06ebf486f Work around M1 Parallels hypervisor not showing CPU type
Parallels claims that the CPU is of implementor 0x41 and part number 0.
Easy enough to work around before real numbers get exposed
2021-04-27 22:26:35 -07:00
Ryan Houdek b17464d81a Oops: Missed line on FEXConfig SDL2 2021-04-27 16:51:12 -07:00
Ryan Houdek 2ab8a97055 Merge pull request #989 from Sonicadvance1/sdl2_fix
Only use SDL2 import target if it exists
2021-04-27 01:11:28 -07:00
Ryan Houdek 34a5eb6d6f Optimizes CPUID generation a bit
The flags calculation is fairly intensive and we were doing it for every core.
Do it once then duplicate it per core instead

Improves generation time quite significantly. A couple of milliseconds converted to half a millisecond
Slight improvement to startup time
2021-04-27 01:09:28 -07:00
Ryan Houdek 2a1a863c58 Only use SDL2 import target if it exists
Should fix Arch and Ubuntu 20.04 together
2021-04-27 00:45:20 -07:00
Ryan Houdek bab5927931 Merge pull request #981 from Sonicadvance1/optimize_long_divide
Adds Long Divide removal pass
2021-04-26 23:48:50 -07:00
Ryan Houdek 6255eba524 Memory layout optimizations
Some of these memory layouts weren't optimal for ARM loading.
This is now significantly improved
2021-04-26 23:48:22 -07:00
Ryan Houdek 37108058d1 Removes some unnecessary branching checks
It doesn't actually matter if the invalid node has has its linked list nodes changed
Faster to just set than to check while in a tight loop
2021-04-26 23:48:22 -07:00
Stefanos Kornilios Mitsis Poiitidis c3ad571062 Merge pull request #987 from Sonicadvance1/fix_assert_dumb_code
Changes LogMan assert to defines
2021-04-27 09:41:12 +03:00
Ryan Houdek 04e0baadc6 Changes LogMan assert to defines
C++ no-op functions can't optimize out the predicate arguments in all cases.
This was causing a problem where zero cost assertions weren't actually zero cost.

The only way to resolve this is to actually use macros sadly enough.

This will give a fairly hefty performance uplift with anything operating on IR.
2021-04-26 13:49:35 -07:00
Ryan Houdek 5ffbd97f01 IROp types can be marked const
Allows the compiler to know it can be optimized out without LTO
2021-04-26 13:45:37 -07:00
Ryan Houdek 7d4380fe6d Merge pull request #984 from FEX-Emu/skmp/relocation-fixes
IR: Fix RIP relocation edge-cases
2021-04-26 10:11:24 -07:00
Ryan Houdek 7d5157b602 Merge pull request #983 from FEX-Emu/skmp/faster-l1
LookupCache: Add new entries to L1C
2021-04-26 10:10:51 -07:00
Ryan Houdek e9a32da997 Merge pull request #986 from FEX-Emu/skmp/fix-20.04-build
Deps: Require xxhash 0.7.3, ubuntu 20.04 ships with that version
2021-04-26 10:05:35 -07:00
Stefanos Kornilios Mitsis Poiitidis b3564a4a48 IR: Fix RIP relocation edge-cases 2021-04-26 19:20:01 +03:00
Stefanos Kornilios Mitsis Poiitidis 2294419353 Deps: Require xxhash 0.7.3, ubuntu 20.04 ships with that version 2021-04-26 19:07:23 +03:00
Stefanos Kornilios Mitsis Poiitidis df8b78b327 Dispatcher: Use Also lookup L1C when dispatching interpreter 2021-04-26 19:06:11 +03:00
Stefanos Kornilios Mitsis Poiitidis 2ea6a2a141 LookupCache: Fix L1C to actually cache entries 2021-04-26 18:38:03 +03:00
Ryan Houdek db776fae4e Merge pull request #985 from FEX-Emu/skmp/rip-relative-compilation
IR: Remove Entry from OP_HEADER, pass as parameter to CompileCode
2021-04-25 18:27:37 -07:00
Stefanos Kornilios Mitsis Poiitidis b45b7c3441 IR: Remove Entry from OP_HEADER, pass as parameter to CompileCode 2021-04-25 13:15:23 +03:00
Stefanos Kornilios Mitsis Poiitidis 7805552edb LookupCache: Add new entries to L1C 2021-04-25 13:03:43 +03:00
Ryan Houdek 520dfd7edf Adds Long Divide removal pass
If the long divides don't have anything in the upper bits then they can be optimized away.
Teeworlds, SuperTuxKart, and RRootage were between 50%-55% getting removed
2021-04-24 23:10:59 -07:00
Stefanos Kornilios Mitsis Poiitidis 60671ee6cb Merge pull request #975 from Sonicadvance1/use_xxhash
Switch from fasthash64 to xxhash's XXH3
2021-04-23 09:49:06 +03:00
Stefanos Kornilios Mitsis Poiitidis 6635765ea9 Merge pull request #972 from Sonicadvance1/naked_mman
Naked mman usage removal
2021-04-22 09:47:41 +03:00
Stefanos Kornilios Mitsis Poiitidis 3e4ff23ab6 Merge pull request #977 from Sonicadvance1/fix_relative_elf
Fixes ELFCodeLoader2 loading relative ELF files
2021-04-22 09:40:08 +03:00
Ryan Houdek 93ed411899 Fixes ELFCodeLoader2 loading relative ELF files 2021-04-20 01:58:37 -07:00
Ryan Houdek 0b0db08ebf Switch from fasthash64 to xxhash's XXH3
Fixes #797
2021-04-18 18:13:27 -07:00
Stefanos Kornilios Mitsis Poiitidis 3419f40c00 Merge pull request #971 from Sonicadvance1/template_specialization
Declare missing template specialization for GetListIfExists
2021-04-19 00:03:25 +03:00
Stefanos Kornilios Mitsis Poiitidis 8260ddda13 Merge pull request #970 from Sonicadvance1/sdl2_target_imports
Use cmake SDL2 target properties
2021-04-19 00:02:59 +03:00
Stefanos Kornilios Mitsis Poiitidis 872e49f02a Merge pull request #969 from Sonicadvance1/mcpu_native
Use mcpu=native with Clang 12
2021-04-19 00:02:22 +03:00
Stefanos Kornilios Mitsis Poiitidis f0f6bc7a73 Merge pull request #973 from Sonicadvance1/fix_openat
Fixes OpenAt breaking with anonymous objects
2021-04-17 15:51:24 +03:00
Ryan Houdek 5820c251e8 Fixes OpenAt breaking with anonymous objects
Fixes #753
2021-04-16 21:21:08 -07:00
Ryan Houdek eff97509c0 Replaces naked mman usage in TestHarnessRunner 2021-04-16 21:01:59 -07:00
Ryan Houdek 3128d0c148 Replaces naked mman usage in SyscallHandler 2021-04-16 21:01:39 -07:00
Ryan Houdek 0216d7b552 Replaces naked mman usage in x86-64 syscalls 2021-04-16 21:01:07 -07:00
Ryan Houdek 87b473c7ee Replaces naked mman usage in IRLoader 2021-04-16 21:00:51 -07:00
Ryan Houdek f9d647c852 Replaces naked mman usage in HarnessHelpers 2021-04-16 21:00:19 -07:00
Ryan Houdek 2c175f1e0b Replaces naked mman usage in FEXLoader 2021-04-16 21:00:01 -07:00
Ryan Houdek 96813b0d6c Replaces naked mman usage in ELFCodeLoader 2021-04-16 20:59:27 -07:00
Ryan Houdek 967ac863be Replaces naked mman usage in X86Dispatcher 2021-04-16 20:50:14 -07:00
Ryan Houdek fa546fb492 Replaces naked mman usage in ELFSymbolDatabase 2021-04-16 20:50:09 -07:00
Ryan Houdek 99ab9864aa Replaces naked mman usage in X86HelperGen 2021-04-16 20:50:03 -07:00
Ryan Houdek 11246355e2 Replaces naked mman usage in LookupCache 2021-04-16 20:49:58 -07:00
Ryan Houdek 0d236940e0 Replaces naked mman usage in x86-64 JIT 2021-04-16 20:49:52 -07:00
Ryan Houdek a0afb4be5a Replaces naked mman usage in Arm64 JIT 2021-04-16 20:49:47 -07:00
Ryan Houdek ca45485d48 Adds new Allocator namespace for replace mmap and munmap
This is necessary for overriding these functions
2021-04-16 20:49:35 -07:00
Ryan Houdek c8bb0e2d51 Declare missing template specialization for GetListIfExists
Clang 12 picks up that this was missing
2021-04-16 18:54:16 -07:00
Ryan Houdek e9aeebb3a1 Use cmake SDL2 target properties
Some distros include generated cmake files instead of the two cmake files from the source.
Distros that use the generated cmake files only have SDL import properties, so we are required
to use those.
2021-04-16 18:45:06 -07:00
Ryan Houdek 1ad46c5af1 Use mcpu=native with Clang 12
Clang 12 fixes the bug with big.little configurations so we
can keep using mcpu=native past this point
2021-04-16 18:44:34 -07:00
Ryan Houdek 08bc7a8364 Merge pull request #967 from FEX-Emu/skmp/new-elf-loading
ELF: Simpler loader, uses mmap
2021-04-16 18:35:56 -07:00
Ryan Houdek e7179ff84b Merge pull request #968 from FEX-Emu/skmp/fix-self-exe-after-fork
Emulated Files: Don't precalculate /proc/pid path, it changes after fork
2021-04-15 13:01:41 -07:00
Stefanos Kornilios Mitsis Poiitidis 13bfdcad06 Emulated Files: Don't precalculate /proc/pid path, it changes after fork 2021-04-15 15:33:10 +03:00
Stefanos Kornilios Mitsis Poiitidis 38a707b38e Add missing functions to elfcodeloader2 2021-04-15 13:51:22 +03:00
Stefanos Kornilios Mitsis Poiitidis ada7915b01 Convert an assert to log 2021-04-15 13:40:28 +03:00
Stefanos Kornilios Mitsis Poiitidis 9775a2148e Fix rootfs interpreter loading 2021-04-15 12:16:33 +03:00
Stefanos Kornilios Mitsis Poiitidis bc8696828b More logging 2021-04-15 12:14:55 +03:00
Stefanos Kornilios Mitsis Poiitidis 7d792e65f0 Rootfs, assert fixes 2021-04-15 12:04:42 +03:00
Stefanos Kornilios Mitsis Poiitidis 14b5b881c5 Add more logging 2021-04-15 11:47:42 +03:00
Stefanos Kornilios Mitsis Poiitidis 830e41459c Fix build for test harness, irloader 2021-04-15 11:05:07 +03:00
Stefanos Kornilios Mitsis Poiitidis 0db99ea4f6 Use allocator for x32 2021-04-15 10:51:16 +03:00
Stefanos Kornilios Mitsis Poiitidis 22f1cf0e2a More wip 2021-04-15 10:05:59 +03:00
Stefanos Kornilios Mitsis Poiitidis 80493487db ELF Loading: Cleanups all around 2021-04-14 10:59:23 +03:00
Stefanos Kornilios Mitsis Poiitidis 59ce750af6 Elf: Add ELFCodeLoader2, with simplified loading logic 2021-04-13 15:59:36 +03:00
Stefanos Kornilios Mitsis Poiitidis 4ac81e22de Move some more files around 2021-04-13 15:59:36 +03:00
Stefanos Kornilios Mitsis Poiitidis a0a382cc5c Clear up file naming a big 2021-04-13 15:59:36 +03:00
Ryan Houdek 6cc7d065aa Merge pull request #964 from Sonicadvance1/paranoid_tso
First step in paranoid TSO mode
2021-04-12 23:34:54 -07:00
Ryan Houdek 93b9ca4bdb Merge pull request #965 from Sonicadvance1/add_jemalloc
Switches FEX over to using jemalloc
2021-04-08 14:04:12 -07:00
Ryan Houdek cc0c45cf47 Switches FEX over to using jemalloc
This is the first step that we need to do for correct 32-bit memory allocations.
2021-04-08 12:42:37 -07:00
Ryan Houdek 4a504bdd00 Merge pull request #939 from Sonicadvance1/hidden_visibility
Default hidden visibility and strip symbols
2021-04-06 18:56:28 -07:00
Ryan Houdek 66c8dd2013 First step in paranoid TSO mode
This moves vector ops down the GPR atomic backpatch path.
Next step after this is to add SIGBUS based handlers for these rather than backpatching.
Needs more work to treat aligned versus unaligned vector loadstores differently
2021-04-06 18:48:36 -07:00
Ryan Houdek 91ac4ccdd8 Adds LDAXP and STLXP backpatch handlers
This will be used in the next commit
2021-04-06 18:47:50 -07:00
Ryan Houdek 849b83bd02 Adds new ParanoidTSO option 2021-04-06 18:46:52 -07:00
Ryan Houdek 286f103dab Adds visibility attributes to everything we need to expose 2021-04-06 01:01:31 -07:00
Ryan Houdek 2a49037789 update external vixl 2021-04-06 00:39:03 -07:00
Ryan Houdek 235f6fbe39 Strip symbols and dead sections
These are unnecessary and just add some file bloat. Strip them out
2021-04-06 00:39:03 -07:00
Ryan Houdek b43db294ec Change our compilation visibility to hidden
This bloats our compilation with symbols that don't need to be visible
2021-04-06 00:39:03 -07:00
Stefanos Kornilios Mitsis Poiitidis 5e6c6adf8b Merge pull request #956 from Sonicadvance1/enable_gcc_32_tests
Enables GCC 32-bit unit tests
2021-04-06 09:48:38 +03:00
Stefanos Kornilios Mitsis Poiitidis 50c3ee047c Merge pull request #961 from Sonicadvance1/fix_segment_override
Fixes segment register override on string instructions.
2021-04-06 09:24:23 +03:00
Stefanos Kornilios Mitsis Poiitidis c0ef6da6a8 Merge pull request #960 from Sonicadvance1/more_cpuid
Adds some more CPUID functions
2021-04-06 09:23:13 +03:00
Stefanos Kornilios Mitsis Poiitidis 2063411443 Merge pull request #959 from Sonicadvance1/fix_tag_order
Fixes ordering problem of tag generation in release script
2021-04-06 09:22:12 +03:00
Stefanos Kornilios Mitsis Poiitidis 4a5eefec25 Merge pull request #958 from Sonicadvance1/fix_atomic_zext
Fixes Zext semantics on atomic ops with x64 JIT
2021-04-06 09:21:44 +03:00
Stefanos Kornilios Mitsis Poiitidis eaad9e99a9 Merge pull request #954 from Sonicadvance1/posix_tests_docs
Updates a couple POSIX test case failures
2021-04-06 09:18:18 +03:00
Stefanos Kornilios Mitsis Poiitidis 62406a45be Merge pull request #953 from Sonicadvance1/env_var_override
Allows overriding FEX folder locations with environment variables
2021-04-06 09:16:12 +03:00
Stefanos Kornilios Mitsis Poiitidis 03537d351d Merge pull request #952 from Sonicadvance1/add_asan_use_after_scope
Adds use-after-scope to the ASAN options
2021-04-06 09:09:09 +03:00
Ryan Houdek 0cb53fff89 Change kernel version error to not be fatal 2021-04-05 23:09:06 -07:00
Ryan Houdek 3e250fa071 Enables GCC 32-bit unit tests
Relies on #950 getting merged first
2021-04-05 23:09:06 -07:00
Stefanos Kornilios Mitsis Poiitidis 1d4b81f556 Merge pull request #951 from Sonicadvance1/fix_getenv_aotir
Removes getenv usage for aotir
2021-04-06 09:08:46 +03:00
Stefanos Kornilios Mitsis Poiitidis c26f19ed4d Merge pull request #950 from Sonicadvance1/fix_gcc_tests
Fixes GCC tests
2021-04-06 09:05:45 +03:00
Stefanos Kornilios Mitsis Poiitidis 9619cea097 Merge pull request #949 from Sonicadvance1/arm64_print
Adds support for Arm64 JIT to Print
2021-04-06 08:58:13 +03:00
Stefanos Kornilios Mitsis Poiitidis 44dbecc2ac Merge pull request #948 from Sonicadvance1/fix_leak
Fixes memory leak in CPU backends
2021-04-06 08:56:00 +03:00
Stefanos Kornilios Mitsis Poiitidis 415632209f Merge pull request #947 from Sonicadvance1/minimal_siginfo
Implements a minimal siginfo_t in the dispatcher's signal handler
2021-04-06 08:54:01 +03:00
Ryan Houdek b7e81e867d Fixes segment register override on string instructions.
The segment prefix only overrides one side of the string instruction.
Passing in the flags was causing the instruction to have the segment override on both sides of the copy.
So something like `movsb es:[rdi], fs:[rsi]` was being interpreted as `movsb fs:[rdi], fs:[rsi]`
Same for all the string ops which is now fixed.

We can't unit test these currently since we can't set host segment register for confirmed host behaviour.
2021-04-05 21:28:31 -07:00
Ryan Houdek 4f276aae82 Adds comment for LEA and segment registers
LEA doesn't give you the result with segment register applied to it.
Adds a comment to OpDispatcher to make it more clear
2021-04-05 21:28:25 -07:00
Ryan Houdek c80254f6f4 Adds some more CPUID functions
Noticed a game poking at these
2021-04-05 18:37:05 -07:00
Ryan Houdek 8adb1ca3a2 Fixes ordering problem of tag generation in release script
Wasn't actually creating an annotated tag with this order
2021-04-05 18:35:09 -07:00
Ryan Houdek 0dd1959d65 Fixes Zext semantics on atomic ops with x64 JIT
Fixes Factorio 1.1.30 on x86-64 hosts

Fixes #957
2021-04-05 18:19:15 -07:00
Ryan Houdek f4ab7cceab Updates a couple POSIX test case failures
The behaviour of these is broken but now they are documented in how they are broken
2021-04-05 04:27:52 -07:00
Ryan Houdek 8cc799ab65 Allows overriding FEX folder locations with environment variables
Introduces three new environment variables that need to live outside of the scope
of the regular argument loader path.
This will allow external applications to adjust FEX parameters in interesting ways.

FEX_APP_CONFIG: Allows you to override where Config.json lives
FEX_APP_CONFIG_LOCATION: Allows you to provide an entire folder of app profiles
FEX_APP_DATA_LOCATION: Allows you to override where the data gets stored and loaded from

Fixes #572
2021-04-05 04:09:41 -07:00
Ryan Houdek acb329b8e7 Adds use-after-scope to the ASAN options
Useful for seeing stack scoping problems that can crop up
2021-04-04 22:07:40 -07:00
Ryan Houdek 3020e39428 Remove DestSize from Print IR Op 2021-04-04 22:07:03 -07:00
Ryan Houdek 4a094c5dee Reenables new gcc unit tests that are no longer failing
Documents the few remaining tests with details as to why the test fails.
2021-04-04 22:05:55 -07:00
Ryan Houdek f8eabb4f33 Implements 64bit scalar VUMin on x86 JIT 2021-04-04 22:05:55 -07:00
Ryan Houdek 4da492d361 Fixes vector integer min and max on AArch64
These instructions don't have a 64bit element variant.
Switch the 64bit element variant over to a coded variant similar to float min/max
2021-04-04 22:05:55 -07:00
Ryan Houdek a200d2c91c Fixes packed shift ops
In the case of a shift value larger than the element size then the shift will zero the element.
Incoming shift amount is always a 64bit value which means that the value can become quite large on shifts.
Checks the incoming source and sets the maximum to be the element size, which works for AArch64's vector shifts
Which only operate on 8bit incoming shift amounts
2021-04-04 22:05:55 -07:00
Ryan Houdek f1663abe81 Removes getenv usage for aotir
We can't rely on `getenv("HOME")` always working.
Have the AOTIR code use the helper method for getting the data directory instead.
2021-04-04 21:43:14 -07:00
Ryan Houdek af205e9f5f Fixes PEXTRW
In the case of 16bit PEXTRW it will zext to 32bits which wasn't being done
2021-04-04 21:32:12 -07:00
Ryan Houdek 032d1fbba2 Fixes VFMIN/VFMax NaN behaviour
In a world with NaNs we were hitting some undefined behaviour with fmin/fmax
Needed to change these over to something more complex to handle the NaN case.

ARMv8.7 alternative FP mode would solve this for the scalar case, but it still doesn't work for the vecto case sadly.
2021-04-04 21:29:46 -07:00
Ryan Houdek 313c4bb784 Fixes bug in VectorImm
If passing in a 64bit immediate then movi doesn't behaviour how you would expect.

Instead to a tmp move + dup to implement it the way we want
2021-04-04 20:24:23 -07:00
Ryan Houdek ceb484fd3a Adds support for Arm64 JIT to Print
Useful for debugging, supports both vector and GPR
2021-04-04 20:22:44 -07:00
Ryan Houdek b98c7bd853 Moves AArch64's GetPhys to be a class function
No need for it to be static
2021-04-04 20:21:06 -07:00
Ryan Houdek 4c045148b6 Fixes memory leak in CPU backends
Dispatcher wasn't ever getting cleaned up.
Also ensures sharing it with the CompileService otherwise it crashes on use.
2021-04-04 20:19:35 -07:00
Ryan Houdek 9f29bba4aa Fixes missing virtual destructor on Dispatcher class 2021-04-04 20:10:58 -07:00
Ryan Houdek 2c592bd8a8 Implements a minimal siginfo_t in the dispatcher's signal handler
This lets Carrion get farther in game.
Seems like one of its threads hits sigsegv and it just...lets it die after checking siginfo?
2021-04-03 17:03:19 -07:00
Ryan Houdek 8319a85524 Merge pull request #946 from Sonicadvance1/fix_eventfd
Fixes eventfd and eventfd2
2021-04-03 06:52:32 -07:00
Ryan Houdek 8ad45071e4 Merge pull request #945 from Sonicadvance1/fix_truncate_creat
Fixes truncate and creat
2021-04-03 06:47:31 -07:00
Ryan Houdek 3379f3a8f9 Fixes eventfd and eventfd2
glibc function signature doesn't match kernel
2021-04-03 06:46:28 -07:00
Stefanos Kornilios Mitsis Poiitidis c76205040e Merge pull request #944 from Sonicadvance1/fix_clock_nanosleep
Fixes clock_nanosleep
2021-04-03 11:09:46 +03:00
Stefanos Kornilios Mitsis Poiitidis 648ef617cf Merge pull request #943 from Sonicadvance1/fix_fexconfig_unnamed_options
Fixes FEXConfig filling in unnamed options
2021-04-03 11:09:21 +03:00
Stefanos Kornilios Mitsis Poiitidis 5fac51f659 Merge pull request #942 from Sonicadvance1/implement_cmdline
Fixes wrapping cmdline arguments
2021-04-03 11:08:50 +03:00
Ryan Houdek 5d63dc179c Fixes truncate and creat
These were using the passthrough helper which doesn't was breaking the result value
2021-04-02 20:45:33 -07:00
Ryan Houdek 858566c356 Fixes clock_nanosleep
glibc version behaves slightly differently than kernel
2021-04-02 20:41:04 -07:00
Ryan Houdek fb94e62df1 Fixes FEXConfig filling in unnamed options
These aren't meant to be set by the user
2021-04-02 20:28:12 -07:00
Ryan Houdek 51e4fe7f0a Fixes wrapping cmdline arguments
In case an application checks this
2021-04-02 20:26:28 -07:00
Stefanos Kornilios Mitsis Poiitidis be03767c0b Merge pull request #941 from Sonicadvance1/pselect6_fix
Fixes pselect6 signal arg pack argument
2021-04-02 23:42:02 +03:00
Stefanos Kornilios Mitsis Poiitidis adeea56ea8 Merge pull request #940 from Sonicadvance1/update_release_script
Updates release script to match what I want it to be
2021-04-02 23:41:17 +03:00
Ryan Houdek 6923c47a70 Fixes pselect6 signal arg pack argument
This doesn't exactly match the glibc call so do the syscall directly
2021-04-02 13:23:07 -07:00
Ryan Houdek 26d9ddef9f Updates release script to match what I want it to be
Generates the current and previous tag automatically.
Checks to ensure the previous tag exists and that the current tag does not
2021-04-02 11:49:19 -07:00
Ryan Houdek c6c2555e1b Merge pull request #938 from Sonicadvance1/FEXConfig_improvements
FEXConfig improvements
2021-04-01 23:55:16 -07:00
Stefanos Kornilios Mitsis Poiitidis 283a5c1293 Merge pull request #937 from Sonicadvance1/fix_inverted_bool
Fixes inverted boolean FEXLoader arguments
2021-04-02 09:38:21 +03:00
Stefanos Kornilios Mitsis Poiitidis b31976d0e2 Merge pull request #936 from Sonicadvance1/fix_cp_config_error
Fixes Thunk Guest libs short arg. C&P error made it not be j
2021-04-02 09:36:53 +03:00
Ryan Houdek f922e6420b Allow opening the first configuration file passed in
Lets us easily open any configuration file
2021-04-01 19:25:17 -07:00
Ryan Houdek 7ff06c828f Loads default config if doesn't exist
Allows us to save a folder with the default if we want it
2021-04-01 19:22:53 -07:00
Ryan Houdek be16cc1456 Fixes window ordering so popup message is visible without config open
This was a child of the config popup before.
Moves it to a child of the workspace instead so that if we had a load error the message is still visible
2021-04-01 19:16:03 -07:00
Ryan Houdek 59df3d13cc Don't set up RootFS folder or inotify thread on config load failure
Can happen in the case that the default file tries to get loaded and doens't exist
2021-04-01 19:15:08 -07:00
Ryan Houdek 1adf518e6f Don't try creating inotify thread twice
If the inotify FD is already opened then just early exit
Can happen in the case that a config tries to get opened and didn't exist
2021-04-01 19:13:43 -07:00
Ryan Houdek 68171ad9b5 Creates RootFS data folder as a user convenience
To make sure users don't mess this up, create the folder if it isn't found
2021-04-01 19:12:56 -07:00
Ryan Houdek 78a923b5b0 Fixes inverted boolean FEXLoader arguments
The default_value doesn't have anything to do with the boolean argument being set.
Setting a boolean argument will always set true, inverted always sets false.
We don't currently have a need to support the inverted case where passing in an argument sets it to false
2021-04-01 19:10:29 -07:00
Ryan Houdek 0c6741438b Update config generator python to check for duplicate options
Checks for duplicate short and long option arguments
2021-04-01 19:08:19 -07:00
Ryan Houdek 5d0c080a9d Fixes Thunk Guest libs short arg. C&P error made it not be j 2021-04-01 19:08:00 -07:00
Ryan Houdek 5f49b57948 Merge pull request #928 from Sonicadvance1/pthreads_threading
Switches FEXCore over to pthreads implementation
2021-04-01 01:15:43 -07:00
Ryan Houdek 38778953a1 Merge pull request #924 from Sonicadvance1/select_named_rootfs
Adds support for Named RootFS folders in FEXConfig
2021-04-01 00:32:15 -07:00
Ryan Houdek 21a364c2ad Switches FEXCore over to pthreads implementation
This defaults to a pthread implementation but it can be switched over to a custom thread
handler if the frontend desires.
2021-04-01 00:31:45 -07:00
Ryan Houdek 3294cc209f Adds support for Named RootFS folders in FEXConfig
Provides a drop down dialog of RootFS folders to select.
Will be empty if the user doesn't have any folders in place.
Just allowing directory paths to be inserted

Fixes #689
2021-04-01 00:15:29 -07:00
Stefanos Kornilios Mitsis Poiitidis ae5d41e46b Merge pull request #926 from Sonicadvance1/disable_seccomp
Disables seccomp
2021-04-01 10:03:36 +03:00
Ryan Houdek 43f83fd6d2 Merge pull request #923 from Sonicadvance1/named_rootfs
Adds support for named rootfs configurations
2021-04-01 00:02:11 -07:00
Ryan Houdek 8cf47f8221 Adds support for named rootfs configurations
If the relative or absolute folder doesn't exist then FEX will search in the data folder for a rootFS named the same thing
If the path exists then it is used.

For example if my data folder contains `$HOME/.fex-emu/RootFS/Ubuntu_main/` and I set the RootFS option to `Ubuntu_main`
Then this rootfs will be used. Allows you to easily select a rootfs in that folder without having the full file path.
2021-03-31 23:46:07 -07:00
Ryan Houdek 43b6cc1cb9 Adds GetDataDirectory config helper
This ends up in either $HOME/.fex-emu/ or $XDG_DATA_HOME/.fex-emu/
2021-03-31 23:46:07 -07:00
Ryan Houdek 1ab6726498 Moves Config directory finding to FEXCore
This will be necessary in the next commit
2021-03-31 23:46:07 -07:00
Ryan Houdek c21acd0d45 Merge pull request #916 from Sonicadvance1/fix_execve_again
Fix execve again
2021-03-31 23:45:06 -07:00
Ryan Houdek e637751112 ptrace behaviour is now changed 2021-03-31 23:38:14 -07:00
Ryan Houdek fac6377ed3 Fixes a crash in openat 2021-03-31 23:37:57 -07:00
Ryan Houdek 4f3cb933cb Fixes execve 2021-03-31 23:37:56 -07:00
Ryan Houdek b2825bd848 Adds handler in ELFLoader to determine ELF type. 2021-03-31 23:37:56 -07:00
Ryan Houdek 83eb7f4ad3 Return EPERM in ptrace. A Feral launcher is attempting to use this to ensure it can't ptrace. 2021-03-31 23:37:56 -07:00
Ryan Houdek c3854c211a Fix finding home directory when we have no environment variables. 2021-03-31 23:37:56 -07:00
Ryan Houdek 61cd3eb3ce Fix crash in ELFDB if it tried loading something that wasn't an ELF 2021-03-31 23:37:56 -07:00
Ryan Houdek 490352f568 Merge pull request #921 from Sonicadvance1/update_imgui
Updates external imgui which fixes keypad enter in FEXConfig
2021-03-31 23:24:42 -07:00
Ryan Houdek 4004d5a3b7 Disables seccomp
FEX doesn't support seccomp in userspace and allowing these through causes chromium secure sandbox to break.
Disabling these with EINVAL allows FEX to behave as if seccomp isn't enabled in the kernel config

Necessary to get the Civ6 launcher further
2021-03-31 23:23:40 -07:00
Ryan Houdek 761447467e Updates external imgui which fixes keypad enter in FEXConfig 2021-03-30 15:39:11 -07:00
Ryan Houdek 3f38ed94ca Merge pull request #922 from Sonicadvance1/disable_oomscore
Disables gvisor test proc_pid_oomscore_test
2021-03-30 15:38:33 -07:00
Ryan Houdek ad877d4088 Disables gvisor test proc_pid_oomscore_test
Depending on runner this passes or fails.
The test expects to be able to open `/proc/self/oom_score_adj` as writable to adjust the oom score.
This is expected to fail on all three of our runners, but sometimes the solidrun board manages to open it.
Disable as it is a flake. Could be a kernel bug on the solid run board
2021-03-30 15:29:52 -07:00
Ryan Houdek 92fc6b1909 Merge pull request #920 from lioncash/insert
Config: Make lookup map assignment match behavior of comment
2021-03-30 14:17:37 -07:00
Lioncache 4b11dcb72a Config: Make lookup map assignment match behavior of comment
emplace() only performs an insertion or assignment if the key doesn't
already exist within the map, which is at odds with what the comment
above the line indicates should happen.

insert_or_assign() better models what the comment indicates.
2021-03-30 11:51:52 -04:00
Lioncache dd7c78dcd7 Config: Perform lookups by character overloads where applicable
These are marginally better to perform than the string equivalents
2021-03-30 11:41:57 -04:00
Lioncache a20ef403c1 Config: std::move strings within MergeEnvironmentVariables()
Same behavior, minus potential heap duplicate allocations
2021-03-30 11:40:42 -04:00
Ryan Houdek be06511d3d Merge pull request #919 from FEX-Emu/skmp/fix-build
Build: Fix for ubuntu 20.04
2021-03-30 03:20:43 -07:00
Stefanos Kornilios Mitsis Poiitidis b7a60337e8 Build: Fix for ubuntu 20.04 2021-03-30 13:08:19 +03:00
Stefanos Kornilios Mitsis Poiitidis 315b8e9c1e Merge pull request #917 from FEX-Emu/skmp/lightweight-docs-3
Docs: Add tags, unittest Readme.md, generated SourceOutline.md
2021-03-30 12:52:18 +03:00
Ryan Houdek 155a2b0194 Merge pull request #918 from lioncash/fallthrough
CMakeLists: Flag unannotated implicit fallthrough as an error
2021-03-30 02:37:07 -07:00
Stefanos Kornilios Mitsis Poiitidis 8c874f4540 Docs: Commit generated SourceOutline.md, update Readme.md 2021-03-30 12:21:18 +03:00
Stefanos Kornilios Mitsis Poiitidis 942b8549c6 Docs: Add tags to the source code 2021-03-30 12:21:18 +03:00
Ryan Houdek fa577b4527 Merge pull request #910 from FEX-Emu/skmp/lightweight-docs
Docs: lightweight doc autogen
2021-03-30 02:17:41 -07:00
Stefanos Kornilios Mitsis Poiitidis 15537f8c8c Scripts: Also add unittests folder for doc outline 2021-03-30 11:58:18 +03:00
Lioncache 434a74a88a CMakeLists: Flag unannotated implicit fallthrough as an error
Prevents a class of sneaky logic bugs from slipping through into the
codebase.

This also resolves a case of such a bug within the Decorder's ReadData()
where all 3 byte reads would be performed as if they were a 4 byte read.
2021-03-30 04:56:12 -04:00
Stefanos Kornilios Mitsis Poiitidis 1fdd6fbb14 Scripts: Add a documentation comment in changelog_generator.py 2021-03-30 11:55:39 +03:00
Stefanos Kornilios Mitsis Poiitidis 458bebf598 Scripts: Add a documentation comment in doc_outline_generator.py 2021-03-30 11:53:11 +03:00
Stefanos Kornilios Mitsis Poiitidis 2b10b9792b Scripts: Fix generate_release, don't use markdown for changelogs 2021-03-30 11:23:00 +03:00
Stefanos Kornilios Mitsis Poiitidis 0dd02da57c Docs: Update outline scripts 2021-03-30 11:23:00 +03:00
Stefanos Kornilios Mitsis Poiitidis 309139e203 Scripts: Add generate_release script 2021-03-30 11:23:00 +03:00
Stefanos Kornilios Mitsis Poiitidis 350c33ada4 Docs: Update outline script 2021-03-30 11:23:00 +03:00
Stefanos Kornilios Mitsis Poiitidis bdd35e5743 docs: Update outline generator 2021-03-30 11:23:00 +03:00
Stefanos Kornilios Mitsis Poiitidis ceb7082e37 Docs/Versioning: Add Changelog Generator 2021-03-30 11:23:00 +03:00
Stefanos Kornilios Mitsis Poiitidis 2d6dc80039 Docs: Adds tag-based outline generator w/ glossary support 2021-03-30 11:22:56 +03:00
Ryan Houdek c92df21627 Merge pull request #915 from lioncash/fallthrough
OpcodeDispatcher: Add missing break for UD2 in INTOp()
2021-03-29 22:56:19 -07:00
Lioncache 539de0492e OpcodeDispatcher: Add missing break for UD2 in INTOp()
Fixes a case where the reason gets overwritten.
2021-03-29 23:42:26 -04:00
Ryan Houdek efd41de8ea Merge pull request #914 from lioncash/optional
Context: Make use of std::optional with GetFilenameHash()
2021-03-29 16:44:08 -07:00
Lioncache 99ae862875 Context: Eliminate undefined behavior in LoadEntryList()
This breaks the strict-aliasing rule. We can use std::memcpy here to
make this code well-defined.
2021-03-29 12:17:36 -04:00
Lioncache b81ea43601 Context: Make use of std::optional with GetFilenameHash()
Same behavior, minus the need for an out parameter. While we're at it,
we can tidy up some of the file handling code.
2021-03-29 12:17:32 -04:00
Ryan Houdek fcd4974cff Merge pull request #912 from Sonicadvance1/config_docs_improvements
Configuration option improvements
2021-03-29 04:47:45 -07:00
Stefanos Kornilios Mitsis Poiitidis 8550c9f2dd Merge pull request #913 from Sonicadvance1/fix_32bit_mask
Adds a 32-bit mask for multiblock RIP calculation
2021-03-29 14:35:32 +03:00
Ryan Houdek 15dd70487f Removes legacy difference between FEX config env variable and enum names 2021-03-29 00:26:11 -07:00
Ryan Houdek 8fad0c8fdb Adds a 32-bit mask for multiblock RIP calculation
In the case of overflow then it'll just mask this result.
The rest of the logic already matches what 32-bit expects.

Fixes #703
2021-03-28 23:39:53 -07:00
Ryan Houdek b6c0a9f2a0 Configuration option improvements
This commit does three things that are bounded to each other
1) Moves configs from ConfigValues.inl to Config.json
2) Uses the json to generate a man file with the options inside of it
3) Generates the code required for FEXLoader to automatically parse defined options

Moving the configuration options to a parseable format was required to generate the man pages.
Can't really include an inl file in the man page
Man page gets generated and installed through the regular cmake install process
`man FEX` to get the man page

With this change, FEXLoader's argument parsing now will automatically be generated from the json.
This way whatever is in the json file matches what is in the man page, in the json, AND what is returned in FEXLoader --help.
It will stay in sync now.

FEXConfig is the only application that stays out of sync for now as it requires some more thought to plan out.

A minor improvement that this brings as well is that every boolean option that has a long argument also gains the inversion of that property.
aka, `--gdb` also gains `--no-gdb` The use case for this is minor but boolean arguments should always allow negated variants.
2021-03-28 23:28:41 -07:00
Ryan Houdek b5a6dd031b Merge pull request #911 from Sonicadvance1/std_size_life
Replaces FEX's usage of classic C array element count with std::size
2021-03-28 05:58:55 -07:00
Ryan Houdek 650877adca Replaces FEX's usage of classic C array element count with std::size
Just learned that this is a c++17 feature and it's a nice little
cleanup.

Only one remains in our codebase which can't easily be replaced
2021-03-28 05:39:35 -07:00
Ryan Houdek 432b6fccf2 Merge pull request #907 from Sonicadvance1/replace_glfw
Replaces FEXConfig usage of GLFW with SDL2
2021-03-27 05:53:00 -07:00
Ryan Houdek 47f3c983ee Merge pull request #905 from Sonicadvance1/removes_warnings_aarch64
Removes remaining warnings from AArch64 JIT.
2021-03-27 05:52:51 -07:00
Ryan Houdek 9565247ada Merge pull request #904 from Sonicadvance1/stop_compiling_twice
Stop compiling FEXCore twice
2021-03-27 05:52:39 -07:00
Ryan Houdek c59b9fbede Merge pull request #903 from Sonicadvance1/fix_silent_logs
Disables silent logging on unit tests
2021-03-27 05:52:28 -07:00
Ryan Houdek 7c96893579 Updates README with SDL2 requirement 2021-03-27 05:26:00 -07:00
Ryan Houdek 8ebef457fc Replaces FEXConfig usage of GLFW with SDL2
Sometimes GLFW3 can just consume a full CPU core, spinning on nothing.

Fixes #906
2021-03-27 05:23:58 -07:00
Ryan Houdek 9eee879be0 Removes remaining warnings from AArch64 JIT.
There are a couple of warnings remaining but need restructuring to solve.
I believe @phire is going to fix the one warning about a virtual destructor in a upcoming PR.
Fixes #679
2021-03-26 18:49:40 -07:00
Ryan Houdek 64affa8c8e Stop compiling FEXCore twice
This now creates an object binary then static and shared libraries from that.
This stops us outputting warnings twice in FEXCore.
Also improves compilation time even with ccache enabled.
Went from 12s to 8s compilation WITH ccache.
I'm sure without ccache it is even better.
2021-03-26 18:27:23 -07:00
Ryan Houdek dc041bdf0e Disables silent logging on unit tests
We need these for our CI artifacts
2021-03-26 18:04:17 -07:00
Ryan Houdek 51c43a0761 Adds option to disable silent logging 2021-03-26 18:00:23 -07:00
Ryan Houdek aa67142e6f Merge pull request #902 from Azkali/patch-1
Dockerfile add arm64 compatibility
2021-03-25 22:36:41 -07:00
The Great Wizard Azkali ae569da895 Update Dockerfile 2021-03-26 06:30:17 +01:00
The Great Wizard Azkali d77acdf474 Dockerfile add arm64 compatibility
Python3, Python3-dev and linux-headers prevent from building on arm64
2021-03-26 02:18:13 +01:00
Ryan Houdek 264ec44276 Merge pull request #892 from Sonicadvance1/install_thunks
Allows installing of FEXThunks in our data directory
2021-03-25 14:50:54 -07:00
Ryan Houdek da49eb3394 Allows installing of FEXThunks in our data directory
This is necessary for building FEX packages that contain some initial thunk libs.
Gives an initial foothold for a default location for the host and guest thunk folders
2021-03-24 03:27:38 -07:00
Ryan Houdek 5447ec3ec8 Merge pull request #884 from Sonicadvance1/fix_empty_string_crash
Fixes crash on empty path config string
2021-03-24 02:34:20 -07:00
Ryan Houdek ff3974c4a1 Merge pull request #890 from Sonicadvance1/merged_environment_variables
Fixes environment variable configuration from multiple layers
2021-03-24 02:34:14 -07:00
Ryan Houdek 358f12e074 Merge pull request #891 from Sonicadvance1/installed_global_app_configs
Implements support for installing global application profiles
2021-03-24 02:34:00 -07:00
Ryan Houdek c5648ac84c Implements support for installing global application profiles
These need to be used sparingly, we don't want to proactively crush user options.
2021-03-23 22:34:01 -07:00
Ryan Houdek 30e1f871ad Fixes environment variable configuration from multiple layers
This was a missing feature that I had skipped previously.
Before this commit, each layer would overwrite all previous environment variables if they had anything defined.

After this commit, the layers will now merge in priority order.
Meaning if a higher priority layer has the same environment variable defined, it will overwrite that specific variable
Ending up with a superset of all the layer's environment variables now.
2021-03-23 22:01:09 -07:00
Ryan Houdek 5c624684b7 Merge pull request #887 from Sonicadvance1/FEXBashLoader
Adds a FEXBash helper program
2021-03-23 21:37:45 -07:00
Ryan Houdek ef11b534ef Adds a FEXBash helper program
This is a helper program to execute a bash command through FEX.
Works around an edge case of
eg:
FEXInterpreter /bin/sh -c "echo A"
versus
FEXBash "echo A"
Argument expansion ends up being a pain point
This isn't currently used but will be very shortly
2021-03-23 21:27:30 -07:00
Ryan Houdek cd32eeb428 Adds FEXInterpreter as a build target
Makes it easier to have FEXInterpreter around
2021-03-23 21:27:29 -07:00
Ryan Houdek 43984c810d Merge pull request #886 from Sonicadvance1/namespace_perm
Returns EPERM on clone with namespace
2021-03-23 20:56:20 -07:00
Ryan Houdek a50689aa8b Merge pull request #885 from Sonicadvance1/cpuid_leaf
Adds support for CPUID leafs
2021-03-23 20:55:33 -07:00
Scott Mansell 311d6d4385 Merge pull request #883 from phire/thread_cleanup
Cleanup threads when they exit
2021-03-24 16:22:15 +13:00
Ryan Houdek a19c59f5b2 Merge pull request #888 from Sonicadvance1/default_silentlogs
Default to silent logging
2021-03-23 20:13:07 -07:00
Scott Mansell 6a8f68022f Join threads after fork.
Also, add some comments to document things and
rename function to be clear about it's purpose.
2021-03-24 15:41:09 +13:00
Ryan Houdek fdcb6206da Default to silent logging
Useful for debugging but we need to be silent by default now.
Without this anything that checks stdout output of applications from bash would fail

Steam with lspci, lsusb, uname, etc
2021-03-23 19:30:42 -07:00
Ryan Houdek 6ea977ba34 Remove proc_pid_uid_gid_map from known failures list
Now instead of crashing with namespacing, it skips the tests if it gets EPERM.
2021-03-23 19:28:12 -07:00
Ryan Houdek 9b35cb4408 Returns EPERM on clone with namespace
Notably this allows applications to work that don't require the namespace but check up front if they are able to clone with it.
Civ 6's launcher checks this as an example
2021-03-23 19:28:06 -07:00
Ryan Houdek afa869e1f2 Adds Config.h generated file
Gives us the install path and FEXInterpreter locations
2021-03-23 19:12:18 -07:00
Ryan Houdek 43ddba4e82 Adds support for CPUID leafs
Leafs come from ECX but only some CPUID functions support this.
This adds the initial infrastructure but doesn't yet add support for the CPUID functions to consume the leaf.
2021-03-23 18:13:14 -07:00
Ryan Houdek e3380957ba Fixes crash on empty path config string
If the config exists as an empty string then this would crash
2021-03-23 18:02:56 -07:00
Scott Mansell 21687f4dc5 Also destroy the parent thread 2021-03-24 03:14:23 +13:00
Scott Mansell 41a1a9d400 Cleanup threads when they exit 2021-03-24 03:14:23 +13:00
Stefanos Kornilios Mitsis Poiitidis 82c2b49a2e Merge pull request #878 from Sonicadvance1/fexconfig_actual_default
Changes FEXConfig to actually load default configuration
2021-03-22 18:24:57 +02:00
Stefanos Kornilios Mitsis Poiitidis fbc49c1648 Merge pull request #876 from Sonicadvance1/QOL_config
Implements a couple quality of life configuration option handling
2021-03-22 18:23:52 +02:00
Stefanos Kornilios Mitsis Poiitidis d2f636893d Merge pull request #880 from Sonicadvance1/github_label_testing
Support disabling unit tests based on runner label
2021-03-22 18:18:08 +02:00
Ryan Houdek e5a9bd4d3e Fixes incorrect result on cmpxchg reg, reg failure when regsize = 32bit 2021-03-22 09:03:48 -07:00
Ryan Houdek ca24ea1d36 Fixes missing HandledLock on XCHG 2021-03-22 09:03:48 -07:00
Ryan Houdek 998e53aa70 Only disabled unaligned atomics test on ARMv8.0 2021-03-22 09:03:48 -07:00
Ryan Houdek de890e7387 Have unit tests check for runner label 2021-03-22 09:03:48 -07:00
Ryan Houdek 042a71be96 Get the runner's label for testing disabled tests later 2021-03-22 09:03:48 -07:00
Ryan Houdek 0a61741596 special case logging option stdout/stderr 2021-03-21 02:26:56 -07:00
Ryan Houdek 91a5a625ac Implements a couple quality of life configuration option handling
This takes the changes from #829 and moves it to the correct location to be picked up from any loader.
This also fixes #873.
Expands the config paths. Anything that has ~ or is relative will be converted to an absolute path.
2021-03-21 02:25:30 -07:00
Stefanos Kornilios Mitsis Poiitidis f68593ea97 Merge pull request #877 from Sonicadvance1/fix_absolute_linker
Adds some additional logic for finding absolute soft linked linker
2021-03-21 11:08:46 +02:00
Stefanos Kornilios Mitsis Poiitidis e15730782b Merge pull request #879 from Sonicadvance1/update_readme
Updates readme with some more direct information
2021-03-21 11:07:35 +02:00
Ryan Houdek fc27893f20 Updates readme with some more direct information
Some of this taken from Stef's recent changes. Some taken from the wiki.
combination of the two best bits
2021-03-21 01:53:51 -07:00
Ryan Houdek 5b8bf8dd24 Adds some additional logic for finding absolute soft linked linker
Ubuntu's x86_64 rootfs does a soft link from `/lib64/ld-linux-x86-64.so.2` to an absolute path of `/lib/x86_64-linux-gnu/ld-2.32.so`
Our frontend ELF loader logic would resolve this symlink to be just `/lib/x86_64-linux-gnu/ld-2.32.so` which would then fail.
Adds some additional logic to our frontend that if the symlink ends up being a softlink to an absolute address then it'll first resolve that symlink.
Then combines rootfs + absolute path. If that still fails then it'll fall down the regular linker path, which could still find the linker on the host side.

This is all a bit of a kludge to just work around Ubuntu doing an absolute address rather than a relative one.

Fixes #869
2021-03-21 01:11:47 -07:00
Ryan Houdek 0c48f1409e Changes FEXConfig to actually load default configuration
This was previously just some default values that I put in as placeholder
Now that we actually have defaults configured somewhere, use those.

Fixed #874
2021-03-21 00:48:51 -07:00
Ryan Houdek 8c1f2eb0b3 Merge pull request #870 from FEX-Emu/skmp/lock-validation
OpDisp: Validate LOCK handling, add missing segment offsets
2021-03-20 15:53:06 -07:00
Ryan Houdek 07613456d2 Merge pull request #872 from FEX-Emu/skmp/x87-fixes
X87: Init on X87FNSAVE, fix FNINIT
2021-03-20 15:52:54 -07:00
Ryan Houdek 493bb3bef3 Merge pull request #875 from phire/test-no-multibock
Explictly as for --no-multiblock in tests
2021-03-20 15:52:44 -07:00
Scott Mansell cf8572e090 Explictly as for --no-multiblock in tests
We switched the default over a while back, so we haven't
been getting test coverage with multiblock off
2021-03-21 04:57:47 +13:00
Stefanos Kornilios Mitsis Poiitidis d120f12c93 X87: Init on X87FNSAVE, fix FNINIT 2021-03-19 17:04:51 +02:00
Stefanos Kornilios Mitsis Poiitidis 695322d446 OpDisp: Validate LOCK handling, add missing segment offsets 2021-03-19 16:32:29 +02:00
Stefanos Kornilios Mitsis Poiitidis d34cde12ae Merge pull request #850 from Sonicadvance1/struct_verifier
libclang based Struct verifier written in python
2021-03-19 10:06:34 +02:00
Stefanos Kornilios Mitsis Poiitidis a7dc9d00d2 Merge pull request #864 from Sonicadvance1/cpuid_15h
Implements CPUID 15h
2021-03-19 10:02:02 +02:00
Ryan Houdek dbde2e400e Merge pull request #863 from Sonicadvance1/enable_invariant_tsc
Enables Invariant TSC CPUID bit
2021-03-18 12:39:08 -07:00
Ryan Houdek bdeefa7bf0 Merge pull request #861 from Sonicadvance1/add_version_config
Implements a --version argument
2021-03-18 12:39:00 -07:00
Ryan Houdek 51e513df14 Merge pull request #865 from FEX-Emu/skmp/fix-emufiles-lock
EmulatedFiles: Lock around FDToNameMap accesses
2021-03-18 04:11:58 -07:00
Stefanos Kornilios Mitsis Poiitidis 0106b362d6 EmulatedFiles: Lock around FDToNameMap accesses 2021-03-18 12:45:27 +02:00
Ryan Houdek c7f265158f Implements CPUID 15h
ARMv8 allows you query the cycle counter register very from userspace which allows us to emulate this easily.

My IceLake device returns 1.5Ghz through this interface
My AMD Zen+ device doesn't support this but with measurements has 3Ghz TSC
My Snapdragon 865 device has a TSC frequency of 19.20Mhz
2021-03-18 01:42:44 -07:00
Ryan Houdek fbf5325bdd Enables Invariant TSC CPUID bit
I keep forgetting to enable this bit. Fixes #855
2021-03-18 00:15:48 -07:00
Ryan Houdek 9b2bd8df28 Implements a --version argument
Nicer for the user to determine what their version is
2021-03-17 00:00:57 -07:00
Ryan Houdek 2a074204bc Merge pull request #859 from Sonicadvance1/improve_fexinterpreter_msg
Adds an installer message to the FEXInterpreter install
2021-03-16 23:13:12 -07:00
Ryan Houdek d79fb37135 Merge pull request #858 from Sonicadvance1/remove_testharness
Removes TestHarness
2021-03-16 23:13:03 -07:00
Ryan Houdek d01b40c5aa Adds an installer message to the FEXInterpreter install
Otherwise it doesn't feel like FEXInterpreter is installing
2021-03-16 22:36:58 -07:00
Ryan Houdek e871a43387 Merge pull request #857 from phire/deletething
Remove old and unused HostCore
2021-03-16 22:30:30 -07:00
Ryan Houdek ff45b37904 Merge pull request #856 from Sonicadvance1/remove_libcap
Removes libcap-dev dependency
2021-03-16 22:30:21 -07:00
Ryan Houdek 62aab57ef7 Removes TestHarness
This has been completely deprecated for favour of the TestHarnessRunner instead.
Is unused.
2021-03-16 22:14:49 -07:00
Scott Mansell 208fa0d1fc Remove old and unused HostCore
The thing we are actually using is Source/CommonCore/HostFactory.cpp
2021-03-17 18:09:49 +13:00
Ryan Houdek 634d4fead0 Removes libcap-dev dependency
We are only using this for syscalls getcap and setcap.
These are only passthrough pointers and don't need to be handled from a library.

Noticed this while writing user documentation
2021-03-16 22:08:15 -07:00
Ryan Houdek 0bdddedfe6 Merge pull request #853 from FEX-Emu/skmp/dont-lse-though-syscalls
RCLSE: Invalidate around OP_SYSCALLs, Syscalls might read the context
2021-03-16 17:54:54 -07:00
Stefanos Kornilios Mitsis Poiitidis 971740991b RCLSE: Invalidate around OP_SYSCALLs, Syscalls might read the context and need the latest version of it
Fixes steam w/ all optimization passes enabled
2021-03-17 02:42:32 +02:00
Ryan Houdek cb672e035c Merge pull request #852 from Sonicadvance1/fix_fexconfig_idle
Fixes FEXConfig trying to update full refresh
2021-03-16 00:53:40 -07:00
Ryan Houdek 4f64ba582c Patch up the remaining 32bit structs 2021-03-15 15:51:59 -07:00
Ryan Houdek fbe2583a04 Fix truncating to not set the log files to 21MB 2021-03-15 15:24:49 -07:00
Ryan Houdek befe9dcbae Adds struct verifier to github yaml workflow file
This way CI tests this on each commit
2021-03-15 15:24:49 -07:00
Ryan Houdek aacb0f6891 Adds new struct_verifier ctest to cmake
Currently only testing 32bit syscall struct definitions
2021-03-15 15:24:49 -07:00
Ryan Houdek 040cc746e7 Fixes FEXConfig trying to update full refresh
This burns a decent amount of CPU time just idling and we don't have active screen elements to matter here
It's still responsive because it'll update as the window gets any events
2021-03-15 09:46:49 -07:00
Ryan Houdek e0c5840f2c Adds libclang based struct verifier
This requires multiarch on the targets to work.
Will run a header through multiple architectures and ensure that the struct packing all works
2021-03-15 06:58:06 -07:00
Ryan Houdek 48027f1d2e Moves BUILD_TESTS check up the list
This way Source/ can have its own tests
2021-03-15 06:58:06 -07:00
Ryan Houdek 9dc0717cdb Attributes a few 32bit syscall types
This will be necessary for struct verification in the next commit
rusage needed to be updated to match the real rusage. Has unions with two named types
2021-03-15 06:58:06 -07:00
Ryan Houdek 8de7dd8fd0 Merge pull request #848 from Sonicadvance1/remove_warnings
Remove most warnings from FEX
2021-03-15 06:57:42 -07:00
Ryan Houdek 79e9477b14 Removes warnings from RAPass 2021-03-14 13:27:40 -07:00
Ryan Houdek 0b8f29000e Removes warnings from ConstProp 2021-03-14 13:27:39 -07:00
Ryan Houdek e530e3676f Removes warnings from IRDumper 2021-03-14 13:27:39 -07:00
Ryan Houdek 0c5e5dbdf2 Removes warnings from OpcodeDispatcher 2021-03-14 13:27:39 -07:00
Ryan Houdek d8591a8f14 Removes warnings from x86 JITCore 2021-03-14 13:27:39 -07:00
Ryan Houdek 941107120c Removes warnings from x86 BranchOps 2021-03-14 13:27:39 -07:00
Ryan Houdek ffc17c90c5 Removes warnings from InterpreterOps 2021-03-14 13:27:39 -07:00
Ryan Houdek 5285f4baa6 Adds CMake option to enable -Werror
We aren't error free so can't be default enabled
2021-03-14 13:27:39 -07:00
Ryan Houdek 037781c4d0 Merge pull request #849 from Sonicadvance1/fix_kernel_version
Fixes uname version and wraps /proc/version
2021-03-14 11:09:05 -07:00
Scott Mansell 4d2221a456 Merge pull request #831 from phire/one_dispatcher_to_rule_them_all
Unify all four dispatchers
2021-03-15 05:42:21 +13:00
Scott Mansell 466dc03c19 Remove stray cmake changes 2021-03-15 04:49:09 +13:00
Scott Mansell 5e1e09a43d Fix review issues 2021-03-15 04:41:14 +13:00
Ryan Houdek 3a0b475cfa Wrap /proc/version where we were leaking host kernel information
drops the correct FEX version in to the string as well
2021-03-12 20:29:35 -08:00
Ryan Houdek 4ece56ac4d Fixes uname version string
This wasn't following the correct format and it never actually had a working FEX_VERSION define.
Now it pulls in the GIT_DESCRIBE_STRING and brings in the correct date + time format
2021-03-12 20:29:35 -08:00
Ryan Houdek ea7af248e8 Ensure programs can pull in git_version.h 2021-03-12 20:29:35 -08:00
Scott Mansell 42b186a76c Don't generate a default vixl::codebuffer 2021-03-12 12:47:29 +13:00
Scott Mansell bdca7109a4 Force XBYAK64
Because XBYAK's autoconfig doesn't quite do the right thing
2021-03-11 23:46:07 +13:00
Scott Mansell 074bedf25c Automatically use correct ContextBackup type 2021-03-11 23:46:07 +13:00
Scott Mansell bdb199b6d6 Unify all four dispatchers 2021-03-11 23:46:07 +13:00
Stefanos Kornilios Mitsis Poiitidis e9afabc4cb Merge pull request #827 from FEX-Emu/skmp/thunks-json
Thunks: Add Thunk json, thunk guest folder
2021-03-11 11:05:23 +02:00
Ryan Houdek da1532f3b8 Merge pull request #836 from Sonicadvance1/fexcore_config_nodefault_value
Adds config value option constructor without default initializer
2021-03-11 01:04:58 -08:00
Ryan Houdek c73d2b2bca Merge pull request #843 from Sonicadvance1/locked_not
Adds support for locked NOT
2021-03-11 00:47:01 -08:00
Ryan Houdek da43db8787 Merge pull request #842 from Sonicadvance1/locked_adc_sbb
Adds support for locked ADC and SBB
2021-03-11 00:46:53 -08:00
Ryan Houdek ab8aff6e0d Adds unittests for locked not 2021-03-11 00:38:44 -08:00
Ryan Houdek 8a7caa82b2 Adds support for locked NOT
This was trivial
2021-03-11 00:38:12 -08:00
Ryan Houdek 2706c9aaae Adds unittests for locked ADC and SBB 2021-03-11 00:24:03 -08:00
Ryan Houdek 96f2a461f8 Adds support for locked ADC and SBB
These were fairly straightforward
2021-03-11 00:22:35 -08:00
Ryan Houdek cf966eb2e0 Adds config value option constructor without default initializer
This has the expected behaviour that the configuration option will be available and not default initialized.
To enforce this fact, it will assert if it tries to get a value that doesn't exist yet.
2021-03-10 23:52:19 -08:00
Ryan Houdek 6b860cd68e Merge pull request #839 from Sonicadvance1/more_32bit_syscalls
Adds a couple new 32bit syscalls found while running Steam
2021-03-10 21:34:31 -08:00
Ryan Houdek 6b4ccc46a8 Merge pull request #838 from Sonicadvance1/fix_cmpxchg_32bit_reg
Fixes an edge case of 32bit cmpxchg <reg>, <reg>
2021-03-10 21:34:24 -08:00
Ryan Houdek 8acb18b2b9 Merge pull request #837 from Sonicadvance1/fix_logmanager
Fixes crash with large strings through LogManager
2021-03-10 21:34:17 -08:00
Ryan Houdek 832a634f64 Merge pull request #841 from phire/vixl_cmake_improvements
Improve vixl cmakefiles
2021-03-10 21:20:57 -08:00
Scott Mansell ec196b47e2 Improve Vixl cmakefiles 2021-03-11 16:43:55 +13:00
Ryan Houdek 4dc2cf2b32 Adds more cmpxchg <reg>, <reg> unit tests
This will catch the previous fix
2021-03-10 17:47:54 -08:00
Ryan Houdek e264194453 Fixes an edge case of 32bit cmpxchg <reg>, <reg>
The comparison was using the wrong values which means this cmpxchg would fail
2021-03-10 17:47:54 -08:00
Ryan Houdek ddce28df11 Merge pull request #834 from Sonicadvance1/cleanup_clang_tidy_iwyu
Moves clang-tidy arguments to root cmakelists
2021-03-10 17:47:19 -08:00
Ryan Houdek fc5bf5d261 Merge pull request #833 from Sonicadvance1/add_perf_warning
Adds x86-64 host performance warning
2021-03-10 17:47:07 -08:00
Ryan Houdek 45e5e58c7e Merge pull request #832 from Sonicadvance1/cleanup_error_interpreter
Extends error message about not being able to find guest interpreter
2021-03-10 17:46:52 -08:00
Ryan Houdek 0dc7fd75e1 Adds a couple new 32bit syscalls found while running Steam
These are straightforward to implement. Only a few dozen more syscalls missing on 32bit
2021-03-10 17:43:21 -08:00
Ryan Houdek 10fbf60259 Converts the printf usages in FEXCore to LogManager
Now that we can safely handle long strings these will now work
2021-03-10 16:38:46 -08:00
Ryan Houdek 4e55f29589 Fixes crash with large strings through LogManager
va_list types have an edge where after operating on it, then it is undefined behaviour to do anything other than va_end on the object.
So in order to call vsnprintf on it multiple times we must create copies with va_copy.
Easy enough to work around by doing a few more copies of the va_list
2021-03-10 16:36:33 -08:00
Ryan Houdek 1ddfa3e7ed Moves clang-tidy arguments to root cmakelists
This way we don't need to redeclare the arguments twice
Also moves IWYU lower so it doesn't hit any external projects other than FEXCore

Still not running these since everything needs to be cleaned up anyway
2021-03-10 12:38:16 -08:00
Ryan Houdek ec3883181b Adds x86-64 host performance warning
Lets users know that x86-64 isn't our optimal target but can still be used if passed in a new cmake argument.
Easy enough just pass in -DENABLE_X86_HOST_DEBUG=True to cmake.

Closes #776
2021-03-10 11:36:19 -08:00
Ryan Houdek e2ec645855 Extends error message about not being able to find guest interpreter
Passes the result back up to the frontend as well which allows us to early exit correctly.
Also ensures that we return ENOEXEC on these error cases so if someone is waiting on a return value, they don't just get zero
Fixes #757
2021-03-10 11:11:36 -08:00
Stefanos Kornilios Mitsis Poiitidis f0d3004363 Thunks: Add Thunk json, thunk guest folder 2021-03-10 15:51:34 +02:00
Stefanos Kornilios Mitsis Poiitidis e708b83dbb Merge pull request #826 from Sonicadvance1/disable_rcpc
Disables RCPC on ARM64 JIT
2021-03-10 12:31:17 +02:00
Ryan Houdek a9d3851684 Disables RCPC on ARM64 JIT
This is currently bugged on Snapdragon 865.
Seems to only affect its prime core. Smells like errata.
2021-03-10 02:13:16 -08:00
Ryan Houdek 7e4ba77d72 Merge pull request #825 from FEX-Emu/skmp/fix-sigchld
Signals: SA_NOCLDSTOP only blocks CLD_CONTINUED/STOPPED/TRAPPED
2021-03-10 01:50:04 -08:00
Stefanos Kornilios Mitsis Poiitidis 449ea3e80b Signals: SA_NOCLDSTOP only blocks CLD_CONTINUED/STOPPED/TRAPPED 2021-03-10 11:25:40 +02:00
Stefanos Kornilios Mitsis Poiitidis 8fd91a5b7a Merge pull request #821 from Sonicadvance1/update_vixl
Updates vixl submodule
2021-03-10 09:12:40 +02:00
Ryan Houdek e3d6db2eb5 Merge pull request #823 from FEX-Emu/skmp/fix-secondaryalu-atomics
OpDisp: Add atomic logic for SecondaryALUOp
2021-03-09 18:15:18 -08:00
Stefanos Kornilios Mitsis Poiitidis d930de30ef OpDisp: Add atomic logic for SecondaryALUOp 2021-03-09 18:32:13 +02:00
Scott Mansell b36378369a Merge pull request #822 from FEX-Emu/skmp/fix-scm-bug
ConstProp: Restrict imm code motion around selects to matching sizes, fixes dav1d
2021-03-09 22:12:50 +13:00
Stefanos Kornilios Mitsis Poiitidis cbf4c9a687 ConstProp: Restrict imm code motion around selects to matching sizes, fixes dav1d 2021-03-09 11:06:16 +02:00
Ryan Houdek bcebe7d47a Updates vixl submodule
Works around the no enum enum conversion warnings
2021-03-08 23:57:58 -08:00
Ryan Houdek e9b057bc8b Merge pull request #817 from phire/SeparateThreadAndState
Separate thread and state
2021-03-08 23:41:56 -08:00
Scott Mansell afda855caf disptacher fixes/cleanups 2021-03-09 20:28:53 +13:00
Scott Mansell 6ed36e3304 Refactor syscalls to take Frame
The vast majoirty of syscalls don't need anything in thread or frame.
So lets save an indirection for all those syscalls.

Most of the syscalls which do need Thread (or CTX via
Thread are in Thread.cpp or Memory.cpp
These have all been modifiy to fetch Thread from Frame
2021-03-09 20:28:50 +13:00
Scott Mansell 35c01038ba Seperate per-thread and per-frame state 2021-03-09 20:27:44 +13:00
Scott Mansell 43a087d554 Allow both interpeter dispatchers to be built
This commit doesn't include cmakefile changes to actually do this.
2021-03-09 20:25:14 +13:00
Ryan Houdek a1fed323df Merge pull request #820 from Sonicadvance1/remove_config_duplication
Deduplicates some configuration data
2021-03-08 23:20:04 -08:00
Ryan Houdek 0bcd1c7df5 Merge pull request #816 from Sonicadvance1/cpuid_80000005
Implements CPUID 0x8000'0005 for L1 cacheline information
2021-03-08 23:15:03 -08:00
Ryan Houdek a3ba1ae199 Deduplicates some configuration data
Configuration mapping was duplicated between three different tables.
Additionally default configuration values were strewn about. Making it confusing as to what the default value would end up being

Adds a new ConfigValues.inl header that defines a few things right next to each other.
Defines the enum name as usual.
Defines the JSON config option name.
Defines the Environment config option name
Defines the default value that the configuration should be
2021-03-08 22:53:41 -08:00
Ryan Houdek a2278cfb1e Adds StrConv type for enum conversion 2021-03-08 22:50:42 -08:00
Scott Mansell 98cb54e3e4 Merge pull request #812 from phire/both_sides
Allow both ARM64 and X86_64 jits to be compiled at the same time
2021-03-09 14:59:11 +13:00
Scott Mansell cb17127ccb Remove last traces of vixl simulator mode 2021-03-08 16:30:25 +13:00
Scott Mansell 93b6dbdc2b Fix Arm64JitCore instantiation on x86 2021-03-08 16:30:25 +13:00
Scott Mansell bea0adc3d1 Allow both jitcores to be enabled simultaneously 2021-03-08 16:30:25 +13:00
Scott Mansell 9c339c85c6 Cmake: allow independant control of both jits 2021-03-08 16:30:25 +13:00
Scott Mansell 9cf7545f8d Abstract mcontext out of JITs
This will help with unification of common code later
2021-03-08 16:30:25 +13:00
Ryan Houdek c657165e56 Implements CPUID 0x8000'0005 for L1 cacheline information
@skmp commented about this in the L2 cacheline information PR.
Quickly implement it since it also has cacheline information.

Fairly trivial since every piece of information is just some 8bit variables.
2021-03-06 08:32:47 -08:00
Ryan Houdek 05ae7d3c5e Merge pull request #814 from Sonicadvance1/cacheline_cpuid
Implements CPUID 0x8000'0006 for cacheline information
2021-03-06 08:23:03 -08:00
Ryan Houdek f94529ebb2 Implements CPUID 0x8000'0006 for cacheline information
This exposes some cache information including cacheline information.

Fills out this data structure as well just incase we hit another cacheline size bug
2021-03-06 07:30:25 -08:00
Ryan Houdek 852ab4e245 Merge pull request #815 from Sonicadvance1/sse4_1_movnt
Implements MOVNTDQA
2021-03-06 07:25:56 -08:00
Ryan Houdek bb6e7360a2 Merge pull request #813 from Sonicadvance1/disable_python_dev_check
Disabled cmake check for python development
2021-03-06 07:25:16 -08:00
Ryan Houdek 50ae18cbaf Adds MOVNTDQA unit test 2021-03-06 06:23:27 -08:00
Ryan Houdek 1adbf66fee Implements MOVNTDQA
Found this when running Steamlink under FEX on my laptop.
Turns out the Iris video driver now uses this unconditionally in a couple of locations.
Makes sense there since all the Iris targets are expected to support SSE4.1 atm.
2021-03-06 06:23:18 -08:00
Ryan Houdek 8222acdf20 Disabled cmake check for python development
We only need the interpreter
2021-03-06 06:06:59 -08:00
338 changed files with 17009 additions and 7702 deletions

No files matched your search

+30 -3
View File
@@ -13,18 +13,22 @@ env:
BUILD_TYPE: Release
CC: clang
CXX: clang++
FEX_FORCE32BITALLOCATOR: 1
jobs:
build:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
arch: [[self-hosted, x64], [self-hosted, ARMv8.0], [self-hosted, ARMv8.2]]
arch: [[self-hosted, x64], [self-hosted, ARMv8.0], [self-hosted, ARMv8.2], [self-hosted, ARMv8.4]]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Set runner label
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
- name : submodule checkout
# Need to update submodules
run: git submodule update --init --depth 1
@@ -45,7 +49,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
- name: Build
working-directory: ${{runner.workspace}}/build
@@ -113,13 +117,36 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GCC64.log || true
- name: gcc target tests 32
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the gvisor tests
run: cmake --build . --config $BUILD_TYPE --target gcc_target_tests_32
- name: GCC32 Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GCC32.log || true
- name: Struct verifier tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target struct_verifier
- name: Struct verifier Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_StructVerifier.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
# Cap out the log files at 20M in case something crash spins and dumps fault text
# ASM tests get quite close to 10MB
run: truncate --size=20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
run: truncate --size=<20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
- name: Set runner name
if: ${{ always() }}
+3
View File
@@ -30,3 +30,6 @@
shallow = true
path = External/fex-gcc-target-tests-bins
url = https://github.com/FEX-Emu/fex-gcc-target-tests-bins.git
[submodule "External/jemalloc"]
path = External/jemalloc
url = https://github.com/FEX-Emu/jemalloc.git
+140 -33
View File
@@ -12,6 +12,8 @@ option(ENABLE_ASAN "Enables Clang ASAN" FALSE)
option(ENABLE_TSAN "Enables Clang TSAN" FALSE)
option(ENABLE_ASSERTIONS "Enables assertions in build" FALSE)
option(ENABLE_VISUAL_DEBUGGER "Enables the visual debugger for compiling" FALSE)
option(ENABLE_STRICT_WERROR "Enables stricter -Werror for CI" FALSE)
option(ENABLE_WERROR "Enables -Werror" FALSE)
set (X86_C_COMPILER "x86_64-linux-gnu-gcc" CACHE STRING "c compiler for compiling x86 guest libs")
set (X86_CXX_COMPILER "x86_64-linux-gnu-g++" CACHE STRING "c++ compiler for compiling x86 guest libs")
@@ -27,14 +29,6 @@ if (ENABLE_ASSERTIONS)
add_definitions(-DASSERTIONS_ENABLED=1)
endif()
if (ENABLE_IWYU)
find_program(IWYU_EXE "iwyu")
if (IWYU_EXE)
message(STATUS "IWYU enabled")
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXE}")
endif()
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Bin)
@@ -67,8 +61,8 @@ endif()
if (ENABLE_ASAN)
add_definitions(-DENABLE_ASAN=1)
add_compile_options(-fno-omit-frame-pointer -fsanitize=address)
link_libraries(-fno-omit-frame-pointer -fsanitize=address)
add_compile_options(-fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope)
link_libraries(-fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope)
endif()
if (ENABLE_TSAN)
@@ -83,6 +77,14 @@ set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer")
set (CMAKE_LINKER_FLAGS_RELEASE "${CMAKE_LINKER_FLAGS_RELEASE} -fomit-frame-pointer")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
option(ENABLE_X86_HOST_DEBUG "Enables compiling on x86_64 host" FALSE)
if (NOT ENABLE_X86_HOST_DEBUG)
message(FATAL_ERROR
" Be warned: FEX isn't optimized for x86_64 hosts!\n"
" Support for x86_64 hosts is only for debugging and convenience!\n"
" Don't expect amazing performance or optimal code generation!\n"
" Pass -DENABLE_X86_HOST_DEBUG=True to bypass this message!")
endif()
set(_M_X86_64 1)
add_definitions(-D_M_X86_64=1)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
@@ -91,23 +93,26 @@ endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(_M_ARM_64 1)
add_definitions(-D_M_ARM_64=1)
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
add_definitions(-DVIXL_DEBUG=1)
endif()
endif()
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# This means we were attempted to get compiled with GCC
message(FATAL_ERROR "FEX doesn't support getting compiled with GCC!")
endif()
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter Development)
find_package(PkgConfig REQUIRED)
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter)
pkg_check_modules(XXHASH libxxhash REQUIRED)
add_definitions(-Wno-trigraphs)
add_definitions(-DGLOBAL_DATA_DIRECTORY="${DATA_DIRECTORY}/")
add_subdirectory(External/jemalloc/)
include_directories(External/jemalloc/pregen/include/)
add_subdirectory(External/cpp-optparse/)
include_directories(External/cpp-optparse/)
@@ -149,32 +154,115 @@ if(COMPILER_SUPPORTS_MARCH_NATIVE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
if(_M_ARM_64)
# Due to an oversight in llvm, it declares any reasonably new Kryo CPU to only be ARMv8.0
# Manually detect newer CPU revisions until clang and llvm fixes their bug
# This script will either provide a supported CPU or 'native'
# Additionally -march doesn't work under AArch64+Clang, so you have to use -mcpu or -mtune
execute_process(COMMAND python3 "${PROJECT_SOURCE_DIR}/Scripts/aarch64_fit_native.py" "/proc/cpuinfo"
OUTPUT_VARIABLE AARCH64_CPU)
string(STRIP ${AARCH64_CPU} AARCH64_CPU)
check_cxx_compiler_flag("-mcpu=${AARCH64_CPU}" COMPILER_SUPPORTS_CPU_TYPE)
if(COMPILER_SUPPORTS_CPU_TYPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${AARCH64_CPU}")
if(ENABLE_WERROR OR ENABLE_STRICT_WERROR)
add_compile_options(-Werror)
if (NOT ENABLE_STRICT_WERROR)
# Disable some Werror that can add frustration when developing
add_compile_options(-Wno-error=unused-variable)
endif()
endif()
if(_M_ARM_64)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 999999.0)
# Clang 12.0 fixed the -mcpu=native bug with mixed big.little implementers
# Clang can not currently check for native Apple M1 type in hypervisor. Currently disabled
check_cxx_compiler_flag("-mcpu=native" COMPILER_SUPPORTS_CPU_TYPE)
if(COMPILER_SUPPORTS_CPU_TYPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
endif()
else()
# Due to an oversight in llvm, it declares any reasonably new Kryo CPU to only be ARMv8.0
# Manually detect newer CPU revisions until clang and llvm fixes their bug
# This script will either provide a supported CPU or 'native'
# Additionally -march doesn't work under AArch64+Clang, so you have to use -mcpu or -mtune
execute_process(COMMAND python3 "${PROJECT_SOURCE_DIR}/Scripts/aarch64_fit_native.py" "/proc/cpuinfo" "${CMAKE_CXX_COMPILER_VERSION}"
OUTPUT_VARIABLE AARCH64_CPU)
string(STRIP ${AARCH64_CPU} AARCH64_CPU)
check_cxx_compiler_flag("-mcpu=${AARCH64_CPU}" COMPILER_SUPPORTS_CPU_TYPE)
if(COMPILER_SUPPORTS_CPU_TYPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${AARCH64_CPU}")
endif()
endif()
endif()
if (ENABLE_IWYU)
find_program(IWYU_EXE "iwyu")
if (IWYU_EXE)
message(STATUS "IWYU enabled")
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXE}")
endif()
endif()
if (ENABLE_CLANG_FORMAT)
find_program(CLANG_TIDY_EXE "clang-tidy")
if (NOT CLANG_TIDY_EXE)
message(FATAL_ERROR "Couldn't find clang-tidy")
endif()
set(CLANG_TIDY_FLAGS
"-checks=*"
"-fuchsia*"
"-bugprone-macro-parentheses"
"-clang-analyzer-core.*"
"-cppcoreguidelines-pro-type-*"
"-cppcoreguidelines-pro-bounds-array-to-pointer-decay"
"-cppcoreguidelines-pro-bounds-pointer-arithmetic"
"-cppcoreguidelines-avoid-c-arrays"
"-cppcoreguidelines-avoid-magic-numbers"
"-cppcoreguidelines-pro-bounds-constant-array-index"
"-cppcoreguidelines-no-malloc"
"-cppcoreguidelines-special-member-functions"
"-cppcoreguidelines-owning-memory"
"-cppcoreguidelines-macro-usage"
"-cppcoreguidelines-avoid-goto"
"-google-readability-function-size"
"-google-readability-namespace-comments"
"-google-readability-braces-around-statements"
"-google-build-using-namespace"
"-hicpp-*"
"-llvm-namespace-comment"
"-llvm-include-order" # Messes up with case sensitivity
"-llvmlibc-*"
"-misc-unused-parameters"
"-modernize-loop-convert"
"-modernize-use-auto"
"-modernize-avoid-c-arrays"
"-modernize-use-nodiscard"
"readability-*"
"-readability-function-size"
"-readability-implicit-bool-conversion"
"-readability-braces-around-statements"
"-readability-else-after-return"
"-readability-magic-numbers"
"-readability-named-parameter"
"-readability-uppercase-literal-suffix"
"-cert-err34-c"
"-cert-err58-cpp"
"-bugprone-exception-escape"
)
string(REPLACE ";" "," CLANG_TIDY_FLAGS "${CLANG_TIDY_FLAGS}")
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} "${CLANG_TIDY_FLAGS}")
endif()
add_compile_options(-Wall)
add_subdirectory(External/FEXCore)
add_subdirectory(Source/)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/Config.h.in
${CMAKE_BINARY_DIR}/generated/Config.h)
if (BUILD_TESTS)
include(CTest)
enable_testing()
message(STATUS "Unit tests are enabled")
endif()
add_subdirectory(External/FEXCore)
add_subdirectory(Source/)
add_subdirectory(Data/AppConfig/)
if (BUILD_TESTS)
add_subdirectory(unittests/)
endif()
@@ -185,16 +273,35 @@ if (BUILD_THUNKS)
PREFIX host-libs
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/HostLibs"
BINARY_DIR "Host"
CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
INSTALL_COMMAND ""
BUILD_ALWAYS ON
)
install(
CODE "MESSAGE(\"-- Installing: host-libs\")"
CODE "
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target ThunkHostsInstall
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Host
)"
DEPENDS host-libs
)
ExternalProject_Add(guest-libs
PREFIX guest-libs
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
BINARY_DIR "Guest"
CMAKE_ARGS "-DX86_C_COMPILER:STRING=${X86_C_COMPILER}" "-DX86_CXX_COMPILER:STRING=${X86_CXX_COMPILER}"
CMAKE_ARGS "-DX86_C_COMPILER:STRING=${X86_C_COMPILER}" "-DX86_CXX_COMPILER:STRING=${X86_CXX_COMPILER}" "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
INSTALL_COMMAND ""
BUILD_ALWAYS ON
)
install(
CODE "MESSAGE(\"-- Installing: guest-libs\")"
CODE "
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target ThunkGuestsInstall
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest
)"
DEPENDS guest-libs
)
endif()
+25
View File
@@ -0,0 +1,25 @@
file(GLOB CONFIG_SOURCES CONFIGURE_DEPENDS *.json)
file(GLOB GEN_CONFIG_SOURCES CONFIGURE_DEPENDS *.json.in)
# Any application configuration json file gets installed
foreach(CONFIG_SRC ${CONFIG_SOURCES})
install(FILES ${CONFIG_SRC}
DESTINATION ${DATA_DIRECTORY}/AppConfig/)
endforeach()
# Any configuration file json file that needs to be generated
# First generate then install it
foreach(GEN_CONFIG_SRC ${GEN_CONFIG_SOURCES})
# Get the filename only component
get_filename_component(CONFIG_NAME ${GEN_CONFIG_SRC} NAME_WE)
# Configure it
configure_file(
${GEN_CONFIG_SRC}
${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json)
# Then install the configured json
install(
FILES ${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json
DESTINATION ${DATA_DIRECTORY}/AppConfig/)
endforeach()
+5
View File
@@ -0,0 +1,5 @@
{
"Config": {
"Env": "STEAM_GAME_LAUNCH_SHELL=@CMAKE_INSTALL_PREFIX@/bin/FEXBash"
}
}
+2 -1
View File
@@ -4,7 +4,8 @@ FROM ubuntu:20.04 as builder
RUN DEBIAN_FRONTEND="noninteractive" apt-get update
RUN DEBIAN_FRONTEND="noninteractive" apt install -y cmake \
clang-10 llvm-10 nasm ninja-build libnuma-dev \
libcap-dev libglfw3-dev libepoxy-dev
libcap-dev libglfw3-dev libepoxy-dev python3-dev \
python3 linux-headers-generic
COPY . /opt/FEX
+12 -12
View File
@@ -4,6 +4,17 @@ project(${PROJECT_NAME}
VERSION 0.01
LANGUAGES CXX)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(_M_X86_64 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(_M_ARM_64 1)
endif()
set(ENABLE_JIT_X86_64 ${_M_X86_64} CACHE BOOL "Enable the x86_64 JIT")
set(ENABLE_JIT_ARM64 ${_M_ARM_64} CACHE BOOL "Enable the ARM64 JIT")
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
option(ENABLE_JITSYMBOLS "Enable visibility of JITSymbols in profiling tools" FALSE)
@@ -17,22 +28,11 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(CheckCXXCompilerFlag)
include(CheckIncludeFileCXX)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(_M_X86_64 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
message(STATUS "Enabling x86-64 JIT")
set(ENABLE_JIT 1)
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
message(STATUS "Enabling AArch64 JIT")
set(_M_ARM_64 1)
set(ENABLE_JIT 1)
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
# Useful to have for freestanding libFEXCore
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
endif()
endif()
set(CMAKE_CXX_STANDARD 20)
+467
View File
@@ -0,0 +1,467 @@
import datetime
import json
import sys
def print_header():
header = '''#ifndef OPT_BASE
#define OPT_BASE(type, group, enum, json, default)
#endif
#ifndef OPT_BOOL
#define OPT_BOOL(group, enum, json, default) OPT_BASE(bool, group, enum, json, default)
#endif
#ifndef OPT_UINT8
#define OPT_UINT8(group, enum, json, default) OPT_BASE(uint8_t, group, enum, json, default)
#endif
#ifndef OPT_INT32
#define OPT_INT32(group, enum, json, default) OPT_BASE(int32_t, group, enum, json, default)
#endif
#ifndef OPT_UINT32
#define OPT_UINT32(group, enum, json, default) OPT_BASE(uint32_t, group, enum, json, default)
#endif
#ifndef OPT_UINT64
#define OPT_UINT64(group, enum, json, default) OPT_BASE(uint64_t, group, enum, json, default)
#endif
#ifndef OPT_STR
#define OPT_STR(group, enum, json, default) OPT_BASE(std::string, group, enum, json, default)
#endif
#ifndef OPT_STRARRAY
#define OPT_STRARRAY(group, enum, json, default) OPT_BASE(std::string, group, enum, json, default)
#endif
'''
output_file.write(header)
def print_tail():
tail = '''#undef OPT_BASE
#undef OPT_BOOL
#undef OPT_UINT8
#undef OPT_INT32
#undef OPT_UINT32
#undef OPT_UINT64
#undef OPT_STR
#undef OPT_STRARRAY
'''
output_file.write(tail)
def print_config(type, group_name, json_name, default_value):
output_file.write("OPT_{0} ({1}, {2}, {3}, {4})\n".format(type.upper(), group_name.upper(), json_name.upper(), json_name, default_value))
def print_options(options):
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "\"" + default + "\""
print_config(
op_vals["Type"],
op_group,
op_key,
default)
output_file.write("\n")
def print_unnamed_options(options):
output_file.write("// Unnamed configuration options\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "\"" + default + "\""
print_config(
op_vals["Type"],
op_group,
op_key.upper(), # KEY is the enum here, there is no json configuration for these
default)
output_file.write("\n")
def print_man_option(short, long, desc, default):
if (short != None):
output_man.write(".It Fl {0} , ".format(short))
else:
output_man.write(".It ")
output_man.write("Fl Fl {0}=".format(long))
output_man.write("\n");
# Print description
for line in desc:
output_man.write(".Pp\n")
output_man.write("{0}\n".format(line))
output_man.write(".Pp\n")
output_man.write("\\fBdefault:\\fR {0}\n".format(default))
output_man.write(".Pp\n\n")
def print_man_env_option(name, desc, default):
output_man.write("\\fBFEX_{0}\\fR\n".format(name))
# Print description
for line in desc:
output_man.write(".Pp\n")
output_man.write("{0}\n".format(line))
output_man.write(".Pp\n")
output_man.write("\\fBdefault:\\fR {0}\n".format(default))
output_man.write(".Pp\n\n")
def print_man_options(options):
output_man.write(".Sh OPTIONS\n")
output_man.write(".Bl -tag -width -indent\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
short = None
long = op_key.lower()
if ("ShortArg" in op_vals):
short = op_vals["ShortArg"]
default = op_vals["Default"]
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = op_vals["TextDefault"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "'" + default + "'"
print_man_option(
short,
long,
op_vals["Desc"],
default
)
output_man.write(".El\n")
def print_man_environment(options):
output_man.write(".Sh ENVIRONMENT\n")
output_man.write(".Bl -tag -width -indent\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = op_vals["TextDefault"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "'" + default + "'"
print_man_env_option(
op_key.upper(),
op_vals["Desc"],
default
)
print_man_environment_tail()
output_man.write(".El\n")
def print_man_environment_tail():
# Additional environment variables that live outside of the normal loop
print_man_env_option(
"FEX_APP_CONFIG_LOCATION",
[
"Allows the user to override where FEX looks for configuration files",
"By default FEX will look in {$HOME, $XDG_CONFIG_HOME}/.fex-emu/",
"This will override the full path",
],
"''")
print_man_env_option(
"FEX_APP_CONFIG",
[
"Allows the user to override where FEX looks for only the application config file",
"By default FEX will look in {$HOME, $XDG_CONFIG_HOME}/.fex-emu/Config.json",
"This will override this file location",
"One must be careful with this option as it will override any applications that load with execve as well"
"If you need to support applications that execve then use FEX_APP_CONFIG_LOCATION instead"
],
"''")
print_man_env_option(
"FEX_APP_DATA_LOCATION",
[
"Allows the user to override where FEX looks for data files",
"By default FEX will look in {$HOME, $XDG_DATA_HOME}/.fex-emu/",
"This will override the full path",
"This is the folder where FEX stores generated files like IR cache"
],
"''")
def print_man_header():
header ='''.Dd {0}
.Dt FEX
.Os Linux
.Sh NAME
.Nm FEXLoader
.Nm FEXInterpreter
.Nm FEXBash
.Nd Fast x86-64 and x86 emulation.
.Sh SYNOPSIS
.Nm
.Op options
.Op Ar --
.Ar Application
<args> ...
.Pp
.Nm FEXInterpreter
.Ar Application
<args> ...
.Pp
.Nm FEXBash
.Ar <args> ...
.Sh DESCRIPTION
FEX allows you to run x86 and x86-64 binaries on an AArch64 host, similar to qemu-user and box86.
It has native support for a rootfs overlay, so you don't need to chroot, as well as some thunklibs so it can forward things like GL to the host.
FEX presents a Linux 5.0 interface to the guest, and supports both AArch64 and x86-64 as hosts.
FEX is very much work in progress, so expect things to change.
'''
output_man.write(header.format(datetime.datetime.now().strftime("%d-%m-%Y")))
def print_man_tail():
tail ='''.Sh FILES
.Bl -tag -width "$prefix/share/fex-emu/GuestThunks" -compact
.It Pa $XDG_HOME_DIR/.fex-emu
Default FEX user configuration directory
.It Pa $prefix/share/fex-emu/AppConfig
System level application configuration files
.It Pa $prefix/share/fex-emu/GuestThunks
guest-side thunk data libraries
.It Pa $prefix/lib/fex-emu/HostThunks
host-side thunks for guest communication
.El
'''
output_man.write(tail)
def print_config_option(type, group_name, json_name, default_value, short, choices, desc):
if (type == "bool"):
# Bool gets some special handling to add an inverted case
output_argloader.write("{0}Group".format(group_name))
options = ""
AddedArg = False
if (short != None):
AddedArg = True
options += "\"-{0}\"".format(short)
if (AddedArg):
options += ", "
options += "\"--{0}\"".format(json_name.lower())
output_argloader.write(".add_option({0})".format(options))
output_argloader.write("\n")
output_argloader.write("\t.action(\"store_true\")\n")
output_argloader.write("\t.dest(\"{0}\")\n".format(json_name));
# help
output_argloader.write("\t.help(\n")
desc_line_ender = ""
if (len(desc) > 1):
desc_line_ender = "\\n"
for line in desc:
output_argloader.write("\t\t\"{0}{1}\"\n".format(line, desc_line_ender))
output_argloader.write("\t)\n")
output_argloader.write("\t.set_default({0});\n\n".format(default_value));
output_argloader.write("{0}Group".format(group_name))
output_argloader.write(".add_option(\"--no-{0}\")\n".format(json_name.lower()))
# Inverted case sets the bool to false
output_argloader.write("\t.action(\"store_false\")\n")
output_argloader.write("\t.dest(\"{0}\");\n".format(json_name));
else:
output_argloader.write("{0}Group".format(group_name))
options = ""
AddedArg = False
if (short != None):
AddedArg = True
options += "\"-{0}\"".format(short)
if (AddedArg):
options += ", "
options += "\"--{0}\"".format(json_name.lower())
output_argloader.write(".add_option({0})".format(options))
output_argloader.write("\n")
output_argloader.write("\t.dest(\"{0}\")\n".format(json_name));
if (choices != None):
output_argloader.write("\t.choices({\n")
for choice in choices:
output_argloader.write("\t\t\"{0}\",\n".format(choice))
output_argloader.write("\t})\n")
# help
output_argloader.write("\t.help(\n")
desc_line_ender = ""
if (len(desc) > 1):
desc_line_ender = "\\n"
for line in desc:
output_argloader.write("\t\t\"{0}{1}\"\n".format(line, desc_line_ender))
output_argloader.write("\t)\n")
output_argloader.write("\t.set_default({0});\n".format(default_value));
output_argloader.write("\n");
def print_argloader_options(options):
output_argloader.write("#ifdef BEFORE_PARSE\n")
output_argloader.write("#undef BEFORE_PARSE\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "\"" + default + "\""
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = "\"" + op_vals["TextDefault"] + "\""
short = None
choices = None
if ("ShortArg" in op_vals):
short = op_vals["ShortArg"]
if ("Choices" in op_vals):
choices = op_vals["Choices"]
print_config_option(
op_vals["Type"],
op_group,
op_key,
default,
short,
choices,
op_vals["Desc"])
output_argloader.write("\n")
output_argloader.write("#endif\n")
def print_parse_argloader_options(options):
output_argloader.write("#ifdef AFTER_PARSE\n")
output_argloader.write("#undef AFTER_PARSE\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
output_argloader.write("if (Options.is_set_by_user(\"{0}\")) {{\n".format(op_key))
value_type = op_vals["Type"]
NeedsString = False
conversion_func = "std::to_string"
if ("ArgumentHandler" in op_vals):
NeedsString = True
conversion_func = "FEX::Handler::{0}".format(op_vals["ArgumentHandler"])
if (value_type == "str"):
NeedsString = True
conversion_func = ""
if (value_type == "strarray"):
# these need a bit more help
output_argloader.write("\tauto Array = Options.all(\"{0}\");\n".format(op_key))
output_argloader.write("\tfor (auto iter = Array.begin(); iter != Array.end(); ++iter) {\n")
output_argloader.write("\t\tSet(FEXCore::Config::ConfigOption::CONFIG_{0}, *iter);\n".format(op_key.upper()))
output_argloader.write("\t}\n")
else:
if (NeedsString):
output_argloader.write("\tstd::string UserValue = Options[\"{0}\"];\n".format(op_key))
else:
output_argloader.write("\t{0} UserValue = Options.get(\"{1}\");\n".format(value_type, op_key))
output_argloader.write("\tSet(FEXCore::Config::ConfigOption::CONFIG_{0}, {1}(UserValue));\n".format(op_key.upper(), conversion_func))
output_argloader.write("}\n")
output_argloader.write("#endif\n")
def check_for_duplicate_options(options):
short_map = []
long_map = []
# Spin through all the items and see if we have a duplicate option
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
short = None
long = op_key.lower()
long_invert = None
if ("ShortArg" in op_vals):
short = op_vals["ShortArg"]
if (op_vals["Type"] == "bool"):
long_invert = "no-" + long
# Check for short key duplication
if (short != None):
if (short in short_map):
raise Exception("Short config '{0}' for option '{1}' has duplicate entry!".format(short, op_key))
else:
short_map.append(short)
# Check for long key duplication
if (long in long_map):
raise Exception("Long config '{0}' has duplicate entry!".format(long))
else:
long_map.append(long)
# Check for long key duplication
if (long_invert != None):
if (long_invert in long_map):
raise Exception("Long config '{0}' has duplicate entry!".format(long_invert))
else:
long_map.append(long_invert)
if (len(sys.argv) < 5):
sys.exit()
output_filename = sys.argv[2]
output_man_page = sys.argv[3]
output_argumentloader_filename = sys.argv[4]
json_file = open(sys.argv[1], "r")
json_text = json_file.read()
json_file.close()
json_object = json.loads(json_text)
options = json_object["Options"]
unnamed_options = json_object["UnnamedOptions"]
check_for_duplicate_options(options)
# Generate config include file
output_file = open(output_filename, "w")
print_header()
print_options(options)
print_unnamed_options(unnamed_options)
print_tail()
output_file.close()
# Generate man file
output_man = open(output_man_page, "w")
print_man_header()
print_man_options(options)
print_man_environment(options)
print_man_tail()
output_man.close()
# Generate argument loader code
output_argloader = open(output_argumentloader_filename, "w")
print_argloader_options(options);
print_parse_argloader_options(options);
output_argloader.close()
+25 -19
View File
@@ -108,10 +108,10 @@ def print_ir_sizes(ops, defines):
output_file.write("[[maybe_unused]] static size_t GetSize(IROps Op) { return IRSizes[Op]; }\n\n")
output_file.write("std::string_view const& GetName(IROps Op);\n")
output_file.write("uint8_t GetArgs(IROps Op);\n")
output_file.write("FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n")
output_file.write("bool HasSideEffects(IROps Op);\n")
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) std::string_view const& GetName(IROps Op);\n")
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) uint8_t GetArgs(IROps Op);\n")
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n")
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) bool HasSideEffects(IROps Op);\n")
output_file.write("#undef IROP_SIZES\n")
output_file.write("#endif\n\n")
@@ -277,7 +277,7 @@ def print_ir_allocator_helpers(ops, defines):
output_file.write("\tusing IRPair = Wrapper<T>;\n\n")
output_file.write("\tIRPair<IROp_Header> AllocateRawOp(size_t HeaderSize) {\n")
output_file.write("\t\tauto Op = reinterpret_cast<IROp_Header*>(Data.Allocate(HeaderSize));\n")
output_file.write("\t\tauto Op = reinterpret_cast<IROp_Header*>(DualListData.DataAllocate(HeaderSize));\n")
output_file.write("\t\tmemset(Op, 0, HeaderSize);\n")
output_file.write("\t\tOp->Op = IROps::OP_DUMMY;\n")
output_file.write("\t\treturn IRPair<IROp_Header>{Op, CreateNode(Op)};\n")
@@ -286,7 +286,7 @@ def print_ir_allocator_helpers(ops, defines):
output_file.write("\ttemplate<class T, IROps T2>\n")
output_file.write("\tT *AllocateOrphanOp() {\n")
output_file.write("\t\tsize_t Size = FEXCore::IR::GetSize(T2);\n")
output_file.write("\t\tauto Op = reinterpret_cast<T*>(Data.Allocate(Size));\n")
output_file.write("\t\tauto Op = reinterpret_cast<T*>(DualListData.DataAllocate(Size));\n")
output_file.write("\t\tmemset(Op, 0, Size);\n")
output_file.write("\t\tOp->Header.Op = T2;\n")
output_file.write("\t\treturn Op;\n")
@@ -295,25 +295,25 @@ def print_ir_allocator_helpers(ops, defines):
output_file.write("\ttemplate<class T, IROps T2>\n")
output_file.write("\tIRPair<T> AllocateOp() {\n")
output_file.write("\t\tsize_t Size = FEXCore::IR::GetSize(T2);\n")
output_file.write("\t\tauto Op = reinterpret_cast<T*>(Data.Allocate(Size));\n")
output_file.write("\t\tauto Op = reinterpret_cast<T*>(DualListData.DataAllocate(Size));\n")
output_file.write("\t\tmemset(Op, 0, Size);\n")
output_file.write("\t\tOp->Header.Op = T2;\n")
output_file.write("\t\treturn IRPair<T>{Op, CreateNode(&Op->Header)};\n")
output_file.write("\t}\n\n")
output_file.write("\tuint8_t GetOpSize(OrderedNode *Op) const {\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(Data.Begin());\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
output_file.write("\t\treturn HeaderOp->Size;\n")
output_file.write("\t}\n\n")
output_file.write("\tuint8_t GetOpElements(OrderedNode *Op) const {\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(Data.Begin());\n")
output_file.write("\t\tLogMan::Throw::A(HeaderOp->HasDest, \"Op %s has no dest\\n\", GetName(HeaderOp->Op));\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
output_file.write("\t\tLOGMAN_THROW_A(HeaderOp->HasDest, \"Op %s has no dest\\n\", GetName(HeaderOp->Op));\n")
output_file.write("\t\treturn HeaderOp->Size / HeaderOp->ElementSize;\n")
output_file.write("\t}\n\n")
output_file.write("\tbool OpHasDest(OrderedNode *Op) const {\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(Data.Begin());\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
output_file.write("\t\treturn HeaderOp->HasDest;\n")
output_file.write("\t}\n\n")
@@ -387,11 +387,14 @@ def print_ir_allocator_helpers(ops, defines):
output_file.write(") {\n")
output_file.write("\t\tauto Op = AllocateOp<IROp_%s, IROps::OP_%s>();\n" % (op_key, op_key.upper()))
output_file.write("\t\tOp.first->Header.NumArgs = %d;\n" % (SSAArgs))
if (SSAArgs != 0):
output_file.write("\t\tauto ListDataBegin = DualListData.ListBegin();\n")
for i in range(0, SSAArgs):
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(ListDataBegin);\n" % (i, i))
if (SSAArgs != 0):
for i in range(0, SSAArgs):
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(ListData.Begin());\n" % (i, i))
output_file.write("\t\tssa%d->AddUse();\n" % (i))
if (HasArgs):
@@ -399,11 +402,6 @@ def print_ir_allocator_helpers(ops, defines):
data_name = op_vals["Args"][i]
output_file.write("\t\tOp.first->%s = %s;\n" % (data_name, data_name))
if (HasFixedDestSize):
output_file.write("\t\tOp.first->Header.Size = %d;\n" % FixedDestSize)
if (HasDestSize):
output_file.write("\t\tOp.first->Header.Size = %s;\n" % DestSize)
if (HasDest):
# We can only infer a size if we have arguments
if not (HasFixedDestSize or HasDestSize):
@@ -412,10 +410,18 @@ def print_ir_allocator_helpers(ops, defines):
if (SSAArgs != 0):
for i in range(0, SSAArgs):
output_file.write("\t\tuint8_t Size%d = GetOpSize(ssa%s);\n" % (i, i))
for i in range(0, SSAArgs):
output_file.write("\t\tInferSize = std::max(InferSize, Size%d);\n" % (i))
output_file.write("\t\tOp.first->Header.Size = InferSize;\n")
output_file.write("\t\tOp.first->Header.NumArgs = %d;\n" % (SSAArgs))
if (HasFixedDestSize):
output_file.write("\t\tOp.first->Header.Size = %d;\n" % FixedDestSize)
if (HasDestSize):
output_file.write("\t\tOp.first->Header.Size = %s;\n" % DestSize)
output_file.write("\t\tOp.first->Header.ElementSize = Op.first->Header.Size / (%s);\n" % NumElements)
if (HasDest):
@@ -499,7 +505,7 @@ def print_ir_parser_allocator_helpers(ops, defines):
if (SSAArgs != 0):
for i in range(0, SSAArgs):
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(ListData.Begin());\n" % (i, i))
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(DualListData.ListBegin());\n" % (i, i))
output_file.write("\t\tssa%d->AddUse();\n" % (i))
if (HasArgs):
+130 -94
View File
@@ -1,49 +1,4 @@
if (ENABLE_CLANG_FORMAT)
find_program(CLANG_TIDY_EXE "clang-tidy")
set(CLANG_TIDY_FLAGS
"-checks=*"
"-fuchsia*"
"-bugprone-macro-parentheses"
"-clang-analyzer-core.*"
"-cppcoreguidelines-pro-type-*"
"-cppcoreguidelines-pro-bounds-array-to-pointer-decay"
"-cppcoreguidelines-pro-bounds-pointer-arithmetic"
"-cppcoreguidelines-avoid-c-arrays"
"-cppcoreguidelines-avoid-magic-numbers"
"-cppcoreguidelines-pro-bounds-constant-array-index"
"-cppcoreguidelines-no-malloc"
"-cppcoreguidelines-special-member-functions"
"-cppcoreguidelines-owning-memory"
"-cppcoreguidelines-macro-usage"
"-cppcoreguidelines-avoid-goto"
"-google-readability-function-size"
"-google-readability-namespace-comments"
"-google-readability-braces-around-statements"
"-google-build-using-namespace"
"-hicpp-*"
"-llvm-namespace-comment"
"-llvm-include-order" # Messes up with case sensitivity
"-misc-unused-parameters"
"-modernize-loop-convert"
"-modernize-use-auto"
"-modernize-avoid-c-arrays"
"-modernize-use-nodiscard"
"readability-*"
"-readability-function-size"
"-readability-implicit-bool-conversion"
"-readability-braces-around-statements"
"-readability-else-after-return"
"-readability-magic-numbers"
"-readability-named-parameter"
"-readability-uppercase-literal-suffix"
"-cert-err34-c"
"-cert-err58-cpp"
"-bugprone-exception-escape"
)
string(REPLACE ";" "," CLANG_TIDY_FLAGS "${CLANG_TIDY_FLAGS}")
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} "${CLANG_TIDY_FLAGS}")
endif()
set (MAN_DIR ${CMAKE_INSTALL_PREFIX}/share/man CACHE PATH "MAN_DIR")
set (SRCS
Common/Paths.cpp
@@ -130,6 +85,11 @@ set (SRCS
Interface/Core/X86Tables.cpp
Interface/Core/X86DebugInfo.cpp
Interface/Core/X86HelperGen.cpp
Interface/Core/ArchHelpers/Arm64_stubs.cpp
Interface/Core/ArchHelpers/Arm64Emitter.cpp
Interface/Core/Dispatcher/Dispatcher.cpp
Interface/Core/Dispatcher/X86Dispatcher.cpp
Interface/Core/Dispatcher/Arm64Dispatcher.cpp
Interface/Core/Interpreter/InterpreterCore.cpp
Interface/Core/Interpreter/InterpreterOps.cpp
Interface/Core/X86Tables/BaseTables.cpp
@@ -154,6 +114,7 @@ set (SRCS
Interface/IR/Passes/DeadContextStoreElimination.cpp
Interface/IR/Passes/IRCompaction.cpp
Interface/IR/Passes/IRValidation.cpp
Interface/IR/Passes/LongDivideRemovalPass.cpp
Interface/IR/Passes/ValueDominanceValidation.cpp
Interface/IR/Passes/PhiValidation.cpp
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
@@ -161,61 +122,63 @@ set (SRCS
Interface/IR/Passes/StaticRegisterAllocationPass.cpp
Interface/IR/Passes/RegisterAllocationPass.cpp
Interface/IR/Passes/SyscallOptimization.cpp
Utils/ELFLoader.cpp
Utils/Allocator.cpp
Utils/Allocator/64BitAllocator.cpp
Utils/ELFContainer.cpp
Utils/ELFSymbolDatabase.cpp
Utils/LogManager.cpp
Utils/Threads.cpp
)
if (_M_X86_64)
list(APPEND SRCS Interface/Core/Interpreter/x86_64Dispatcher.cpp)
endif()
if(_M_ARM_64)
list(APPEND SRCS
Interface/Core/ArchHelpers/Arm64.cpp
Interface/Core/Interpreter/Arm64Dispatcher.cpp)
Interface/Core/ArchHelpers/Arm64.cpp)
endif()
set (JIT_LIBS )
if (ENABLE_JIT)
if (_M_X86_64)
add_definitions(-D_M_X86_64=1)
if (NOT FORCE_AARCH64)
list(APPEND SRCS
Interface/Core/JIT/x86_64/JIT.cpp
Interface/Core/JIT/x86_64/ALUOps.cpp
Interface/Core/JIT/x86_64/AtomicOps.cpp
Interface/Core/JIT/x86_64/BranchOps.cpp
Interface/Core/JIT/x86_64/ConversionOps.cpp
Interface/Core/JIT/x86_64/EncryptionOps.cpp
Interface/Core/JIT/x86_64/FlagOps.cpp
Interface/Core/JIT/x86_64/MemoryOps.cpp
Interface/Core/JIT/x86_64/MiscOps.cpp
Interface/Core/JIT/x86_64/MoveOps.cpp
Interface/Core/JIT/x86_64/VectorOps.cpp)
endif()
endif()
if(_M_ARM_64)
add_definitions(-D_M_ARM_64=1)
add_definitions(-DVIXL_INCLUDE_TARGET_AARCH64=1)
add_definitions(-DVIXL_CODE_BUFFER_MMAP=1)
list(APPEND SRCS
Interface/Core/JIT/Arm64/JIT.cpp
Interface/Core/JIT/Arm64/ALUOps.cpp
Interface/Core/JIT/Arm64/AtomicOps.cpp
Interface/Core/JIT/Arm64/BranchOps.cpp
Interface/Core/JIT/Arm64/ConversionOps.cpp
Interface/Core/JIT/Arm64/EncryptionOps.cpp
Interface/Core/JIT/Arm64/FlagOps.cpp
Interface/Core/JIT/Arm64/MemoryOps.cpp
Interface/Core/JIT/Arm64/MiscOps.cpp
Interface/Core/JIT/Arm64/MoveOps.cpp
Interface/Core/JIT/Arm64/VectorOps.cpp)
list(APPEND JIT_LIBS vixl)
endif()
set(DEFINES )
if (_M_X86_64)
list(APPEND DEFINES -D_M_X86_64=1)
endif()
if (_M_ARM_64)
list(APPEND DEFINES -D_M_ARM_64=1)
endif()
if (ENABLE_JIT_X86_64)
list(APPEND SRCS
Interface/Core/JIT/x86_64/JIT.cpp
Interface/Core/JIT/x86_64/ALUOps.cpp
Interface/Core/JIT/x86_64/AtomicOps.cpp
Interface/Core/JIT/x86_64/BranchOps.cpp
Interface/Core/JIT/x86_64/ConversionOps.cpp
Interface/Core/JIT/x86_64/EncryptionOps.cpp
Interface/Core/JIT/x86_64/FlagOps.cpp
Interface/Core/JIT/x86_64/MemoryOps.cpp
Interface/Core/JIT/x86_64/MiscOps.cpp
Interface/Core/JIT/x86_64/MoveOps.cpp
Interface/Core/JIT/x86_64/VectorOps.cpp)
list(APPEND DEFINES -DJIT_X86_64)
endif()
if (ENABLE_JIT_ARM64)
list(APPEND DEFINES -DJIT_ARM64)
list(APPEND SRCS
Interface/Core/JIT/Arm64/JIT.cpp
Interface/Core/JIT/Arm64/ALUOps.cpp
Interface/Core/JIT/Arm64/AtomicOps.cpp
Interface/Core/JIT/Arm64/BranchOps.cpp
Interface/Core/JIT/Arm64/ConversionOps.cpp
Interface/Core/JIT/Arm64/EncryptionOps.cpp
Interface/Core/JIT/Arm64/FlagOps.cpp
Interface/Core/JIT/Arm64/MemoryOps.cpp
Interface/Core/JIT/Arm64/MiscOps.cpp
Interface/Core/JIT/Arm64/MoveOps.cpp
Interface/Core/JIT/Arm64/VectorOps.cpp)
endif()
if (ENABLE_JITSYMBOLS)
add_definitions(-DENABLE_JITSYMBOLS=1)
list(APPEND DEFINES -DENABLE_JITSYMBOLS=1)
endif()
# Generate IR include file
@@ -251,21 +214,64 @@ add_custom_command(
set_source_files_properties(${OUTPUT_IR_NAME} PROPERTIES
GENERATED TRUE)
# Create teh target
# Create the target
add_custom_target(IR_INC
DEPENDS "${OUTPUT_NAME}"
DEPENDS "${OUTPUT_IR_DOC}")
# Generate the configuration include file
set(OUTPUT_CONFIG_FOLDER "${CMAKE_BINARY_DIR}/include/FEXCore/Config")
set(OUTPUT_CONFIG_NAME "${OUTPUT_CONFIG_FOLDER}/ConfigValues.inl")
set(OUTPUT_CONFIG_OPTION_NAME "${OUTPUT_CONFIG_FOLDER}/ConfigOptions.inl")
set(INPUT_CONFIG_NAME "${CMAKE_CURRENT_SOURCE_DIR}/Interface/Config/Config.json")
set(OUTPUT_MAN_NAME "${CMAKE_BINARY_DIR}/generated/FEX.1")
add_custom_target(CREATE_CONFIG_FOLDER ALL
COMMAND ${CMAKE_COMMAND} -E make_directory "${OUTPUT_CONFIG_FOLDER}")
add_custom_command(
OUTPUT "${OUTPUT_CONFIG_NAME}"
OUTPUT "${OUTPUT_CONFIG_OPTION_NAME}"
OUTPUT "${OUTPUT_MAN_NAME}"
DEPENDS "${INPUT_CONFIG_NAME}"
DEPENDS CREATE_CONFIG_FOLDER
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/config_generator.py"
COMMAND "python3" "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/config_generator.py" "${INPUT_CONFIG_NAME}" "${OUTPUT_CONFIG_NAME}" "${OUTPUT_MAN_NAME}"
"${OUTPUT_CONFIG_OPTION_NAME}"
)
set_source_files_properties(${OUTPUT_CONFIG_NAME} PROPERTIES
GENERATED TRUE)
set_source_files_properties(${OUTPUT_CONFIG_OPTION_NAME} PROPERTIES
GENERATED TRUE)
set_source_files_properties(${OUTPUT_MAN_NAME} PROPERTIES
GENERATED TRUE)
# Create the target
add_custom_target(CONFIG_INC
DEPENDS "${OUTPUT_CONFIG_NAME}"
DEPENDS "${OUTPUT_CONFIG_OPTION_NAME}"
DEPENDS "${OUTPUT_MAN_NAME}")
# Install the man page
install(FILES ${OUTPUT_MAN_NAME} DESTINATION ${MAN_DIR}/man1)
# Add in diagnostic colours if the option is available.
# Ninja code generator will kill colours if this isn't here
check_cxx_compiler_flag(-fdiagnostics-color=always GCC_COLOR)
check_cxx_compiler_flag(-fcolor-diagnostics CLANG_COLOR)
function(AddLibrary Name Type)
function(AddObject Name Type)
add_library(${Name} ${Type} ${SRCS})
add_dependencies(${Name} IR_INC)
target_link_libraries(${Name} pthread rt ${JIT_LIBS} ${LINUX_LIBS} dl)
add_dependencies(${Name} CONFIG_INC)
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
set_target_properties(${Name} PROPERTIES C_VISIBILITY_PRESET hidden)
set_target_properties(${Name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
set_target_properties(${Name} PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
target_include_directories(${Name} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
@@ -275,9 +281,16 @@ function(AddLibrary Name Type)
target_include_directories(${Name} PUBLIC "${PROJECT_SOURCE_DIR}/include/")
target_include_directories(${Name} PUBLIC "${CMAKE_BINARY_DIR}/include/")
target_compile_definitions(${Name} PRIVATE ${DEFINES})
target_compile_options(${Name}
PRIVATE
-Wno-trigraphs -Wall)
-Wall
-Werror=implicit-fallthrough
-Wno-trigraphs
-ffunction-sections
)
if (GCC_COLOR)
target_compile_options(${Name}
@@ -291,6 +304,29 @@ function(AddLibrary Name Type)
endif()
endfunction()
function(AddLibrary Name Type)
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
set_target_properties(${Name} PROPERTIES C_VISIBILITY_PRESET hidden)
set_target_properties(${Name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
set_target_properties(${Name} PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
target_include_directories(${Name} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
target_include_directories(${Name} PUBLIC "${PROJECT_SOURCE_DIR}/include/")
target_include_directories(${Name} PUBLIC "${CMAKE_BINARY_DIR}/include/")
if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
target_link_options(${Name}
PRIVATE
"LINKER:--gc-sections"
"LINKER:--strip-all"
"LINKER:--as-needed"
)
endif()
endfunction()
AddObject(${PROJECT_NAME}_object OBJECT)
AddLibrary(${PROJECT_NAME} STATIC)
AddLibrary(${PROJECT_NAME}_shared SHARED)
+7 -6
View File
@@ -1,5 +1,6 @@
#pragma once
#include "Common/MathUtils.h"
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/LogManager.h>
#include <cstring>
@@ -16,16 +17,16 @@ struct BitSet final {
ElementType *Memory;
void Allocate(size_t Elements) {
size_t AllocateSize = AlignUp(Elements, MinimumSizeBits) / MinimumSize;
LogMan::Throw::A((AllocateSize * MinimumSize) >= Elements, "Fail");
Memory = static_cast<ElementType*>(malloc(AllocateSize));
LOGMAN_THROW_A((AllocateSize * MinimumSize) >= Elements, "Fail");
Memory = static_cast<ElementType*>(FEXCore::Allocator::malloc(AllocateSize));
}
void Realloc(size_t Elements) {
size_t AllocateSize = AlignUp(Elements, MinimumSizeBits) / MinimumSize;
LogMan::Throw::A((AllocateSize * MinimumSize) >= Elements, "Fail");
Memory = static_cast<ElementType*>(realloc(Memory, AllocateSize));
LOGMAN_THROW_A((AllocateSize * MinimumSize) >= Elements, "Fail");
Memory = static_cast<ElementType*>(FEXCore::Allocator::realloc(Memory, AllocateSize));
}
void Free() {
free(Memory);
FEXCore::Allocator::free(Memory);
Memory = nullptr;
}
bool Get(T Element) {
@@ -60,7 +61,7 @@ struct BitSetView final {
ElementType *Memory;
void GetView(BitSet<T> &Set, uint64_t ElementOffset) {
LogMan::Throw::A((ElementOffset % MinimumSize) == 0,
LOGMAN_THROW_A((ElementOffset % MinimumSize) == 0,
"Bitset view offset needs to be aligned to size of backing element");
Memory = &Set.Memory[ElementOffset / MinimumSizeBits];
}
@@ -96,6 +96,7 @@ extFloat80_t
switch ( roundingMode ) {
case softfloat_round_near_even:
if ( !(sigA & UINT64_C( 0x7FFFFFFFFFFFFFFF )) ) break;
__attribute__((fallthrough));
case softfloat_round_near_maxMag:
if ( exp == 0x3FFE ) goto mag1;
break;
+7
View File
@@ -38,4 +38,11 @@ namespace FEXCore::StrConv {
*Result = Value;
return true;
}
template <typename T,
typename = std::enable_if<std::is_enum<T>::value, T>>
[[maybe_unused]] static bool Conv(std::string_view Value, T *Result) {
*Result = static_cast<T>(std::stoull(std::string(Value), nullptr, 0));
return true;
}
}
+251 -105
View File
@@ -4,118 +4,123 @@
#include "Interface/Context/Context.h"
#include <FEXCore/Config/Config.h>
#include <filesystem>
#include <pwd.h>
#include <map>
#include <sys/sysinfo.h>
#include <unistd.h>
namespace FEXCore::Config {
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config) {
switch (Option) {
case FEXCore::Config::CONFIG_MULTIBLOCK:
CTX->Config.Multiblock = Config != 0;
break;
case FEXCore::Config::CONFIG_MAXBLOCKINST:
CTX->Config.MaxInstPerBlock = Config;
break;
case FEXCore::Config::CONFIG_DEFAULTCORE:
CTX->Config.Core = static_cast<FEXCore::Config::ConfigCore>(Config);
break;
case FEXCore::Config::CONFIG_VIRTUALMEMSIZE:
CTX->Config.VirtualMemSize = Config;
break;
case FEXCore::Config::CONFIG_SINGLESTEP:
CTX->Config.RunningMode = Config != 0 ? FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP : FEXCore::Context::CoreRunningMode::MODE_RUN;
break;
case FEXCore::Config::CONFIG_GDBSERVER:
Config != 0 ? CTX->StartGdbServer() : CTX->StopGdbServer();
break;
case FEXCore::Config::CONFIG_IS64BIT_MODE:
CTX->Config.Is64BitMode = Config != 0;
break;
case FEXCore::Config::CONFIG_TSO_ENABLED:
CTX->Config.TSOEnabled = Config != 0;
break;
case FEXCore::Config::CONFIG_SMC_CHECKS:
CTX->Config.SMCChecks = static_cast<FEXCore::Config::ConfigSMCChecks>(Config);
break;
case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS:
CTX->Config.ABILocalFlags = Config != 0;
break;
case FEXCore::Config::CONFIG_ABI_NO_PF:
CTX->Config.ABINoPF = Config != 0;
break;
case FEXCore::Config::CONFIG_VALIDATE_IR_PARSER:
CTX->Config.ValidateIRarser = Config != 0;
break;
case FEXCore::Config::CONFIG_AOTIR_GENERATE:
CTX->Config.AOTIRCapture = Config != 0;
break;
case FEXCore::Config::CONFIG_AOTIR_LOAD:
CTX->Config.AOTIRLoad = Config != 0;
break;
default: LogMan::Msg::A("Unknown configuration option");
char const* FindUserHomeThroughUID() {
auto passwd = getpwuid(geteuid());
if (passwd) {
return passwd->pw_dir;
}
return nullptr;
}
const char *GetHomeDirectory() {
char const *HomeDir = getenv("HOME");
// Try to get home directory from uid
if (!HomeDir) {
HomeDir = FindUserHomeThroughUID();
}
// try the PWD
if (!HomeDir) {
HomeDir = getenv("PWD");
}
// Still doesn't exit? You get local
if (!HomeDir) {
HomeDir = ".";
}
return HomeDir;
}
std::string GetConfigDirectory(bool Global) {
std::string ConfigDir;
if (Global) {
ConfigDir = GLOBAL_DATA_DIRECTORY;
}
else {
char const *HomeDir = GetHomeDirectory();
char const *ConfigXDG = getenv("XDG_CONFIG_HOME");
char const *ConfigOverride = getenv("FEX_APP_CONFIG_LOCATION");
if (ConfigOverride) {
// Config override completely overrides the config directory
ConfigDir = ConfigOverride;
}
else {
ConfigDir = ConfigXDG ? ConfigXDG : HomeDir;
ConfigDir += "/.fex-emu/";
}
// Ensure the folder structure is created for our configuration
if (!std::filesystem::exists(ConfigDir) &&
!std::filesystem::create_directories(ConfigDir)) {
LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigDir.c_str());
// Let's go local in this case
return "./";
}
}
return ConfigDir;
}
std::string GetConfigFileLocation() {
std::string ConfigFile{};
const char *AppConfig = getenv("FEX_APP_CONFIG");
if (AppConfig) {
// App config environment variable overwrites only the config file
ConfigFile = AppConfig;
}
else {
ConfigFile = GetConfigDirectory(false) + "Config.json";
}
return ConfigFile;
}
std::string GetApplicationConfig(std::string &Filename, bool Global) {
std::string ConfigFile = GetConfigDirectory(Global);
if (!Global &&
!std::filesystem::exists(ConfigFile) &&
!std::filesystem::create_directories(ConfigFile)) {
LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigFile.c_str());
// Let's go local in this case
return "./";
}
ConfigFile += "AppConfig/" + Filename + ".json";
return ConfigFile;
}
std::string GetDataDirectory() {
std::string DataDir{};
char const *HomeDir = GetHomeDirectory();
char const *DataXDG = getenv("XDG_DATA_HOME");
char const *DataOverride = getenv("FEX_APP_DATA_LOCATION");
if (DataOverride) {
// Data override will override the complete directory
DataDir = DataOverride;
}
else {
DataDir = DataXDG ?: HomeDir;
DataDir += "/.fex-emu/";
}
return DataDir;
}
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config) {
}
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, std::string const &Config) {
switch (Option) {
case CONFIG_ROOTFSPATH:
CTX->Config.RootFSPath = Config;
break;
case CONFIG_THUNKLIBSPATH:
CTX->Config.ThunkLibsPath = Config;
break;
case FEXCore::Config::CONFIG_DUMPIR:
CTX->Config.DumpIR = Config;
break;
default: LogMan::Msg::A("Unknown configuration option");
}
}
uint64_t GetConfig(FEXCore::Context::Context *CTX, ConfigOption Option) {
switch (Option) {
case FEXCore::Config::CONFIG_MULTIBLOCK:
return CTX->Config.Multiblock;
break;
case FEXCore::Config::CONFIG_MAXBLOCKINST:
return CTX->Config.MaxInstPerBlock;
break;
case FEXCore::Config::CONFIG_DEFAULTCORE:
return CTX->Config.Core;
break;
case FEXCore::Config::CONFIG_VIRTUALMEMSIZE:
return CTX->Config.VirtualMemSize;
break;
case FEXCore::Config::CONFIG_SINGLESTEP:
return CTX->Config.RunningMode == FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP ? 1 : 0;
case FEXCore::Config::CONFIG_GDBSERVER:
return CTX->GetGdbServerStatus();
break;
case FEXCore::Config::CONFIG_IS64BIT_MODE:
return CTX->Config.Is64BitMode;
break;
case FEXCore::Config::CONFIG_TSO_ENABLED:
return CTX->Config.TSOEnabled;
break;
case FEXCore::Config::CONFIG_SMC_CHECKS:
return CTX->Config.SMCChecks;
break;
case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS:
return CTX->Config.ABILocalFlags;
break;
case FEXCore::Config::CONFIG_ABI_NO_PF:
return CTX->Config.ABINoPF;
break;
case FEXCore::Config::CONFIG_VALIDATE_IR_PARSER:
return CTX->Config.ValidateIRarser;
break;
case FEXCore::Config::CONFIG_AOTIR_GENERATE:
return CTX->Config.AOTIRCapture;
break;
case FEXCore::Config::CONFIG_AOTIR_LOAD:
return CTX->Config.AOTIRLoad;
break;
default: LogMan::Msg::A("Unknown configuration option");
}
return 0;
}
@@ -149,6 +154,7 @@ namespace FEXCore::Config {
private:
void MergeConfigMap(const LayerOptions &Options);
void MergeEnvironmentVariables(ConfigOption const &Option, LayerValue const &Value);
};
void MetaLayer::Load() {
@@ -163,10 +169,56 @@ namespace FEXCore::Config {
}
}
void MetaLayer::MergeEnvironmentVariables(ConfigOption const &Option, LayerValue const &Value) {
// Environment variables need a bit of additional work
// We want to merge the arrays rather than overwrite entirely
auto MetaEnvironment = OptionMap.find(Option);
if (MetaEnvironment == OptionMap.end()) {
// Doesn't exist, just insert
OptionMap.insert_or_assign(Option, Value);
return;
}
// If an environment variable exists in both current meta and in the incoming layer then the meta layer value is overwritten
std::unordered_map<std::string, std::string> LookupMap;
const auto AddToMap = [&LookupMap](FEXCore::Config::LayerValue const &Value) {
for (const auto &EnvVar : Value) {
const auto ItEq = EnvVar.find_first_of('=');
if (ItEq == std::string::npos) {
// Broken environment variable
// Skip
continue;
}
auto Key = std::string(EnvVar.begin(), EnvVar.begin() + ItEq);
auto Value = std::string(EnvVar.begin() + ItEq + 1, EnvVar.end());
// Add the key to the map, overwriting whatever previous value was there
LookupMap.insert_or_assign(std::move(Key), std::move(Value));
}
};
AddToMap(MetaEnvironment->second);
AddToMap(Value);
// Now with the two layers merged in the map
// Add all the values to the option
Erase(Option);
for (auto &Val : LookupMap) {
// Set will emplace multiple options in to its list
Set(Option, Val.first + "=" + Val.second);
}
}
void MetaLayer::MergeConfigMap(const LayerOptions &Options) {
// Insert this layer's options, overlaying previous options that exist here
for (auto &it : Options) {
OptionMap.insert_or_assign(it.first, it.second);
if (it.first == FEXCore::Config::ConfigOption::CONFIG_ENV) {
MergeEnvironmentVariables(it.first, it.second);
}
else {
OptionMap.insert_or_assign(it.first, it.second);
}
}
}
@@ -189,8 +241,91 @@ namespace FEXCore::Config {
}
}
std::string ExpandPath(std::string PathName) {
if (PathName.empty()) {
return {};
}
std::filesystem::path Path{PathName};
// Expand home if it exists
if (Path.is_relative()) {
std::string Home = getenv("HOME") ?: "";
// Home expansion only works if it is the first character
// This matches bash behaviour
if (PathName.at(0) == '~') {
PathName.replace(0, 1, Home);
return PathName;
}
// Expand relative path to absolute
Path = std::filesystem::absolute(Path);
// Only return if it exists
if (std::filesystem::exists(Path)) {
return Path;
}
}
return {};
}
void ReloadMetaLayer() {
Meta->Load();
// Do configuration option fix ups after everything is reloaded
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THREADS)) {
FEX_CONFIG_OPT(Cores, THREADS);
if (Cores == 0) {
// When the number of emulated CPU cores is zero then auto detect
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_THREADS, std::to_string(get_nprocs_conf()));
}
}
auto ExpandPathIfExists = [](FEXCore::Config::ConfigOption Config, std::string PathName) {
auto NewPath = ExpandPath(PathName);
if (!NewPath.empty()) {
FEXCore::Config::EraseSet(Config, NewPath);
}
};
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_ROOTFS)) {
FEX_CONFIG_OPT(PathName, ROOTFS);
auto ExpandedString = ExpandPath(PathName());
if (!ExpandedString.empty()) {
// Adjust the path if it ended up being relative
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, ExpandedString);
}
else if (!PathName().empty()) {
// If the filesystem doesn't exist then let's see if it exists in the fex-emu folder
std::string NamedRootFS = GetDataDirectory() + "RootFS/" + PathName();
if (std::filesystem::exists(NamedRootFS)) {
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, NamedRootFS);
}
}
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKHOSTLIBS)) {
FEX_CONFIG_OPT(PathName, THUNKHOSTLIBS);
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKHOSTLIBS, PathName());
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKGUESTLIBS)) {
FEX_CONFIG_OPT(PathName, THUNKGUESTLIBS);
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKGUESTLIBS, PathName());
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKCONFIG)) {
FEX_CONFIG_OPT(PathName, THUNKCONFIG);
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKCONFIG, PathName());
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_OUTPUTLOG)) {
FEX_CONFIG_OPT(PathName, OUTPUTLOG);
if (PathName() != "stdout" && PathName() != "stderr") {
ExpandPathIfExists(FEXCore::Config::CONFIG_OUTPUTLOG, PathName());
}
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_SINGLESTEP)) {
// Single stepping also enforces single instruction size blocks
Set(FEXCore::Config::ConfigOption::CONFIG_MAXINST, std::to_string(1u));
}
}
void AddLayer(std::unique_ptr<FEXCore::Config::Layer> _Layer) {
@@ -213,6 +348,10 @@ namespace FEXCore::Config {
Meta->Set(Option, Data);
}
void Erase(ConfigOption Option) {
Meta->Erase(Option);
}
void EraseSet(ConfigOption Option, std::string Data) {
Meta->EraseSet(Option, Data);
}
@@ -252,8 +391,14 @@ namespace FEXCore::Config {
}
}
template bool Value<bool>::GetIfExists(FEXCore::Config::ConfigOption Option, bool Default);
template uint8_t Value<uint8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint8_t Default);
template bool Value<bool>::GetIfExists(FEXCore::Config::ConfigOption Option, bool Default);
template int8_t Value<int8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int8_t Default);
template uint8_t Value<uint8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint8_t Default);
template int16_t Value<int16_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int16_t Default);
template uint16_t Value<uint16_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint16_t Default);
template int32_t Value<int32_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int32_t Default);
template uint32_t Value<uint32_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint32_t Default);
template int64_t Value<int64_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int64_t Default);
template uint64_t Value<uint64_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint64_t Default);
// Constructor
@@ -270,5 +415,6 @@ namespace FEXCore::Config {
*List = **Value;
}
}
template void Value<std::string>::GetListIfExists(FEXCore::Config::ConfigOption Option, std::list<std::string> *List);
}
+259
View File
@@ -0,0 +1,259 @@
{
"Options": {
"CPU": {
"Core": {
"Type": "uint32",
"Default": "FEXCore::Config::ConfigCore::CONFIG_IRJIT",
"TextDefault": "irjit",
"ShortArg": "c",
"Choices": [ "irint", "irjit", "host" ],
"ArgumentHandler": "CoreHandler",
"Desc": [
"Which CPU core to use",
"host only exists on x86_64",
"[irint, irjit, host]"
]
},
"Multiblock": {
"Type": "bool",
"Default": "true",
"ShortArg": "m",
"Desc": [
"Controls multiblock code compilation"
]
},
"MaxInst": {
"Type": "int32",
"Default": "5000",
"ShortArg": "n",
"Desc": [
"Maximum number of instruction to store in a block"
]
},
"Threads": {
"Type": "uint32",
"Default": "1",
"ShortArg": "T",
"Desc": [
"Number of physical hardware threads to tell the process we have.",
"0 will auto detect."
]
}
},
"Emulation": {
"RootFS": {
"Type": "str",
"Default": "",
"ShortArg": "R",
"Desc": [
"Which Root filesystem prefix to use",
"This can be a filesystem path",
"\teg: ~/RootFS/Debian_x86_64",
"Or this can be a name of a rootfs",
"If the named rootfs exists in the FEX data folder then it will use that one",
"\teg: $HOME/.fex-emu/RootFS/<RootFS name>/",
"Or if you have XDG_DATA_HOME the config will search in that directory",
"\teg: $XDG_DATA_HOME/.fex-emu/RootFS/<RootFS name>/"
]
},
"ThunkHostLibs": {
"Type": "str",
"Default": "",
"ShortArg": "t",
"Desc": [
"Folder to find the host-side thunking libraries."
]
},
"ThunkGuestLibs": {
"Type": "str",
"Default": "",
"ShortArg": "j",
"Desc": [
"Folder to find the guest-side thunking libraries."
]
},
"ThunkConfig": {
"Type": "str",
"Default": "",
"ShortArg": "k",
"Desc": [
"A json file specifying where to overlay the thunks."
]
},
"Env": {
"Type": "strarray",
"Default": "",
"ShortArg": "E",
"Desc": [
"Adds an environment variable to the emulated environment."
]
}
},
"Debug": {
"SingleStep": {
"Type": "bool",
"Default": "false",
"ShortArg": "S",
"Desc": [
"Single stepping configuration."
]
},
"GdbServer": {
"Type": "bool",
"Default": "false",
"ShortArg": "G",
"Desc": [
"Enables the GDB server."
]
},
"DumpIR": {
"Type": "str",
"Default": "no",
"Desc": [
"Folder to dump the IR in to.",
"[no, stdout, stderr, <Folder>]"
]
},
"DumpGPRs": {
"Type": "bool",
"Default": "false",
"ShortArg": "g",
"Desc": [
"When the test harness ends, print the GPR state."
]
},
"O0": {
"Type": "bool",
"Default": "false",
"ShortArg": "O0",
"Desc": [
"Disables optimizations passes for debugging."
]
},
"Force32BitAllocator": {
"Type": "bool",
"Default": "false",
"Desc": [
"Forces use of the 32-bit allocator on 32-bit applications",
"Used to work around ulimit problems of CI runner",
"Potentially useful for debugging memory problems",
"32-bit allocator is always used if your host kernel is older than 4.17"
]
}
},
"Logging": {
"SilentLog": {
"Type": "bool",
"Default": "true",
"ShortArg": "s",
"Desc": [
"Disables logging"
]
},
"OutputLog": {
"Type": "str",
"Default": "stderr",
"ShortArg": "o",
"Desc": [
"File to write FEX output to.",
"[stdout, stderr, <Filename>]"
]
}
},
"Hacks": {
"SMCChecks": {
"Type": "uint8",
"Default": "FEXCore::Config::CONFIG_SMC_MMAN",
"TextDefault": "mman",
"ArgumentHandler": "SMCCheckHandler",
"Desc": [
"Checks code for modification before execution.",
"\tnone: No checks",
"\tmman: Invalidate on mmap, mprotect, munmap",
"\tfull: Validate code before every run (slow)"
]
},
"TSOEnabled": {
"Type": "bool",
"Default": "true",
"Desc": [
"Controls TSO IR ops.",
"Highly likely to break any multithreaded application if disabled."
]
},
"ABILocalFlags": {
"Type": "bool",
"Default": "false",
"Desc": [
"When enabled enables an optimization around flags.",
"Assumes flags are not used across cals.",
"Hand-written assembly can violate this assumption."
]
},
"ABINoPF": {
"Type": "bool",
"Default": "false",
"Desc": [
"When enabled enables an optimization around parity flag calculation.",
"Removes the calculation of the parity flag from GPR instructions.",
"Assuming no uses rely on it"
]
},
"ParanoidTSO": {
"Type": "bool",
"Default": "false",
"Desc": [
"Makes TSO operations even more strict.",
"Forces vector loadstores to also become atomic."
]
}
},
"Misc": {
"AOTIRCapture": {
"Type": "bool",
"Default": "false",
"Desc": [
"Captures IR and generates an AOT IR cache.",
"Captures both the loaded executable and libraries it loads."
]
},
"AOTIRGenerate": {
"Type": "bool",
"Default": "false",
"Desc": [
"Scans file for executable code and generates an AOT IR cache.",
"Does not run the executable."
]
},
"AOTIRLoad": {
"Type": "bool",
"Default": "false",
"Desc": [
"Loads an AOT IR cache for the loaded executable."
]
}
}
},
"UnnamedOptions": {
"Misc": {
"IS_INTERPRETER": {
"Type": "bool",
"Default": "false"
},
"INTERPRETER_INSTALLED": {
"Type": "bool",
"Default": "false"
},
"APP_FILENAME": {
"Type": "str",
"Default": ""
},
"IS64BIT_MODE": {
"Type": "bool",
"Default": "false"
}
}
}
}
+20 -30
View File
@@ -23,6 +23,9 @@ namespace FEXCore::Context {
}
void DestroyContext(FEXCore::Context::Context *CTX) {
if (CTX->ParentThread) {
CTX->DestroyThread(CTX->ParentThread);
}
delete CTX;
}
@@ -47,6 +50,9 @@ namespace FEXCore::Context {
CTX->Step();
}
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP) {
CTX->CompileBlock(CTX->ParentThread->CurrentFrame, GuestRIP);
}
FEXCore::Context::ExitReason RunUntilExit(FEXCore::Context::Context *CTX) {
return CTX->RunUntilExit();
@@ -65,11 +71,11 @@ namespace FEXCore::Context {
}
void GetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
memcpy(State, &CTX->ParentThread->State.State, sizeof(FEXCore::Core::CPUState));
memcpy(State, CTX->ParentThread->CurrentFrame, sizeof(FEXCore::Core::CPUState));
}
void SetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
memcpy(&CTX->ParentThread->State.State, State, sizeof(FEXCore::Core::CPUState));
memcpy(CTX->ParentThread->CurrentFrame, State, sizeof(FEXCore::Core::CPUState));
}
void Pause(FEXCore::Context::Context *CTX) {
@@ -84,10 +90,6 @@ namespace FEXCore::Context {
CTX->CustomCPUFactory = std::move(Factory);
}
void SetFallbackCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory) {
CTX->FallbackCPUFactory = std::move(Factory);
}
bool AddVirtualMemoryMapping([[maybe_unused]] FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t VirtualAddress, [[maybe_unused]] uint64_t PhysicalAddress, [[maybe_unused]] uint64_t Size) {
return false;
}
@@ -123,20 +125,12 @@ namespace FEXCore::Context {
CTX->StopThread(Thread);
}
void DeleteForkedThreads(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
// This function is called after fork
// We need to cleanup some of the thread data that is dead
for (auto &DeadThread : CTX->Threads) {
if (DeadThread == Thread) {
continue;
}
void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->DestroyThread(Thread);
}
// Setting running to false ensures that when they are shutdown we won't send signals to kill them
DeadThread->State.RunningEvents.Running = false;
}
// We now only have one thread
CTX->IdleWaitRefCount = 1;
void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->CleanupAfterFork(Thread);
}
void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation) {
@@ -147,11 +141,11 @@ namespace FEXCore::Context {
CTX->SyscallHandler = Handler;
}
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, [[maybe_unused]] uint32_t Leaf) {
return CTX->CPUID.RunFunction(Function);
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf) {
return CTX->CPUID.RunFunction(Function, Leaf);
}
void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::istream>(const std::string&)> CacheReader) {
void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader) {
CTX->AOTIRLoader = CacheReader;
}
@@ -159,6 +153,10 @@ namespace FEXCore::Context {
return CTX->WriteAOTIRCache(CacheWriter);
}
void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
CTX->WriteFilesWithCode(Writer);
}
void AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name) {
return CTX->AddNamedRegion(Base, Length, Offset, Name);
}
@@ -178,10 +176,6 @@ namespace Debug {
return CTX->GetRuntimeStatsForThread(Thread);
}
FEXCore::Core::CPUState GetCPUState(FEXCore::Context::Context *CTX) {
return CTX->GetCPUState();
}
bool GetDebugDataForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::Core::DebugData *Data) {
return CTX->GetDebugDataForRIP(RIP, Data);
}
@@ -198,10 +192,6 @@ namespace Debug {
// void SetIRForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::IR::IntrusiveIRList *const ir) {
// CTX->SetIRForRIP(RIP, ir);
// }
FEXCore::Core::ThreadState *GetThreadState(FEXCore::Context::Context *CTX) {
return CTX->GetThreadState();
}
}
}
+96 -54
View File
@@ -12,14 +12,15 @@
#include <FEXCore/Utils/Event.h>
#include <stdint.h>
#include <memory>
#include <map>
#include <unordered_map>
#include <set>
#include <mutex>
#include <istream>
#include <ostream>
#include <functional>
#include <istream>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <ostream>
#include <set>
#include <unordered_map>
namespace FEXCore {
class ThunkHandler;
@@ -28,7 +29,8 @@ class GdbServer;
class SiganlDelegator;
namespace CPU {
class JITCore;
class Arm64JITCore;
class X86JITCore;
}
namespace HLE {
class SyscallHandler;
@@ -50,36 +52,73 @@ namespace FEXCore::Context {
MODE_SINGLESTEP = 1,
};
struct AOTIRCaptureCacheEntry {
uint64_t start;
uint64_t len;
uint64_t crc;
IR::IRListView *IR;
IR::RegisterAllocationData *RAData;
};
struct AOTIRInlineEntry {
uint64_t GuestHash;
uint64_t GuestLength;
/* RAData followed by IRData */
uint8_t InlineData[0];
IR::RegisterAllocationData *GetRAData();
IR::IRListView *GetIRData();
};
struct AOTIRInlineIndexEntry {
uint64_t GuestStart;
uint64_t DataOffset;
};
struct AOTIRInlineIndex {
uint64_t Count;
uint64_t DataBase;
AOTIRInlineIndexEntry Entries[0];
AOTIRInlineEntry *Find(uint64_t GuestStart);
AOTIRInlineEntry *GetInlineEntry(uint64_t DataOffset);
};
struct Context {
friend class FEXCore::HLE::SyscallHandler;
friend class FEXCore::CPU::JITCore;
#ifdef JIT_ARM64
friend class FEXCore::CPU::Arm64JITCore;
#endif
#ifdef JIT_X86_64
friend class FEXCore::CPU::X86JITCore;
#endif
friend class FEXCore::IR::Validation::IRValidation;
struct {
bool Multiblock {false};
bool BreakOnFrontendFailure {true};
int64_t MaxInstPerBlock {-1LL};
uint64_t VirtualMemSize {1ULL << 36};
CoreRunningMode RunningMode {CoreRunningMode::MODE_RUN};
FEXCore::Config::ConfigCore Core {FEXCore::Config::CONFIG_INTERPRETER};
bool GdbServer {false};
std::string RootFSPath;
std::string ThunkLibsPath;
bool Is64BitMode {true};
bool TSOEnabled {true};
FEXCore::Config::ConfigSMCChecks SMCChecks {FEXCore::Config::CONFIG_SMC_MMAN};
bool ABILocalFlags {false};
bool ABINoPF {false};
bool AOTIRCapture {false};
bool AOTIRLoad {false};
std::string DumpIR;
uint64_t VirtualMemSize{1ULL << 36};
// this is for internal use
bool ValidateIRarser { false };
FEX_CONFIG_OPT(Multiblock, MULTIBLOCK);
FEX_CONFIG_OPT(SingleStepConfig, SINGLESTEP);
FEX_CONFIG_OPT(GdbServer, GDBSERVER);
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
FEX_CONFIG_OPT(ABILocalFlags, ABILOCALFLAGS);
FEX_CONFIG_OPT(ABINoPF, ABINOPF);
FEX_CONFIG_OPT(AOTIRCapture, AOTIRCAPTURE);
FEX_CONFIG_OPT(AOTIRGenerate, AOTIRGENERATE);
FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD);
FEX_CONFIG_OPT(SMCChecks, SMCCHECKS);
FEX_CONFIG_OPT(Core, CORE);
FEX_CONFIG_OPT(MaxInstPerBlock, MAXINST);
FEX_CONFIG_OPT(RootFSPath, ROOTFS);
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
FEX_CONFIG_OPT(DumpIR, DUMPIR);
} Config;
using IntCallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
@@ -105,30 +144,30 @@ namespace FEXCore::Context {
std::unique_ptr<FEXCore::ThunkHandler> ThunkHandler;
CustomCPUFactoryType CustomCPUFactory;
CustomCPUFactoryType FallbackCPUFactory;
std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> CustomExitHandler;
struct AOTIRCacheEntry {
uint64_t start;
uint64_t len;
uint64_t crc;
IR::IRListView *IR;
IR::RegisterAllocationData *RAData;
AOTIRInlineIndex *Array;
void *mapping;
size_t size;
};
std::function<std::unique_ptr<std::istream>(const std::string&)> AOTIRLoader;
std::unordered_map<std::string, std::map<uint64_t, AOTIRCacheEntry>> AOTIRCache;
std::unordered_map<std::string, AOTIRCacheEntry> AOTIRCache;
std::function<int(const std::string&)> AOTIRLoader;
std::unordered_map<std::string, std::map<uint64_t, AOTIRCaptureCacheEntry>> AOTIRCaptureCache;
struct AddrToFileEntry {
uint64_t Start;
uint64_t Len;
uint64_t Offset;
std::string fileid;
std::string filename;
void *CachedFileEntry;
bool ContainsCode;
};
std::map<uint64_t, AddrToFileEntry> AddrToFile;
std::map<std::string, std::string> FilesWithCode;
#ifdef BLOCKSTATS
std::unique_ptr<FEXCore::BlockSamplingData> BlockData;
@@ -162,27 +201,30 @@ namespace FEXCore::Context {
static void RemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
// Wrapper which takes CpuStateFrame instead of InternalThreadState
static void RemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
RemoveCodeEntry(Frame->Thread, GuestRIP);
}
// Debugger interface
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
uint64_t GetThreadCount() const;
FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(uint64_t Thread);
FEXCore::Core::CPUState GetCPUState();
bool GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data);
bool FindHostCodeForRIP(uint64_t RIP, uint8_t **Code);
// XXX:
// bool FindIRForRIP(uint64_t RIP, FEXCore::IR::IntrusiveIRList **ir);
// void SetIRForRIP(uint64_t RIP, FEXCore::IR::IntrusiveIRList *const ir);
FEXCore::Core::ThreadState *GetThreadState();
void LoadEntryList();
std::tuple<FEXCore::IR::IRListView *, FEXCore::IR::RegisterAllocationData *, uint64_t, uint64_t, uint64_t, uint64_t> GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool, uint64_t, uint64_t> CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
uintptr_t CompileBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
uintptr_t CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
// same as CompileBlock, but aborts on failure
void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
bool LoadAOTIRCache(std::istream &stream);
bool LoadAOTIRCache(int streamfd);
bool WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer);
// Used for thread creation from syscalls
void InitializeCompiler(FEXCore::Core::InternalThreadState* State, bool CompileThread);
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
@@ -191,6 +233,9 @@ namespace FEXCore::Context {
void CopyMemoryMapping(FEXCore::Core::InternalThreadState *ParentThread, FEXCore::Core::InternalThreadState *ChildThread);
void RunThread(FEXCore::Core::InternalThreadState *Thread);
void DestroyThread(FEXCore::Core::InternalThreadState *Thread);
void CleanupAfterFork(FEXCore::Core::InternalThreadState *ExceptForThread);
std::vector<FEXCore::Core::InternalThreadState*> *const GetThreads() { return &Threads; }
void AddNamedRegion(uintptr_t Base, uintptr_t Size, uintptr_t Offset, const std::string &filename);
@@ -200,13 +245,15 @@ namespace FEXCore::Context {
FEXCore::JITSymbols Symbols;
#endif
// Public for threading
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
protected:
void ClearCodeCache(FEXCore::Core::InternalThreadState *Thread, bool AlsoClearIRCache);
private:
void WaitForIdleWithTimeout();
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
void NotifyPause();
void AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr, uint64_t Start, uint64_t Length);
@@ -214,18 +261,13 @@ namespace FEXCore::Context {
FEXCore::CodeLoader *LocalLoader{};
// Entry Cache
bool GetFilenameHash(std::string const &Filename, std::string &Hash);
void AddThreadRIPsToEntryList(FEXCore::Core::InternalThreadState *Thread);
void SaveEntryList();
std::set<uint64_t> EntryList;
std::vector<uint64_t> InitLocations;
uint64_t StartingRIP;
std::mutex ExitMutex;
std::unique_ptr<GdbServer> DebugServer;
bool StartPaused = false;
FEXCore::Config::Value<std::string> AppFilename{FEXCore::Config::CONFIG_APP_FILENAME, ""};
FEX_CONFIG_OPT(AppFilename, APP_FILENAME);
};
uint64_t HandleSyscall(FEXCore::HLE::SyscallHandler *Handler, FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args);
uint64_t HandleSyscall(FEXCore::HLE::SyscallHandler *Handler, FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args);
}
@@ -38,8 +38,8 @@ static bool StoreCAS8(uint8_t &Expected, uint8_t Val, uint64_t Addr) {
return Atom->compare_exchange_strong(Expected, Val);
}
bool HandleCASPAL(void *_mcontext, void *_info, uint32_t Instr) {
mcontext_t* mcontext = reinterpret_cast<mcontext_t*>(_mcontext);
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) {
mcontext_t* mcontext = &reinterpret_cast<ucontext_t*>(_ucontext)->uc_mcontext;
siginfo_t* info = reinterpret_cast<siginfo_t*>(_info);
if (info->si_code != BUS_ADRALN) {
@@ -827,8 +827,8 @@ std::tuple<uint64_t, bool> DoCAS64(
}
bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr) {
mcontext_t* mcontext = reinterpret_cast<mcontext_t*>(_mcontext);
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr) {
mcontext_t* mcontext = &reinterpret_cast<ucontext_t*>(_ucontext)->uc_mcontext;
siginfo_t* info = reinterpret_cast<siginfo_t*>(_info);
if (info->si_code != BUS_ADRALN) {
@@ -921,8 +921,8 @@ bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr) {
return false;
}
bool HandleAtomicMemOp(void *_mcontext, void *_info, uint32_t Instr) {
mcontext_t* mcontext = reinterpret_cast<mcontext_t*>(_mcontext);
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) {
mcontext_t* mcontext = &reinterpret_cast<ucontext_t*>(_ucontext)->uc_mcontext;
siginfo_t* info = reinterpret_cast<siginfo_t*>(_info);
if (info->si_code != BUS_ADRALN) {
+9 -3
View File
@@ -12,6 +12,12 @@ namespace FEXCore::ArchHelpers::Arm64 {
constexpr uint32_t ATOMIC_MEM_MASK = 0x3B200C00;
constexpr uint32_t ATOMIC_MEM_INST = 0x38200000;
constexpr uint32_t LDAXP_MASK = 0xBF'FF'80'00;
constexpr uint32_t LDAXP_INST = 0x88'7F'80'00;
constexpr uint32_t STLXP_MASK = 0xBF'E0'80'00;
constexpr uint32_t STLXP_INST = 0x88'20'80'00;
// Load ops are 4 bits
// Acquire and release bits are independent on the instruction
constexpr uint32_t ATOMIC_ADD_OP = 0b0000;
@@ -24,7 +30,7 @@ namespace FEXCore::ArchHelpers::Arm64 {
constexpr uint32_t ATOMIC_UMIN_OP = 0b0111;
constexpr uint32_t ATOMIC_SWAP_OP = 0b1000;
bool HandleCASPAL(void *_mcontext, void *_info, uint32_t Instr);
bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr);
bool HandleAtomicMemOp(void *_mcontext, void *_info, uint32_t Instr);
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr);
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr);
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr);
}
@@ -0,0 +1,231 @@
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Core/CoreState.h>
#include "aarch64/cpu-aarch64.h"
namespace FEXCore::CPU {
#define STATE x28
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
Arm64Emitter::Arm64Emitter(size_t size) : vixl::aarch64::Assembler(size, vixl::aarch64::PositionDependentCode) {
CPU.SetUp();
auto Features = vixl::CPUFeatures::InferFromOS();
SupportsAtomics = Features.Has(vixl::CPUFeatures::Feature::kAtomics);
// RCPC is bugged on Snapdragon 865
// Causes glibc cond16 test to immediately throw assert
// __pthread_mutex_cond_lock: Assertion `mutex->__data.__owner == 0'
SupportsRCPC = false; //Features.Has(vixl::CPUFeatures::Feature::kRCpc);
if (SupportsAtomics) {
// Hypervisor can hide this on the c630?
Features.Combine(vixl::CPUFeatures::Feature::kLORegions);
}
SetCPUFeatures(Features);
if (!SupportsAtomics) {
WARN_ONCE("Host CPU doesn't support atomics. Expect bad performance");
}
#ifdef _M_ARM_64
// We need to get the CPU's cache line size
// We expect sane targets that have correct cacheline sizes across clusters
uint64_t CTR;
__asm volatile ("mrs %[ctr], ctr_el0"
: [ctr] "=r"(CTR));
DCacheLineSize = 4 << ((CTR >> 16) & 0xF);
ICacheLineSize = 4 << (CTR & 0xF);
#endif
}
void Arm64Emitter::LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant) {
bool Is64Bit = Reg.IsX();
int Segments = Is64Bit ? 4 : 2;
if (Is64Bit && ((~Constant)>> 16) == 0) {
movn(Reg, (~Constant) & 0xFFFF);
return;
}
movz(Reg, (Constant) & 0xFFFF, 0);
for (int i = 1; i < Segments; ++i) {
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
if (Part) {
movk(Reg, Part, i * 16);
}
}
}
void Arm64Emitter::PushCalleeSavedRegisters() {
// We need to save pairs of registers
// We save r19-r30
MemOperand PairOffset(sp, -16, PreIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x19, x20},
{x21, x22},
{x23, x24},
{x25, x26},
{x27, x28},
{x29, x30},
}};
for (auto &RegPair : CalleeSaved) {
stp(RegPair.first, RegPair.second, PairOffset);
}
// Additionally we need to store the lower 64bits of v8-v15
// Here's a fun thing, we can use two ST4 instructions to store everything
// We just need a single sub to sp before that
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v8, v9, v10, v11},
{v12, v13, v14, v15},
}};
uint32_t VectorSaveSize = sizeof(uint64_t) * 8;
sub(sp, sp, VectorSaveSize);
// SP supporting move
// We just saved x19 so it is safe
add(x19, sp, 0);
MemOperand QuadOffset(x19, 32, PostIndex);
for (auto &RegQuad : FPRs) {
st4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
}
void Arm64Emitter::PopCalleeSavedRegisters() {
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v12, v13, v14, v15},
{v8, v9, v10, v11},
}};
MemOperand QuadOffset(sp, 32, PostIndex);
for (auto &RegQuad : FPRs) {
ld4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
MemOperand PairOffset(sp, 16, PostIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x29, x30},
{x27, x28},
{x25, x26},
{x23, x24},
{x21, x22},
{x19, x20},
}};
for (auto &RegPair : CalleeSaved) {
ldp(RegPair.first, RegPair.second, PairOffset);
}
}
void Arm64Emitter::SpillStaticRegs() {
for (size_t i = 0; i < SRA64.size(); i+=2) {
stp(SRA64[i], SRA64[i+1], MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i])));
}
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
stp(SRAFPR[i].Q(), SRAFPR[i+1].Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
}
}
void Arm64Emitter::FillStaticRegs() {
for (size_t i = 0; i < SRA64.size(); i+=2) {
ldp(SRA64[i], SRA64[i+1], MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i])));
}
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
ldp(SRAFPR[i].Q(), SRAFPR[i+1].Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
}
}
void Arm64Emitter::PushDynamicRegsAndLR() {
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
sub(sp, sp, SPOffset);
int i = 0;
for (auto RA : RAFPR)
{
str(RA.Q(), MemOperand(sp, i * 8));
i+=2;
}
#if 0 // All GPRs should be caller saved
for (auto RA : RA64)
{
str(RA, MemOperand(sp, i * 8));
i++;
}
#endif
str(lr, MemOperand(sp, i * 8));
}
void Arm64Emitter::PopDynamicRegsAndLR() {
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
int i = 0;
for (auto RA : RAFPR)
{
ldr(RA.Q(), MemOperand(sp, i * 8));
i+=2;
}
#if 0 // All GPRs should be caller saved
for (auto RA : RA64)
{
ldr(RA, MemOperand(sp, i * 8));
i++;
}
#endif
ldr(lr, MemOperand(sp, i * 8));
add(sp, sp, SPOffset);
}
void Arm64Emitter::ResetStack() {
if (SpillSlots == 0)
return;
if (IsImmAddSub(SpillSlots * 16)) {
add(sp, sp, SpillSlots * 16);
} else {
// Too big to fit in a 12bit immediate
LoadConstant(x0, SpillSlots * 16);
add(sp, sp, x0);
}
}
void Arm64Emitter::Align16B() {
uint64_t CurrentOffset = GetBuffer()->GetOffsetAddress<uint64_t>(GetCursorOffset());
for (uint64_t i = (16 - (CurrentOffset & 0xF)); i != 0; i -= 4) {
nop();
}
}
}
@@ -0,0 +1,77 @@
#pragma once
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
// All but x29 are caller saved
const std::array<aarch64::Register, 16> SRA64 = {
x4, x5, x6, x7, x8, x9, x10, x11,
x12, x18, x17, x16, x15, x14, x13, x29
};
// All are callee saved
const std::array<aarch64::Register, 9> RA64 = {
x20, x21, x22, x23, x24, x25, x26, x27,
x19
};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA64Pair = {{
{x20, x21},
{x22, x23},
{x24, x25},
{x26, x27},
}};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA32Pair = {{
{w20, w21},
{w22, w23},
{w24, w25},
{w26, w27},
}};
// All are caller saved
const std::array<aarch64::VRegister, 16> SRAFPR = {
v16, v17, v18, v19, v20, v21, v22, v23,
v24, v25, v26, v27, v28, v29, v30, v31
};
// v8..v15 = (lower 64bits) Callee saved
const std::array<aarch64::VRegister, 12> RAFPR = {
/*v0, v1, v2, v3,*/v4, v5, v6, v7, // v0 ~ v3 are used as temps
v8, v9, v10, v11, v12, v13, v14, v15
};
// This class contains common emitter utility functions that can
// be used by both Arm64 JIT and ARM64 Dispatcher
class Arm64Emitter : public vixl::aarch64::Assembler {
protected:
Arm64Emitter(size_t size);
vixl::aarch64::CPU CPU;
bool SupportsAtomics{};
bool SupportsRCPC{};
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
void SpillStaticRegs();
void FillStaticRegs();
void PushDynamicRegsAndLR();
void PopDynamicRegsAndLR();
void PushCalleeSavedRegisters();
void PopCalleeSavedRegisters();
void ResetStack();
void Align16B();
uint32_t SpillSlots{};
uint32_t DCacheLineSize{};
uint32_t ICacheLineSize{};
};
}
@@ -0,0 +1,26 @@
#include "Interface/Core/ArchHelpers/Arm64.h"
#include <FEXCore/Utils/LogManager.h>
namespace FEXCore::ArchHelpers::Arm64 {
#ifndef _M_ARM_64
// These are stub implementations that exist only to allow instantiating the arm64 jit
// on non arm platforms.
// Obvously such a configuration can't do the actual arm64-specific stuff
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) {
ERROR_AND_DIE("HandleCASPAL Not Implemented");
}
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr) {
ERROR_AND_DIE("HandleCASAL Not Implemented");
}
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) {
ERROR_AND_DIE("HandleAtomicMemOp Not Implemented");
}
#endif
}
@@ -0,0 +1,212 @@
#pragma once
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/UContext.h>
#include <signal.h>
#include <string.h>
#include <ucontext.h>
#include <stdint.h>
#include <type_traits>
namespace FEXCore::ArchHelpers::Context {
struct X86ContextBackup {
// Host State
// RIP and RSP is stored in GPRs here
uint64_t GPRs[23];
FEXCore::x86_64::_libc_fpstate FPRState;
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
static constexpr int RedZoneSize = 128;
};
struct ArmContextBackup {
// Host State
uint64_t GPRs[31];
uint64_t PrevSP;
uint64_t PrevPC;
uint64_t PState;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
// Arm64 doesn't have a red zone
static constexpr int RedZoneSize = 0;
};
static inline mcontext_t* GetMContext(void* ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
return &_context->uc_mcontext;
}
#ifdef _M_ARM_64
static inline uint64_t GetSp(void* ucontext) {
return GetMContext(ucontext)->sp;
}
static inline uint64_t GetPc(void* ucontext) {
return GetMContext(ucontext)->pc;
}
static inline void SetSp(void* ucontext, uint64_t val) {
GetMContext(ucontext)->sp = val;
}
static inline void SetPc(void* ucontext, uint64_t val) {
GetMContext(ucontext)->pc = val;
}
static inline uint64_t GetState(void* ucontext) {
return GetMContext(ucontext)->regs[28];
}
static inline void SetState(void* ucontext, uint64_t val) {
GetMContext(ucontext)->regs[28] = val;
}
static inline uint64_t GetArmReg(void* ucontext, uint32_t id) {
return GetMContext(ucontext)->regs[id];
}
static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) {
GetMContext(ucontext)->regs[id] = val;
}
constexpr uint32_t FPR_MAGIC = 0x46508001U;
struct HostCTXHeader {
uint32_t Magic;
uint32_t Size;
};
struct HostFPRState {
HostCTXHeader Head;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
};
using ContextBackup = ArmContextBackup;
template <typename T>
static inline void BackupContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, ArmContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
memcpy(&Backup->GPRs[0], &_mcontext->regs[0], 31 * sizeof(uint64_t));
Backup->PrevSP = ArchHelpers::Context::GetSp(ucontext);
Backup->PrevPC = ArchHelpers::Context::GetPc(ucontext);
Backup->PState = _mcontext->pstate;
// Host FPR state starts at _mcontext->reserved[0];
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LOGMAN_THROW_A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
Backup->FPSR = HostState->FPSR;
Backup->FPCR = HostState->FPCR;
memcpy(&Backup->FPRs[0], &HostState->FPRs[0], 32 * sizeof(__uint128_t));
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
template <typename T>
static inline void RestoreContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, ArmContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LOGMAN_THROW_A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
memcpy(&HostState->FPRs[0], &Backup->FPRs[0], 32 * sizeof(__uint128_t));
HostState->FPCR = Backup->FPCR;
HostState->FPSR = Backup->FPSR;
// Restore GPRs and other state
_mcontext->pstate = Backup->PState;
ArchHelpers::Context::SetPc(ucontext, Backup->PrevPC);
ArchHelpers::Context::SetSp(ucontext, Backup->PrevSP);
memcpy(&_mcontext->regs[0], &Backup->GPRs[0], 31 * sizeof(uint64_t));
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
#endif
#ifdef _M_X86_64
static inline uint64_t GetSp(void* ucontext) {
return GetMContext(ucontext)->gregs[REG_RSP];
}
static inline uint64_t GetPc(void* ucontext) {
return GetMContext(ucontext)->gregs[REG_RIP];
}
static inline void SetSp(void* ucontext, uint64_t val) {
GetMContext(ucontext)->gregs[REG_RSP] = val;
}
static inline void SetPc(void* ucontext, uint64_t val) {
GetMContext(ucontext)->gregs[REG_RIP] = val;
}
static inline uint64_t GetState(void* ucontext) {
return GetMContext(ucontext)->gregs[REG_R14];
}
static inline void SetState(void* ucontext, uint64_t val) {
GetMContext(ucontext)->gregs[REG_R14] = val;
}
static inline uint64_t GetArmReg(void* ucontext, uint32_t id) {
ERROR_AND_DIE("Not impelented for x86 host");
}
static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) {
ERROR_AND_DIE("Not impelented for x86 host");
}
using ContextBackup = X86ContextBackup;
template <typename T>
static inline void BackupContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, X86ContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
// Copy the GPRs
memcpy(&Backup->GPRs[0], &_mcontext->gregs[0], sizeof(X86ContextBackup::GPRs));
// Copy the FPRState
memcpy(&Backup->FPRState, _mcontext->fpregs, sizeof(X86ContextBackup::FPRState));
// XXX: Save 256bit and 512bit AVX register state
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
template <typename T>
static inline void RestoreContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, X86ContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
// Copy the GPRs
memcpy(&_mcontext->gregs[0], &Backup->GPRs[0], sizeof(X86ContextBackup::GPRs));
// Copy the FPRState
memcpy(_mcontext->fpregs, &Backup->FPRState, sizeof(X86ContextBackup::FPRState));
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
#endif
} // namespace FEXCore::ArchHelpers::Context
+172 -4
View File
@@ -1,11 +1,42 @@
/*
$info$
tags: opcodes|cpuid
desc: Handles presented capability bits for guest cpu
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUID.h"
#include "git_version.h"
#include <cstring>
#ifdef _M_X86_64
#include <cpuid.h>
#endif
namespace FEXCore {
//#define CPUID_AMD
#ifdef _M_ARM_64
static uint32_t GetCycleCounterFrequency() {
uint64_t Result{};
__asm("mrs %[Res], CNTFRQ_EL0"
: [Res] "=r" (Result));
return Result;
}
#else
static uint32_t GetCycleCounterFrequency() {
uint32_t eax, ebx, ecx, edx;
__cpuid(0, eax, ebx, ecx, edx);
if (eax >= 0x15) {
__cpuid(0x15, eax, ebx, ecx, edx);
if (eax && ebx && ecx) {
return ecx * ebx / eax;
}
}
return 0;
}
#endif
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_0h() {
FEXCore::CPUID::FunctionResults Res{};
@@ -93,7 +124,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h() {
(1 << 16) | // Page Attribute Table
(1 << 17) | // 36bit page size extension
(0 << 18) | // Processor serial number
(0 << 19) | // CLFLUSH
(1 << 19) | // CLFLUSH
(0 << 20) | // Reserved
(0 << 21) | // Debug store
(0 << 22) | // Thermal monitor and software controled clock
@@ -251,6 +282,18 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h() {
return Res;
}
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_15h() {
FEXCore::CPUID::FunctionResults Res{};
// TSC frequency = ECX * EBX / EAX
uint32_t FrequencyHz = GetCycleCounterFrequency();
if (FrequencyHz) {
Res.eax = 1;
Res.ebx = 1;
Res.ecx = FrequencyHz;
}
return Res;
}
// Highest extended function implemented
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0000h() {
FEXCore::CPUID::FunctionResults Res{};
@@ -375,10 +418,120 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0004h() {
return Res;
}
// L1 Cache and TLB identifiers
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0005h() {
FEXCore::CPUID::FunctionResults Res{};
// L1 TLB Information for 2MB and 4MB pages
Res.eax =
(64 << 0) | // Number of TLB instruction entries
(255 << 8) | // instruction TLB associativity type (full)
(64 << 16) | // Number of TLB data entries
(255 << 24); // data TLB associativity type (full)
// L1 TLB Information for 4KB pages
Res.ebx =
(64 << 0) | // Number of TLB instruction entries
(255 << 8) | // instruction TLB associativity type (full)
(64 << 16) | // Number of TLB data entries
(255 << 24); // data TLB associativity type (full)
// L1 data cache identifiers
Res.ecx =
(64 << 0) | // L1 data cache size line in bytes
(1 << 8) | // L1 data cachelines per tag
(8 << 16) | // L1 data cache associativity
(32 << 24); // L1 data cache size in KB
// L1 instruction cache identifiers
Res.edx =
(64 << 0) | // L1 instruction cache line size in bytes
(1 << 8) | // L1 instruction cachelines per tag
(4 << 16) | // L1 instruction cache associativity
(64 << 24); // L1 instruction cache size in KB
return Res;
}
// L2 Cache identifiers
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0006h() {
FEXCore::CPUID::FunctionResults Res{};
// L2 TLB Information for 2MB and 4MB pages
Res.eax =
(1024 << 0) | // Number of TLB instruction entries
(6 << 12) | // instruction TLB associativity type
(1536 << 16) | // Number of TLB data entries
(3 << 28); // data TLB associativity type
// L2 TLB Information for 4KB pages
Res.ebx =
(1024 << 0) | // Number of TLB instruction entries
(6 << 12) | // instruction TLB associativity type
(1536 << 16) | // Number of TLB data entries
(5 << 28); // data TLB associativity type
// L2 cache identifiers
Res.ecx =
(64 << 0) | // cacheline size
(1 << 8) | // cachelines per tag
(6 << 12) | // cache associativity
(512 << 16); // L2 cache size in KB
// L3 cache identifiers
Res.edx =
(64 << 0) | // cacheline size
(1 << 8) | // cachelines per tag
(6 << 12) | // cache associativity
(16 << 18); // L2 cache size in KB
return Res;
}
// Advanced power management
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0007h() {
FEXCore::CPUID::FunctionResults Res{};
Res.eax = (1 << 2); // APIC timer not affected by p-state
Res.edx =
(1 << 8); // Invariant TSC
return Res;
}
// Virtual and physical address sizes
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0008h() {
FEXCore::CPUID::FunctionResults Res{};
Res.eax =
(48 << 0) | // PhysAddrSize = 48-bit
(48 << 8) | // LinAddrSize = 48-bit
(0 << 16); // GuestPhysAddrSize == PhysAddrSize
Res.ebx =
(0 << 2) | // XSaveErPtr: Saving and restoring error pointers
(0 << 1) | // IRPerf: Instructions retired count support
(0 << 0); // CLZERO support
uint32_t CoreCount = Cores() - 1;
Res.ecx =
(0 << 16) | // PerfTscSize: Performance timestamp count size
(0 << 12) | // ApicIdSize: Number of bits in ApicID
(CoreCount << 0); // Count count subtract one
return Res;
}
// TLB 1GB page identifiers
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0019h() {
FEXCore::CPUID::FunctionResults Res{};
Res.eax =
(0xF << 28) | // L1 DTLB associativity for 1GB pages
(64 << 16) | // L1 DTLB entry count for 1GB pages
(0xF << 12) | // L1 ITLB associativity for 1GB pages
(64 << 0); // L1 ITLB entry count for 1GB pages
Res.ebx =
(0 << 28) | // L2 DTLB associativity for 1GB pages
(0 << 16) | // L2 DTLB entry count for 1GB pages
(0 << 12) | // L2 ITLB associativity for 1GB pages
(0 << 0); // L2 ITLB entry count for 1GB pages
return Res;
}
@@ -408,7 +561,11 @@ void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
// 0x12: Intel SGX capability enumeration
// 0x13: Reserved
// 0x14: Intel Processor trace
// 0x15: Timestamp counter information
#ifndef CPUID_AMD
// Timestamp counter information
// Doesn't exist on AMD hardware
RegisterFunction(0x15, std::bind(&CPUIDEmu::Function_15h, this));
#endif
// 0x16: Processor frequency information
// 0x17: SoC vendor attribute enumeration
@@ -423,12 +580,23 @@ void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
// Processor brand string continued
RegisterFunction(0x8000'0004, std::bind(&CPUIDEmu::Function_8000_0004h, this));
// 0x8000'0005: L1 Cache and TLB identifiers
#ifdef CPUID_AMD
RegisterFunction(0x8000'0005, std::bind(&CPUIDEmu::Function_8000_0005h, this));
#else
// This is full reserved on Intel platforms
RegisterFunction(0x8000'0005, std::bind(&CPUIDEmu::Function_Reserved, this));
#endif
// 0x8000'0006: L2 Cache identifiers
RegisterFunction(0x8000'0006, std::bind(&CPUIDEmu::Function_8000_0006h, this));
// Advanced power management information
RegisterFunction(0x8000'0007, std::bind(&CPUIDEmu::Function_8000_0007h, this));
// 0x8000'0008: Virtual and physical address sizes
// Virtual and physical address sizes
RegisterFunction(0x8000'0008, std::bind(&CPUIDEmu::Function_8000_0008h, this));
// 0x8000'000A: SVM Revision
// 0x8000'0019: TLB 1GB page identifiers
// TLB 1GB page identifiers
RegisterFunction(0x8000'0019, std::bind(&CPUIDEmu::Function_8000_0019h, this));
// 0x8000'001A: Performance optimization identifiers
// 0x8000'001B: Instruction based sampling identifiers
// 0x8000'001C: Lightweight profiling capabilities
+9 -2
View File
@@ -3,6 +3,7 @@
#include <unordered_map>
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/Config/Config.h>
#include <FEXCore/Utils/LogManager.h>
namespace FEXCore {
@@ -23,7 +24,7 @@ private:
public:
void Init(FEXCore::Context::Context *ctx);
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function) {
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, [[maybe_unused]] uint32_t Leaf) {
auto Handler = FunctionHandlers.find(Function);
if (Handler == FunctionHandlers.end()) {
@@ -37,6 +38,7 @@ public:
}
private:
FEXCore::Context::Context *CTX;
FEX_CONFIG_OPT(Cores, THREADS);
using FunctionHandler = std::function<FEXCore::CPUID::FunctionResults()>;
void RegisterFunction(uint32_t Function, FunctionHandler Handler) {
@@ -51,13 +53,18 @@ private:
FEXCore::CPUID::FunctionResults Function_02h();
FEXCore::CPUID::FunctionResults Function_06h();
FEXCore::CPUID::FunctionResults Function_07h();
FEXCore::CPUID::FunctionResults Function_15h();
FEXCore::CPUID::FunctionResults Function_8000_0000h();
FEXCore::CPUID::FunctionResults Function_8000_0001h();
FEXCore::CPUID::FunctionResults Function_8000_0002h();
FEXCore::CPUID::FunctionResults Function_8000_0003h();
FEXCore::CPUID::FunctionResults Function_8000_0004h();
FEXCore::CPUID::FunctionResults Function_8000_0005h();
FEXCore::CPUID::FunctionResults Function_8000_0006h();
FEXCore::CPUID::FunctionResults Function_8000_0007h();
FEXCore::CPUID::FunctionResults Function_8000_0008h();
FEXCore::CPUID::FunctionResults Function_8000_0009h();
FEXCore::CPUID::FunctionResults Function_8000_0019h();
FEXCore::CPUID::FunctionResults Function_Reserved();
};
}
+14 -10
View File
@@ -5,6 +5,12 @@
#include "Interface/Core/OpcodeDispatcher.h"
namespace FEXCore {
static void* ThreadHandler(void *Arg) {
FEXCore::CompileService *This = reinterpret_cast<FEXCore::CompileService*>(Arg);
This->ExecutionThread();
return nullptr;
}
CompileService::CompileService(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: CTX {ctx}
, ParentThread {Thread} {
@@ -16,9 +22,7 @@ namespace FEXCore {
CTX->InitializeCompiler(CompileThreadData.get(), true);
CompileThreadData->CPUBackend->CopyNecessaryDataForCompileThread(ParentThread->CPUBackend.get());
WorkerThread = std::thread([this]() {
ExecutionThread();
});
WorkerThread = FEXCore::Threads::Thread::Create(ThreadHandler, this);
}
void CompileService::Initialize() {
@@ -30,7 +34,7 @@ namespace FEXCore {
ShuttingDown = true;
// Kick the working thread
StartWork.NotifyAll();
WorkerThread.join();
WorkerThread->join(nullptr);
}
void CompileService::ClearCache(FEXCore::Core::InternalThreadState *Thread) {
@@ -57,7 +61,7 @@ namespace FEXCore {
}
}
LogMan::Throw::A(CompileThreadData->LocalIRCache.size() == 0, "Compile service must never have LocalIRCache");
LOGMAN_THROW_A(CompileThreadData->LocalIRCache.size() == 0, "Compile service must never have LocalIRCache");
CompileMutex.unlock();
}
@@ -92,7 +96,7 @@ namespace FEXCore {
// Set our thread name so we can see its relation
char ThreadName[16]{};
snprintf(ThreadName, 16, "%ld-CS", ParentThread->State.ThreadManager.TID.load());
snprintf(ThreadName, 16, "%ld-CS", ParentThread->ThreadManager.TID.load());
pthread_setname_np(pthread_self(), ThreadName);
while (true) {
@@ -120,15 +124,15 @@ namespace FEXCore {
// If we had a work item then work on it
if (Item) {
// Make sure it's not in lookup cache by accident
LogMan::Throw::A(CompileThreadData->LookupCache->FindBlock(Item->RIP) == 0, "Compile Service must never have entries in the LookupCache");
LOGMAN_THROW_A(CompileThreadData->LookupCache->FindBlock(Item->RIP) == 0, "Compile Service must never have entries in the LookupCache");
// Code isn't in cache, compile now
// Set our thread state's RIP
CompileThreadData->State.State.rip = Item->RIP;
CompileThreadData->CurrentFrame->State.rip = Item->RIP;
auto [CodePtr, IRList, DebugData, RAData, Generated, StartAddr, Length] = CTX->CompileCode(CompileThreadData.get(), Item->RIP);
LogMan::Throw::A(Generated == true, "Compile Service doesn't have IR Cache");
LOGMAN_THROW_A(Generated == true, "Compile Service doesn't have IR Cache");
if (!CodePtr) {
// XXX: We currently have the expectation that compile service code will be significantly smaller than regular thread's code
+5 -2
View File
@@ -2,6 +2,7 @@
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Utils/Event.h>
#include <FEXCore/Utils/Threads.h>
#include <memory>
#include <thread>
@@ -45,12 +46,14 @@ class CompileService final {
WorkItem *CompileCode(uint64_t RIP);
void ClearCache(FEXCore::Core::InternalThreadState *Thread);
// Public for threading
void ExecutionThread();
private:
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *ParentThread;
void ExecutionThread();
std::thread WorkerThread;
std::unique_ptr<FEXCore::Threads::Thread> WorkerThread;
std::unique_ptr<FEXCore::Core::InternalThreadState> CompileThreadData;
std::mutex QueueMutex{};
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,364 @@
#include "Interface/Core/ArchHelpers/MContext.h"
#include "Interface/Core/Dispatcher/Arm64Dispatcher.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE x28
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config)
: Dispatcher(ctx, Thread), Arm64Emitter(MAX_DISPATCHER_CODE_SIZE) {
SRAEnabled = config.StaticRegisterAssignment;
SetAllowAssembler(true);
auto Buffer = GetBuffer();
DispatchPtr = Buffer->GetOffsetAddress<CPUBackend::AsmDispatch>(GetCursorOffset());
// while (true) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// Ptr();
// }
uint64_t VirtualMemorySize = Thread->LookupCache->GetVirtualMemorySize();
Literal l_VirtualMemory {VirtualMemorySize};
Literal l_PagePtr {Thread->LookupCache->GetPagePointer()};
Literal l_L1Ptr {Thread->LookupCache->GetL1Pointer()};
Literal l_CTX {reinterpret_cast<uintptr_t>(CTX)};
Literal l_Sleep {reinterpret_cast<uint64_t>(SleepThread)};
Literal l_CompileBlock {GetCompileBlockPtr()};
Literal l_ExitFunctionLink {config.ExitFunctionLink};
Literal l_ExitFunctionLinkThis {config.ExitFunctionLinkThis};
// Push all the register we need to save
PushCalleeSavedRegisters();
// Push our memory base to the correct register
// Move our thread pointer to the correct register
// This is passed in to parameter 0 (x0)
mov(STATE, x0);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
add(x0, sp, 0);
str(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)));
AbsoluteLoopTopAddressFillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled) {
FillStaticRegs();
}
// We want to ensure that we are 16 byte aligned at the top of this loop
Align16B();
aarch64::Label FullLookup{};
aarch64::Label CallBlock{};
aarch64::Label LoopTop{};
aarch64::Label ExitSpillSRA{};
aarch64::Label ThreadPauseHandler{};
bind(&LoopTop);
AbsoluteLoopTopAddress = GetLabelAddress<uint64_t>(&LoopTop);
// Load in our RIP
// Don't modify x2 since it contains our RIP once the block doesn't exist
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
auto RipReg = x2;
// L1 Cache
ldr(x0, &l_L1Ptr);
and_(x3, RipReg, LookupCache::L1_ENTRIES_MASK);
add(x0, x0, Operand(x3, Shift::LSL, 4));
ldp(x3, x0, MemOperand(x0));
cmp(x0, RipReg);
b(&FullLookup, Condition::ne);
if (!config.ExecuteBlocksWithCall) {
br(x3);
} else {
b(&CallBlock);
}
// L1C check failed, do a full lookup
bind(&FullLookup);
// This is the block cache lookup routine
// It matches what is going on it LookupCache.h::FindBlock
ldr(x0, &l_PagePtr);
// Mask the address by the virtual address size so we can check for aliases
if (__builtin_popcountl(VirtualMemorySize) == 1) {
and_(x3, RipReg, Thread->LookupCache->GetVirtualMemorySize() - 1);
}
else {
ldr(x3, &l_VirtualMemory);
and_(x3, RipReg, x3);
}
aarch64::Label NoBlock;
{
// Offset the address and add to our page pointer
lsr(x1, x3, 12);
// Load the pointer from the offset
ldr(x0, MemOperand(x0, x1, Shift::LSL, 3));
// If page pointer is zero then we have no block
cbz(x0, &NoBlock);
// Steal the page offset
and_(x1, x3, 0x0FFF);
// Shift the offset by the size of the block cache entry
add(x0, x0, Operand(x1, Shift::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry))));
// Load the guest address first to ensure it maps to the address we are currently at
// This fixes aliasing problems
ldr(x1, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode)));
cmp(x1, RipReg);
b(&NoBlock, Condition::ne);
// Now load the actual host block to execute if we can
ldr(x3, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode)));
cbz(x3, &NoBlock);
// If we've made it here then we have a real compiled block
{
// update L1 cache
ldr(x0, &l_L1Ptr);
and_(x1, RipReg, LookupCache::L1_ENTRIES_MASK);
add(x0, x0, Operand(x1, Shift::LSL, 4));
stp(x3, x2, MemOperand(x0));
// Jump to the block
if (!config.ExecuteBlocksWithCall) {
br(x3);
} else {
bind(&CallBlock);
mov(x0, STATE);
blr(x3);
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
ldr(x0, &l_CTX);
ldr(w0, MemOperand(x0, offsetof(FEXCore::Context::Context, Config.RunningMode)));
// If the value == 0 then branch to the top
cbz(x0, &LoopTop);
// Else we need to pause now
b(&ThreadPauseHandler);
} else {
// Unconditionally loop to the top
// We will only stop on error when compiling a block or signal
b(&LoopTop);
}
}
}
}
{
bind(&ExitSpillSRA);
ThreadStopHandlerAddressSpillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled)
SpillStaticRegs();
ThreadStopHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
PopCalleeSavedRegisters();
// Return from the function
// LR is set to the correct return location now
ret();
}
{
ExitFunctionLinkerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled)
SpillStaticRegs();
ldr(x0, &l_ExitFunctionLinkThis);
mov(x1, STATE);
mov(x2, lr);
ldr(x3, &l_ExitFunctionLink);
blr(x3);
if (SRAEnabled)
FillStaticRegs();
br(x0);
}
// Need to create the block
{
bind(&NoBlock);
ldr(x0, &l_CTX);
mov(x1, STATE);
ldr(x3, &l_CompileBlock);
if (SRAEnabled)
SpillStaticRegs();
// X2 contains our guest RIP
blr(x3); // { CTX, Frame, RIP}
if (SRAEnabled)
FillStaticRegs();
b(&LoopTop);
}
{
SignalHandlerReturnAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// Now to get back to our old location we need to do a fault dance
// We can't use SIGTRAP here since gdb catches it and never gives it to the application!
hlt(0);
}
{
ThreadPauseHandlerAddressSpillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled)
SpillStaticRegs();
bind(&ThreadPauseHandler);
ThreadPauseHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// We are pausing, this means the frontend should be waiting for this thread to idle
// We will have faulted and jumped to this location at this point
// Call our sleep handler
ldr(x0, &l_CTX);
mov(x1, STATE);
ldr(x2, &l_Sleep);
blr(x2);
PauseReturnInstruction = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// Fault to start running again
hlt(0);
}
{
// The expectation here is that a thunked function needs to call back in to the JIT in a reentrant safe way
// To do this safely we need to do some state tracking and register saving
//
// eg:
// JIT Call->
// Thunk->
// Thunk callback->
//
// The thunk callback needs to execute JIT code and when it returns, it needs to safely return to the thunk rather than JIT space
// This is handled by pushing a return address trampoline to the stack so when the guest address returns it hits our custom thunk return
// - This will safely return us to the thunk
//
// On return to the thunk, the thunk can get whatever its return value is from the thread context depending on ABI handling on its end
// When the thunk itself returns, it'll do its regular return logic there
// void ReentrantCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
CallbackPtr = Buffer->GetOffsetAddress<CPUBackend::JITCallback>(GetCursorOffset());
// We expect the thunk to have previously pushed the registers it was using
PushCalleeSavedRegisters();
// First thing we need to move the thread state pointer back in to our register
mov(STATE, x0);
// Make sure to adjust the refcounter so we don't clear the cache now
LoadConstant(x0, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
ldr(w2, MemOperand(x0));
add(w2, w2, 1);
str(w2, MemOperand(x0));
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
LoadConstant(x0, CTX->X86CodeGen.CallbackReturn);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
sub(x2, x2, 16);
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
str(x0, MemOperand(x2));
// Store RIP to the context state
str(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
// load static regs
if (SRAEnabled)
FillStaticRegs();
// Now go back to the regular dispatcher loop
b(&LoopTop);
}
place(&l_VirtualMemory);
place(&l_PagePtr);
place(&l_L1Ptr);
place(&l_CTX);
place(&l_Sleep);
place(&l_CompileBlock);
place(&l_ExitFunctionLink);
place(&l_ExitFunctionLinkThis);
FinalizeCode();
Start = reinterpret_cast<uint64_t>(DispatchPtr);
End = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
vixl::aarch64::CPU::EnsureIAndDCacheCoherency(reinterpret_cast<void*>(DispatchPtr), End - reinterpret_cast<uint64_t>(DispatchPtr));
GetBuffer()->SetExecutable();
#if ENABLE_JITSYMBOLS
std::string Name = "Dispatch_" + std::to_string(::gettid());
CTX->Symbols.Register(reinterpret_cast<void*>(DispatchPtr), End - reinterpret_cast<uint64_t>(DispatchPtr), Name);
#endif
}
void Arm64Dispatcher::SpillSRA(void *ucontext) {
for(int i = 0; i < SRA64.size(); i++) {
ThreadState->CurrentFrame->State.gregs[i] = ArchHelpers::Context::GetArmReg(ucontext, SRA64[i].GetCode());
}
// TODO: Also recover FPRs, not sure where the neon context is
// This is usually not needed
/*
for(int i = 0; i < SRAFPR.size(); i++) {
State->State.State.xmm[i][0] = _mcontext.neon[SRAFPR[i].GetCode()];
State->State.State.xmm[i][0] = _mcontext.neon[SRAFPR[i].GetCode()];
}
*/
}
#ifdef _M_ARM_64
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
DispatcherConfig config;
config.ExecuteBlocksWithCall = true;
Dispatcher = std::make_unique<Arm64Dispatcher>(ctx, Thread, config);
DispatchPtr = Dispatcher->DispatchPtr;
CallbackPtr = Dispatcher->CallbackPtr;
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = Dispatcher->ReturnPtr;
}
#endif
}
@@ -0,0 +1,18 @@
#pragma once
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "aarch64/assembler-aarch64.h"
namespace FEXCore::CPU {
class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
public:
Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config);
protected:
void SpillSRA(void *ucontext) override;
};
}
@@ -0,0 +1,360 @@
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Common/MathUtils.h"
#include "Interface/Core/ArchHelpers/MContext.h"
#include <FEXCore/Core/X86Enums.h>
namespace FEXCore::CPU {
void Dispatcher::SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame) {
auto Thread = Frame->Thread;
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
// Go to sleep
Thread->StartRunning.Wait();
Thread->RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
}
void Dispatcher::StoreThreadState(int Signal, void *ucontext) {
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
uint64_t OldSP = ArchHelpers::Context::GetSp(ucontext);
uintptr_t NewSP = OldSP;
size_t StackOffset = sizeof(ArchHelpers::Context::ContextBackup);
// We need to back up behind the host's red zone
// We do this on the guest side as well
// (does nothing on arm hosts)
NewSP -= ArchHelpers::Context::ContextBackup::RedZoneSize;
NewSP -= StackOffset;
NewSP = AlignDown(NewSP, 16);
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
ArchHelpers::Context::BackupContext(ucontext, Context);
// Retain the action pointer so we can see it when we return
Context->Signal = Signal;
// Save guest state
// We can't guarantee if registers are in context or host GPRs
// So we need to save everything
memcpy(&Context->GuestState, ThreadState->CurrentFrame, sizeof(FEXCore::Core::CPUState));
// Set the new SP
ArchHelpers::Context::SetSp(ucontext, NewSP);
SignalFrames.push(NewSP);
}
void Dispatcher::RestoreThreadState(void *ucontext) {
uint64_t OldSP = SignalFrames.top();
SignalFrames.pop();
uintptr_t NewSP = OldSP;
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
// First thing, reset the guest state
memcpy(ThreadState->CurrentFrame, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
ArchHelpers::Context::RestoreContext(ucontext, Context);
// Restore the previous signal state
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
}
bool Dispatcher::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
StoreThreadState(Signal, ucontext);
auto Frame = ThreadState->CurrentFrame;
// Ref count our faults
// We use this to track if it is safe to clear cache
++SignalHandlerRefCounter;
// Set the new PC
ArchHelpers::Context::SetPc(ucontext, AbsoluteLoopTopAddressFillSRA);
// Set our state register to point to our guest thread data
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
uint64_t OldGuestSP = Frame->State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
if (!(GuestStack->ss_flags & SS_DISABLE)) {
// If our guest is already inside of the alternative stack
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
if (OldGuestSP >= AltStackBase &&
OldGuestSP <= AltStackEnd) {
// We are already in the alt stack, the rest of the code will handle adjusting this
}
else {
NewGuestSP = AltStackEnd;
}
}
// Back up past the redzone, which is 128bytes
// Don't need this offset if we aren't going to be putting siginfo in to it
NewGuestSP -= 128;
if (GuestAction->sa_flags & SA_SIGINFO) {
if (SRAEnabled) {
if (!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
LOGMAN_THROW_A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
} else {
// We are in jit, SRA must be spilled
SpillSRA(ucontext);
}
}
// Setup ucontext a bit
if (CTX->Config.Is64BitMode) {
NewGuestSP -= sizeof(FEXCore::x86_64::ucontext_t);
uint64_t UContextLocation = NewGuestSP;
NewGuestSP -= sizeof(siginfo_t);
uint64_t SigInfoLocation = NewGuestSP;
FEXCore::x86_64::ucontext_t *guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
siginfo_t *guest_siginfo = reinterpret_cast<siginfo_t*>(SigInfoLocation);
// We have extended float information
guest_uctx->uc_flags |= FEXCore::x86_64::UC_FP_XSTATE;
// Pointer to where the fpreg memory is
guest_uctx->uc_mcontext.fpregs = &guest_uctx->__fpregs_mem;
#define COPY_REG(x) \
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x] = Frame->State.gregs[X86State::REG_##x];
COPY_REG(R8);
COPY_REG(R9);
COPY_REG(R10);
COPY_REG(R11);
COPY_REG(R12);
COPY_REG(R13);
COPY_REG(R14);
COPY_REG(R15);
COPY_REG(RDI);
COPY_REG(RSI);
COPY_REG(RBP);
COPY_REG(RBX);
COPY_REG(RDX);
COPY_REG(RAX);
COPY_REG(RCX);
COPY_REG(RSP);
#undef COPY_REG
// Copy float registers
memcpy(guest_uctx->__fpregs_mem._st, Frame->State.mm, sizeof(Frame->State.mm));
memcpy(guest_uctx->__fpregs_mem._xmm, Frame->State.xmm, sizeof(Frame->State.xmm));
// FCW store default
guest_uctx->__fpregs_mem.fcw = Frame->State.FCW;
// Reconstruct FSW
guest_uctx->__fpregs_mem.fsw =
(Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14);
// Copy over signal stack information
guest_uctx->uc_stack.ss_flags = GuestStack->ss_flags;
guest_uctx->uc_stack.ss_sp = GuestStack->ss_sp;
guest_uctx->uc_stack.ss_size = GuestStack->ss_size;
// siginfo_t
siginfo_t *HostSigInfo = reinterpret_cast<siginfo_t*>(info);
guest_siginfo->si_signo = Signal;
switch (Signal) {
case SIGSEGV:
case SIGBUS:
guest_siginfo->si_code = HostSigInfo->si_code;
guest_siginfo->si_errno = HostSigInfo->si_errno;
// Macro expansion to get the si_addr
guest_siginfo->si_addr = HostSigInfo->si_addr;
break;
default: LogMan::Msg::D("Unhandled siginfo_t signal: %d", Signal); break;
}
Frame->State.gregs[X86State::REG_RSI] = SigInfoLocation;
Frame->State.gregs[X86State::REG_RDX] = UContextLocation;
}
else {
// XXX: 32bit Support
NewGuestSP -= sizeof(FEXCore::x86::ucontext_t);
uint64_t UContextLocation = 0; // NewGuestSP;
NewGuestSP -= sizeof(FEXCore::x86::siginfo_t);
uint64_t SigInfoLocation = 0; // NewGuestSP;
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = UContextLocation;
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = SigInfoLocation;
}
Frame->State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
}
else {
Frame->State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
}
if (CTX->Config.Is64BitMode) {
Frame->State.gregs[X86State::REG_RDI] = Signal;
// Set up the new SP for stack handling
NewGuestSP -= 8;
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
Frame->State.gregs[X86State::REG_RSP] = NewGuestSP;
}
else {
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
LOGMAN_THROW_A(CTX->X86CodeGen.SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
Frame->State.gregs[X86State::REG_RSP] = NewGuestSP;
}
return true;
}
bool Dispatcher::HandleSIGILL(int Signal, void *info, void *ucontext) {
if (ArchHelpers::Context::GetPc(ucontext) == SignalHandlerReturnAddress) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
return true;
}
if (ArchHelpers::Context::GetPc(ucontext) == PauseReturnInstruction) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
return true;
}
return false;
}
bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
FEXCore::Core::SignalEvent SignalReason = ThreadState->SignalReason.load();
auto Frame = ThreadState->CurrentFrame;
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
if (SRAEnabled && IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
// We are in jit, SRA must be spilled
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddressSpillSRA);
} else {
if (SRAEnabled) {
// We are in non-jit, SRA is already spilled
LOGMAN_THROW_A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
}
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddress);
}
// Set the new PC
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddress);
// Set our state register to point to our guest thread data
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
// Ref count our faults
// We use this to track if it is safe to clear cache
++SignalHandlerRefCounter;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
// Our thread is stopping
// We don't care about anything at this point
// Set the stack to our starting location when we entered the core and get out safely
ArchHelpers::Context::SetSp(ucontext, Frame->ReturningStackLocation);
// Our ref counting doesn't matter anymore
SignalHandlerRefCounter = 0;
// Set the new PC
if (SRAEnabled && IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
// We are in jit, SRA must be spilled
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddressSpillSRA);
} else {
if (SRAEnabled) {
// We are in non-jit, SRA is already spilled
LOGMAN_THROW_A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
}
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddress);
}
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
return false;
}
uint64_t Dispatcher::GetCompileBlockPtr() {
using ClassPtrType = void (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast CompileBlockPtr;
CompileBlockPtr.ClassPtr = &FEXCore::Context::Context::CompileBlockJit;
return CompileBlockPtr.Data;
}
void Dispatcher::RemoveCodeBuffer(uint8_t* start_to_remove) {
for (auto iter = CodeBuffers.begin(); iter != CodeBuffers.end(); ++iter) {
auto [start, end] = *iter;
if (start == reinterpret_cast<uint64_t>(start_to_remove)) {
CodeBuffers.erase(iter);
return;
}
}
}
bool Dispatcher::IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher) {
for (auto [start, end] : CodeBuffers) {
if (Address >= start && Address < end) {
return true;
}
}
if (IncludeDispatcher) {
return IsAddressInDispatcher(Address);
}
return false;
}
}
@@ -0,0 +1,85 @@
#pragma once
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Core/SignalDelegator.h>
#include "Interface/Context/Context.h"
#include <stack>
namespace FEXCore::CPU {
struct DispatcherConfig {
bool ExecuteBlocksWithCall = false;
uintptr_t ExitFunctionLink = 0;
uintptr_t ExitFunctionLinkThis = 0;
bool StaticRegisterAssignment = false;
};
class Dispatcher {
public:
virtual ~Dispatcher() = default;
CPUBackend::AsmDispatch DispatchPtr;
CPUBackend::JITCallback CallbackPtr;
FEXCore::Context::Context::IntCallbackReturn ReturnPtr;
/**
* @name Dispatch Helper functions
* @{ */
uint64_t ThreadStopHandlerAddress{};
uint64_t ThreadStopHandlerAddressSpillSRA{};
uint64_t AbsoluteLoopTopAddress{};
uint64_t AbsoluteLoopTopAddressFillSRA{};
uint64_t ThreadPauseHandlerAddress{};
uint64_t ThreadPauseHandlerAddressSpillSRA{};
uint64_t ExitFunctionLinkerAddress{};
uint64_t SignalHandlerReturnAddress{};
uint64_t PauseReturnInstruction{};
/** @} */
uint32_t SignalHandlerRefCounter{};
uint64_t Start{};
uint64_t End{};
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
bool HandleSIGILL(int Signal, void *info, void *ucontext);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
void RegisterCodeBuffer(uint8_t* start, size_t size) {
CodeBuffers.emplace_back(reinterpret_cast<uint64_t>(start),
reinterpret_cast<uint64_t>(start + size));
}
void RemoveCodeBuffer(uint8_t* start);
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
bool IsAddressInDispatcher(uint64_t Address) {
return Address >= Start && Address < End;
}
protected:
Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: CTX {ctx}
, ThreadState {Thread} {}
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
std::stack<uint64_t> SignalFrames;
bool SRAEnabled = false;
virtual void SpillSRA(void *ucontext) {}
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *ThreadState;
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame);
static uint64_t GetCompileBlockPtr();
private:
std::vector<std::tuple<uint64_t, uint64_t>> CodeBuffers; // Start, End
};
}
@@ -0,0 +1,320 @@
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
namespace FEXCore::CPU {
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE r14
X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config)
: Dispatcher(ctx, Thread)
, Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE, nullptr, this) {
using namespace Xbyak;
using namespace Xbyak::util;
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
// Temp registers
// rax, rcx, rdx, rsi, r8, r9,
// r10, r11
//
// Callee Saved
// rbx, rbp, r12, r13, r14, r15
//
// 1St Argument: rdi <ThreadState>
// XMM:
// All temp
// while (true) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Bunch of exit state stuff
// x86-64 ABI has the stack aligned when /call/ happens
// Which means the destination has a misaligned stack at that point
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
mov(STATE, rdi);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
mov(qword [rdi + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)], rsp);
Label LoopTop;
Label FullLookup;
Label CallBlock;
Label NoBlock;
Label ExitBlock;
Label ThreadPauseHandler;
L(LoopTop);
AbsoluteLoopTopAddressFillSRA = AbsoluteLoopTopAddress = getCurr<uint64_t>();
{
// Load our RIP
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
// L1 Cache
mov(r13, Thread->LookupCache->GetL1Pointer());
mov(rax, rdx);
and_(rax, LookupCache::L1_ENTRIES_MASK);
shl(rax, 4);
cmp(qword[r13 + rax + 8], rdx);
jne(FullLookup);
if (!config.ExecuteBlocksWithCall) {
jmp(qword[r13 + rax + 0]);
} else {
mov(rax, qword[r13 + rax + 0]);
jmp(CallBlock);
}
L(FullLookup);
mov(r13, Thread->LookupCache->GetPagePointer());
// Full lookup
mov(rax, rdx);
mov(rbx, Thread->LookupCache->GetVirtualMemorySize() - 1);
and_(rax, rbx);
shr(rax, 12);
// Load page pointer
mov(rdi, qword [r13 + rax * 8]);
cmp(rdi, 0);
je(NoBlock);
mov (rax, rdx);
and_(rax, 0x0FFF);
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
// check for aliasing
mov(rcx, qword [rdi + rax + 8]);
cmp(rcx, rdx);
jne(NoBlock);
// Load the block pointer
mov(rax, qword [rdi + rax]);
cmp(rax, 0);
je(NoBlock);
// Update L1
mov(r13, Thread->LookupCache->GetL1Pointer());
mov(rcx, rdx);
and_(rcx, LookupCache::L1_ENTRIES_MASK);
shl(rcx, 1);
mov(qword[r13 + rcx*8 + 8], rdx);
mov(qword[r13 + rcx*8 + 0], rax);
// Real block if we made it here
if (!config.ExecuteBlocksWithCall) {
jmp(rax);
} else {
L(CallBlock);
mov(rdi, STATE);
call(rax);
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
// If the value == 0 then branch to the top
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
je(LoopTop);
// Else we need to pause now
jmp(ThreadPauseHandler);
ud2();
}
else {
jmp(LoopTop);
}
}
}
{
L(ExitBlock);
ThreadStopHandlerAddress = getCurr<uint64_t>();
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
// Block creation
{
L(NoBlock);
// {rdi, rsi, rdx}
mov(rdi, reinterpret_cast<uint64_t>(CTX));
mov(rsi, STATE);
mov(rax, GetCompileBlockPtr());
call(rax);
// rdx already contains RIP here
jmp(LoopTop);
}
{
ExitFunctionLinkerAddress = getCurr<uint64_t>();
// {rdi, rsi, rdx}
mov(rdi, config.ExitFunctionLinkThis);
mov(rsi, STATE);
mov(rdx, rax); // rax is set at the block end
mov(rax, config.ExitFunctionLink);
call(rax);
jmp(rax);
}
{
// Pause handler
ThreadPauseHandlerAddress = getCurr<uint64_t>();
L(ThreadPauseHandler);
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
mov(rsi, STATE);
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
call(rax);
// XXX: Unsupported atm
PauseReturnInstruction = getCurr<uint64_t>();
ud2();
}
{
CallbackPtr = getCurr<CPUBackend::JITCallback>();
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
// First thing we need to move the thread state pointer back in to our register
mov(STATE, rdi);
// XXX: XMM?
// Make sure to adjust the refcounter so we don't clear the cache now
mov(rax, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
add(dword [rax], 1);
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
mov(rax, CTX->X86CodeGen.CallbackReturn);
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
sub(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])], 16);
mov(rbx, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])]);
mov(qword [rbx], rax);
// Store RIP to the context state
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.rip)], rsi);
// Back to the loop top now
jmp(LoopTop);
}
{
// Signal return handler
SignalHandlerReturnAddress = getCurr<uint64_t>();
ud2();
}
{
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// rdi = thread
// rsi = rsp
mov(rsp, rsi);
// Now jump back to the thunk
// XXX: XMM?
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
ready();
Start = reinterpret_cast<uint64_t>(getCode());
End = Start + getSize();
#if ENABLE_JITSYMBOLS
std::string Name = "Dispatch_" + std::to_string(::gettid());
CTX->Symbols.Register(Start, End-Start, Name);
#endif
}
X86Dispatcher::~X86Dispatcher() {
}
#ifdef _M_X86_64
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
DispatcherConfig config;
config.ExecuteBlocksWithCall = true;
Dispatcher = std::make_unique<X86Dispatcher>(ctx, Thread, config);
DispatchPtr = Dispatcher->DispatchPtr;
CallbackPtr = Dispatcher->CallbackPtr;
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = Dispatcher->ReturnPtr;
}
#endif
}
@@ -0,0 +1,27 @@
#pragma once
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include <FEXCore/Utils/Allocator.h>
#define XBYAK64
#include <xbyak/xbyak.h>
namespace FEXCore::CPU {
class X86Dispatcher final : public Dispatcher, public Xbyak::CodeGenerator, public Xbyak::Allocator {
public:
X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config);
virtual ~X86Dispatcher() override;
// Xbyak::Allocator
Xbyak::uint8 *alloc(size_t size) override { Size = size; return reinterpret_cast<uint8_t*>(FEXCore::Allocator::mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); }
void free(Xbyak::uint8 *p) override { FEXCore::Allocator::munmap(p, Size); }
bool useProtect() const override { return false; }
private:
size_t Size{};
};
}
+74 -36
View File
@@ -1,3 +1,10 @@
/*
$info$
tags: frontend|x86-meta-blocks
desc: Extracts instruction & block meta info, frontend multiblock logic
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Frontend.h"
#include "Interface/Core/InternalThreadState.h"
@@ -8,6 +15,7 @@
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Debug/X86Tables.h>
#include <FEXCore/Utils/LogManager.h>
#include <set>
namespace FEXCore::Frontend {
using namespace FEXCore::X86Tables;
@@ -117,7 +125,7 @@ Decoder::Decoder(FEXCore::Context::Context *ctx)
uint8_t Decoder::ReadByte() {
uint8_t Byte = InstStream[InstructionSize];
LogMan::Throw::A(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
LOGMAN_THROW_A(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
Instruction[InstructionSize] = Byte;
InstructionSize++;
return Byte;
@@ -129,26 +137,17 @@ uint8_t Decoder::PeekByte(uint8_t Offset) {
}
uint64_t Decoder::ReadData(uint8_t Size) {
uint64_t Res{};
#define READ_DATA(x, y) \
case x: { \
y const *Data = reinterpret_cast<y const*>(&InstStream[InstructionSize]); \
Res = *Data; \
} \
break
switch (Size) {
case 0: return 0;
READ_DATA(1, uint8_t);
READ_DATA(2, uint16_t);
case 3: memcpy(&Res, &InstStream[InstructionSize], Size);
READ_DATA(4, uint32_t);
READ_DATA(8, uint64_t);
default:
LogMan::Msg::A("Unknown data size to read");
return 0;
if (Size == 0) {
return 0;
}
#undef READ_DATA
if (Size > sizeof(uint64_t)) {
LOGMAN_MSG_A("Unknown data size to read");
return 0;
}
uint64_t Res = 0;
std::memcpy(&Res, &InstStream[InstructionSize], Size);
#ifndef NDEBUG
for(size_t i = 0; i < Size; ++i) {
@@ -157,6 +156,7 @@ uint64_t Decoder::ReadData(uint8_t Size) {
#else
SkipBytes(Size);
#endif
return Res;
}
@@ -285,7 +285,7 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
Operand->TypeSIB.Base = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, SIB.base, false, false, false, false, ModRM.mod == 0 ? 0b101 : 16);
uint64_t Literal {0};
LogMan::Throw::A(Displacement <= 4, "Number of bytes should be <= 4 for literal src");
LOGMAN_THROW_A(Displacement <= 4, "Number of bytes should be <= 4 for literal src");
Literal = ReadData(Displacement);
if (Displacement == 1) {
@@ -344,7 +344,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
return false;
}
LogMan::Throw::A(!(Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 && Info->Type <= FEXCore::X86Tables::TYPE_GROUP_P),
LOGMAN_THROW_A(!(Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 && Info->Type <= FEXCore::X86Tables::TYPE_GROUP_P),
"Group Ops should have been decoded before this!");
uint8_t DestSize{};
@@ -467,7 +467,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
}
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_REX_IN_BYTE)) {
LogMan::Throw::A(!HasMODRM, "This instruction shouldn't have ModRM!");
LOGMAN_THROW_A(!HasMODRM, "This instruction shouldn't have ModRM!");
// If the REX is in the byte that means the lower nibble of the OP contains the destination GPR
// This also means that the destination is always a GPR on these ones
@@ -476,6 +476,9 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
CurrentDest->TypeGPR.Type = DecodedOperand::TYPE_GPR;
DecodeInst->Dest.TypeGPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM;
CurrentDest->TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, Op & 0b111, Is8BitDest, HasREX, false, false);
if (CurrentDest->TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
return false;
}
uint8_t Bytes = Info->MoreBytes;
@@ -502,27 +505,36 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
GPR.TypeGPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM;
GPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_R ? 1 : 0, ModRM.reg, GPR8Bit, HasREX, HasXMMGPR, HasMMGPR);
if (GPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
return false;
// ModRM.mod == 0b11 == Register
// ModRM.Mod != 0b11 == Register-direct addressing
if (ModRM.mod == 0b11) {
NonGPR.TypeGPR.Type = DecodedOperand::TYPE_GPR;
NonGPR.TypeGPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM;
NonGPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, NonGPR8Bit, HasREX, HasXMMNonGPR, HasMMNonGPR);
if (NonGPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
return false;
}
else {
auto Disp = DecodeModRMs_Disp[Has16BitAddressing];
(this->*Disp)(&NonGPR, ModRM);
}
return true;
};
size_t CurrentSrc = 0;
if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM) {
if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_DST) {
ModRMOperand(DecodeInst->Src[CurrentSrc], DecodeInst->Dest, HasXMMSrc, HasXMMDst, HasMMSrc, HasMMDst, Is8BitSrc, Is8BitDest);
if (!ModRMOperand(DecodeInst->Src[CurrentSrc], DecodeInst->Dest, HasXMMSrc, HasXMMDst, HasMMSrc, HasMMDst, Is8BitSrc, Is8BitDest))
return false;
}
else {
ModRMOperand(DecodeInst->Dest, DecodeInst->Src[CurrentSrc], HasXMMDst, HasXMMSrc, HasMMDst, HasMMSrc, Is8BitDest, Is8BitSrc);
if (!ModRMOperand(DecodeInst->Dest, DecodeInst->Src[CurrentSrc], HasXMMDst, HasXMMSrc, HasMMDst, HasMMSrc, Is8BitDest, Is8BitSrc))
return false;
}
++CurrentSrc;
}
@@ -541,7 +553,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
}
if (Bytes != 0) {
LogMan::Throw::A(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
LOGMAN_THROW_A(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
DecodeInst->Src[CurrentSrc].TypeLiteral.Size = Bytes;
@@ -567,7 +579,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
DecodeInst->Src[CurrentSrc].TypeLiteral.Literal = Literal;
}
LogMan::Throw::A(Bytes == 0, "Inst at 0x%lx: 0x%04x '%s' Had an instruction of size %d with %d remaining", DecodeInst->PC, DecodeInst->OP, DecodeInst->TableInfo->Name, InstructionSize, Bytes);
LOGMAN_THROW_A(Bytes == 0, "Inst at 0x%lx: 0x%04x '%s' Had an instruction of size %d with %d remaining", DecodeInst->PC, DecodeInst->OP, DecodeInst->TableInfo->Name, InstructionSize, Bytes);
DecodeInst->InstSize = InstructionSize;
return true;
}
@@ -592,7 +604,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
return false;
}
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX, "REX PREFIX should have been decoded before this!");
LOGMAN_THROW_A(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX, "REX PREFIX should have been decoded before this!");
if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
Info->Type <= FEXCore::X86Tables::TYPE_GROUP_11) {
@@ -648,7 +660,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
3,
};
uint8_t Field = RegToField[ModRM.reg];
LogMan::Throw::A(Field != 255, "Invalid field selected!");
LOGMAN_THROW_A(Field != 255, "Invalid field selected!");
LocalOp = (Field << 3) | ModRM.rm;
return NormalOp(&SecondModRMTableOps[LocalOp], LocalOp);
@@ -682,7 +694,10 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
uint8_t Byte2 = ReadByte();
pp = Byte2 & 0b11;
map_select = Byte1 & 0b11111;
LogMan::Throw::A(map_select >= 1 && map_select <= 3, "We don't understand a map_select of: %d", map_select);
if (!(map_select >= 1 && map_select <= 3)) {
LogMan::Msg::E("We don't understand a map_select of: %d", map_select);
return false;
}
}
uint16_t VEXOp = ReadByte();
@@ -730,6 +745,8 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
DecodeInst->PC = PC;
for(;;) {
if (InstructionSize >= MAX_INST_SIZE)
return false;
uint8_t Op = ReadByte();
switch (Op) {
case 0x0F: {// Escape Op
@@ -880,7 +897,7 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
auto Info = &FEXCore::X86Tables::BaseOps[Op];
if (Info->Type == FEXCore::X86Tables::TYPE_REX_PREFIX) {
LogMan::Throw::A(CTX->Config.Is64BitMode, "Got REX prefix in 32bit mode");
LOGMAN_THROW_A(CTX->Config.Is64BitMode, "Got REX prefix in 32bit mode");
DecodeInst->Flags |= DecodeFlags::FLAG_REX_PREFIX;
// Widening displacement
@@ -910,6 +927,10 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
}
if (DecodeInst->Dest.TypeNone.Type == FEXCore::X86Tables::DecodedOperand::TYPE_GPR) {
assert(DecodeInst->Dest.TypeGPR.GPR != 255);
}
return true;
}
@@ -919,6 +940,7 @@ void Decoder::BranchTargetInMultiblockRange() {
// If the RIP setting is conditional AND within our symbol range then it can be considered for multiblock
uint64_t TargetRIP = 0;
uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4;
bool Conditional = true;
switch (DecodeInst->OP) {
@@ -928,24 +950,33 @@ void Decoder::BranchTargetInMultiblockRange() {
// auto RIPOffset = LoadSource(Op, Op->Src[0], Op->Flags);
// auto RIPTargetConst = _Constant(Op->PC + Op->InstSize);
// Target offset is PC + InstSize + Literal
LogMan::Throw::A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
LOGMAN_THROW_A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
break;
}
case 0xE9:
case 0xEB: // Both are unconditional JMP instructions
LogMan::Throw::A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
LOGMAN_THROW_A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
Conditional = false;
break;
case 0xE8: // Call - Immediate target, We don't want to inline calls
if (ExternalBranches) {
ExternalBranches->insert(DecodeInst->PC + DecodeInst->InstSize);
}
[[fallthrough]];
case 0xC2: // RET imm
case 0xC3: // RET
case 0xE8: // Call - Immediate target, We don't want to inline calls
default:
return;
break;
}
if (GPRSize == 4) {
// If we are running a 32bit guest then wrap around addresses that go above 32bit
TargetRIP &= 0xFFFFFFFFU;
}
// If the target RIP is within the symbol ranges then we are golden
if (TargetRIP >= SymbolMinAddress && TargetRIP < SymbolMaxAddress) {
// Update our conditional branch ranges before we return
@@ -965,6 +996,10 @@ void Decoder::BranchTargetInMultiblockRange() {
BlocksToDecode.find(TargetRIP) == BlocksToDecode.end()) {
BlocksToDecode.emplace(TargetRIP);
}
} else {
if (ExternalBranches) {
ExternalBranches->insert(TargetRIP);
}
}
}
@@ -988,7 +1023,7 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
// If we don't have symbols available then we become a bit optimistic about multiblock ranges
if (!SymbolAvailable) {
// If we don't have a symbol available then assume all branches are valid for multiblock
SymbolMaxAddress = ~0ULL;
SymbolMaxAddress = SectionMaxAddress;
SymbolMinAddress = EntryPoint;
}
@@ -1018,7 +1053,10 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
if (ErrorDuringDecoding) {
LogMan::Msg::D("Couldn't Decode something at 0x%lx, Started at 0x%lx", PC + PCOffset, PC);
LogMan::Throw::A(Blocks.size() != 1, "Decode Error in entry block");
if (Blocks.size() == 1) {
return false;
}
LOGMAN_THROW_A(Blocks.size() != 1, "Decode Error in entry block");
CurrentBlockDecoding.HasInvalidInstruction = true;
if (ErrorDuringDecoding && Blocks.size() != 1) {
+4
View File
@@ -33,6 +33,8 @@ public:
uint64_t DecodedMinAddress {};
uint64_t DecodedMaxAddress {~0ULL};
void SetSectionMaxAddress(uint64_t v) { SectionMaxAddress = v; }
void SetExternalBranches(std::set<uint64_t> *v) { ExternalBranches = v; }
private:
FEXCore::Context::Context *CTX;
@@ -65,10 +67,12 @@ private:
uint64_t MaxCondBranchBackwards {~0ULL};
uint64_t SymbolMaxAddress {};
uint64_t SymbolMinAddress {~0ULL};
uint64_t SectionMaxAddress {~0ULL};
std::vector<DecodedBlocks> Blocks;
std::set<uint64_t> BlocksToDecode;
std::set<uint64_t> HasBlocks;
std::set<uint64_t> *ExternalBranches {nullptr};
// ModRM rm decoding
using DecodeModRMPtr = void (FEXCore::Frontend::Decoder::*)(X86Tables::DecodedOperand *Operand, X86Tables::ModRMDecoded ModRM);
+22 -10
View File
@@ -1,3 +1,10 @@
/*
$info$
tags: glue|gdbserver
desc: Provides a gdb interface to the guest state
$end_info$
*/
#include <cstdlib>
#include <cstdio>
#include <iomanip>
@@ -232,17 +239,17 @@ std::string GdbServer::readRegs() {
bool Found = false;
for (auto &Thread : *Threads) {
if (Thread->State.ThreadManager.GetTID() != CurrentDebuggingThread) {
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
continue;
}
state = Thread->State.State;
memcpy(&state, Thread->CurrentFrame, sizeof(state));
Found = true;
break;
}
if (!Found) {
// If set to an invalid thread then just get the parent thread ID
state = CTX->GetCPUState();
memcpy(&state, CTX->ParentThread->CurrentFrame, sizeof(state));
}
// Encode the GDB context definition
@@ -284,17 +291,17 @@ GdbServer::HandledPacketType GdbServer::readReg(std::string& packet) {
bool Found = false;
for (auto &Thread : *Threads) {
if (Thread->State.ThreadManager.GetTID() != CurrentDebuggingThread) {
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
continue;
}
state = Thread->State.State;
memcpy(&state, Thread->CurrentFrame, sizeof(state));
Found = true;
break;
}
if (!Found) {
// If set to an invalid thread then just get the parent thread ID
state = CTX->GetCPUState();
memcpy(&state, CTX->ParentThread->CurrentFrame, sizeof(state));
}
@@ -525,7 +532,7 @@ GdbServer::HandledPacketType GdbServer::handleXfer(std::string &packet) {
ss << "<threads>\n";
for (size_t i = 0; i < Threads->size(); ++i) {
auto Thread = Threads->at(i);
ss << "\t<thread id=\"" << std::hex << Thread->State.ThreadManager.GetTID() << "\" core=\"" << i << "\" name=\"" << getThreadName(Thread->State.ThreadManager.GetTID()) << "\">\n";
ss << "\t<thread id=\"" << std::hex << Thread->ThreadManager.GetTID() << "\" core=\"" << i << "\" name=\"" << getThreadName(Thread->ThreadManager.GetTID()) << "\">\n";
ss << "\t</thread>\n";
}
@@ -653,7 +660,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
ss << "m";
for (size_t i = 0; i < Threads->size(); ++i) {
auto Thread = Threads->at(i);
ss << std::hex << Thread->State.ThreadManager.TID << ",";
ss << std::hex << Thread->ThreadManager.TID << ",";
}
return {ss.str(), HandledPacketType::TYPE_ACK};
}
@@ -672,7 +679,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
if (match("qC")) {
// Returns the current Thread ID
std::ostringstream ss;
ss << "m" << std::hex << CTX->ParentThread->State.ThreadManager.TID;
ss << "m" << std::hex << CTX->ParentThread->ThreadManager.TID;
return {ss.str(), HandledPacketType::TYPE_ACK};
}
if (match("QStartNoAckMode")) {
@@ -952,9 +959,14 @@ void GdbServer::GdbServerLoop() {
}
}
}
static void* ThreadHandler(void *Arg) {
FEXCore::GdbServer *This = reinterpret_cast<FEXCore::GdbServer*>(Arg);
This->GdbServerLoop();
return nullptr;
}
void GdbServer::StartThread() {
gdbServerThread = std::thread(&GdbServer::GdbServerLoop, this);
gdbServerThread = FEXCore::Threads::Thread::Create(ThreadHandler, this);
}
std::unique_ptr<std::iostream> GdbServer::OpenSocket() {
+15 -4
View File
@@ -1,23 +1,34 @@
/*
$info$
tags: glue|gdbserver
$end_info$
*/
#pragma once
#include <mutex>
#include <thread>
#include "Interface/Context/Context.h"
#include "Common/NetStream.h"
#include <FEXCore/Utils/Threads.h>
#include <mutex>
namespace FEXCore {
class GdbServer {
public:
GdbServer(FEXCore::Context::Context *ctx);
// Public for threading
void GdbServerLoop();
private:
void Break(int signal);
std::unique_ptr<std::iostream> OpenSocket();
void StartThread();
void GdbServerLoop();
std::string ReadPacket(std::iostream &stream);
void SendPacket(std::ostream &stream, std::string packet);
@@ -50,14 +61,14 @@ private:
HandledPacketType readReg(std::string& packet);
FEXCore::Context::Context *CTX;
std::thread gdbServerThread;
std::unique_ptr<FEXCore::Threads::Thread> gdbServerThread;
std::unique_ptr<std::iostream> CommsStream;
std::mutex sendMutex;
bool SettingNoAckMode{false};
bool NoAckMode{false};
std::string ThreadString{};
uint32_t CurrentDebuggingThread{};
FEXCore::Config::Value<std::string> Filename{FEXCore::Config::CONFIG_APP_FILENAME, ""};
FEX_CONFIG_OPT(Filename, APP_FILENAME);
};
}
@@ -1,563 +0,0 @@
#include "Common/MathUtils.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
#include "aarch64/assembler-aarch64.h"
namespace FEXCore::CPU {
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
// Go to sleep
Thread->StartRunning.Wait();
Thread->State.RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
}
using namespace vixl;
using namespace vixl::aarch64;
#define STATE x28
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
class DispatchGenerator : public vixl::aarch64::Assembler {
public:
DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
CPUBackend::AsmDispatch DispatchPtr;
CPUBackend::JITCallback CallbackPtr;
uint64_t ThreadStopHandlerAddress;
uint64_t AbsoluteLoopTopAddress;
uint64_t ThreadPauseHandlerAddress;
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *State;
private:
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
void PushCalleeSavedRegisters();
void PopCalleeSavedRegisters();
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
std::stack<uint64_t> SignalFrames;
};
void DispatchGenerator::PushCalleeSavedRegisters() {
// We need to save pairs of registers
// We save r19-r30
MemOperand PairOffset(sp, -16, PreIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x19, x20},
{x21, x22},
{x23, x24},
{x25, x26},
{x27, x28},
{x29, x30},
}};
for (auto &RegPair : CalleeSaved) {
stp(RegPair.first, RegPair.second, PairOffset);
}
// Additionally we need to store the lower 64bits of v8-v15
// Here's a fun thing, we can use two ST4 instructions to store everything
// We just need a single sub to sp before that
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v8, v9, v10, v11},
{v12, v13, v14, v15},
}};
uint32_t VectorSaveSize = sizeof(uint64_t) * 8;
sub(sp, sp, VectorSaveSize);
// SP supporting move
// We just saved x19 so it is safe
add(x19, sp, 0);
MemOperand QuadOffset(x19, 32, PostIndex);
for (auto &RegQuad : FPRs) {
st4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
}
void DispatchGenerator::PopCalleeSavedRegisters() {
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v12, v13, v14, v15},
{v8, v9, v10, v11},
}};
MemOperand QuadOffset(sp, 32, PostIndex);
for (auto &RegQuad : FPRs) {
ld4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
MemOperand PairOffset(sp, 16, PostIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x29, x30},
{x27, x28},
{x25, x26},
{x23, x24},
{x21, x22},
{x19, x20},
}};
for (auto &RegPair : CalleeSaved) {
ldp(RegPair.first, RegPair.second, PairOffset);
}
}
void DispatchGenerator::LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant) {
bool Is64Bit = Reg.IsX();
int Segments = Is64Bit ? 4 : 2;
movz(Reg, (Constant) & 0xFFFF, 0);
for (int i = 1; i < Segments; ++i) {
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
if (Part) {
movk(Reg, Part, i * 16);
}
}
}
DispatchGenerator::DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: vixl::aarch64::Assembler(MAX_DISPATCHER_CODE_SIZE, vixl::aarch64::PositionDependentCode)
, CTX {ctx}
, State {Thread} {
SetAllowAssembler(true);
auto Buffer = GetBuffer();
DispatchPtr = Buffer->GetOffsetAddress<CPUBackend::AsmDispatch>(GetCursorOffset());
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Push all the register we need to save
PushCalleeSavedRegisters();
// Push our memory base to the correct register
// Move our thread pointer to the correct register
// This is passed in to parameter 0 (x0)
mov(STATE, x0);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
add(x0, sp, 0);
str(x0, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)));
Label Exit;
Label LoopTop;
Label NoBlock;
Label ThreadPauseHandler;
bind(&LoopTop);
AbsoluteLoopTopAddress = GetLabelAddress<uint64_t>(&LoopTop);
// Load in our RIP
// Don't modify x2 since it contains our RIP once the block doesn't exist
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, State.rip)));
auto RipReg = x2;
// Mask the address by the virtual address size so we can check for aliases
LoadConstant(x3, Thread->LookupCache->GetVirtualMemorySize() - 1);
and_(x3, RipReg, x3);
{
// This is the block cache lookup routine
// It matches what is going on it LookupCache.h::FindBlock
LoadConstant(x0, Thread->LookupCache->GetPagePointer());
// Offset the address and add to our page pointer
lsr(x1, x3, 12);
// Load the pointer from the offset
ldr(x0, MemOperand(x0, x1, Shift::LSL, 3));
// If page pointer is zero then we have no block
cbz(x0, &NoBlock);
// Steal the page offset
and_(x1, x3, 0x0FFF);
// Shift the offset by the size of the block cache entry
add(x0, x0, Operand(x1, Shift::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry))));
// Load the guest address first to ensure it maps to the address we are currently at
// This fixes aliasing problems
ldr(x1, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode)));
cmp(x1, RipReg);
b(&NoBlock, Condition::ne);
// Now load the actual host block to execute if we can
ldr(x1, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode)));
cbz(x1, &NoBlock);
// If we've made it here then we have a real compiled block
{
mov(x0, STATE);
blr(x1);
}
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, CTX)));
ldr(w0, MemOperand(x0, offsetof(FEXCore::Context::Context, Config.RunningMode)));
// If the value == 0 then branch to the top
cbz(x0, &LoopTop);
// Else we need to pause now
b(&ThreadPauseHandler);
}
else {
// Unconditionally loop to the top
// We will only stop on error when compiling a block or signal
b(&LoopTop);
}
}
{
bind(&Exit);
ThreadStopHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
PopCalleeSavedRegisters();
// Return from the function
// LR is set to the correct return location now
ret();
}
// Need to create the block
{
bind(&NoBlock);
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, CTX)));
mov(x1, STATE);
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
LoadConstant(x3, Ptr.Data);
// X2 contains our guest RIP
blr(x3); // { CTX, ThreadState, RIP}
b(&LoopTop);
}
{
bind(&ThreadPauseHandler);
ThreadPauseHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// We are pausing, this means the frontend should be waiting for this thread to idle
// We will have faulted and jumped to this location at this point
// Call our sleep handler
LoadConstant(x0, reinterpret_cast<uintptr_t>(CTX));
mov(x1, STATE);
LoadConstant(x2, reinterpret_cast<uint64_t>(SleepThread));
blr(x2);
// XXX: Unsupported atm
//PauseReturnInstruction = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
//// Fault to start running again
//hlt(0);
}
{
CallbackPtr = Buffer->GetOffsetAddress<CPUBackend::JITCallback>(GetCursorOffset());
// We expect the thunk to have previously pushed the registers it was using
PushCalleeSavedRegisters();
// First thing we need to move the thread state pointer back in to our register
mov(STATE, x0);
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
LoadConstant(x0, CTX->X86CodeGen.CallbackReturn);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
sub(x2, x2, 16);
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
str(x0, MemOperand(x2));
// Store RIP to the context state
str(x1, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.rip)));
// Now go back to the regular dispatcher loop
b(&LoopTop);
}
FinalizeCode();
uint64_t CodeEnd = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
vixl::aarch64::CPU::EnsureIAndDCacheCoherency(reinterpret_cast<void*>(DispatchPtr), CodeEnd - reinterpret_cast<uint64_t>(DispatchPtr));
GetBuffer()->SetExecutable();
}
struct HostCTXHeader {
uint32_t Magic;
uint32_t Size;
};
constexpr uint32_t FPR_MAGIC = 0x46508001U;
struct HostFPRState {
HostCTXHeader Head;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
};
struct ContextBackup {
// Host State
uint64_t GPRs[31];
uint64_t PrevSP;
uint64_t PrevPC;
uint64_t PState;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
};
void DispatchGenerator::StoreThreadState(int Signal, void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
uint64_t OldSP = _mcontext->sp;
uintptr_t NewSP = OldSP;
size_t StackOffset = sizeof(ContextBackup);
NewSP -= StackOffset;
NewSP = AlignDown(NewSP, 16);
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
memcpy(&Context->GPRs[0], &_mcontext->regs[0], 31 * sizeof(uint64_t));
Context->PrevSP = _mcontext->sp;
Context->PrevPC = _mcontext->pc;
Context->PState = _mcontext->pstate;
// Host FPR state starts at _mcontext->reserved[0];
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
Context->FPSR = HostState->FPSR;
Context->FPCR = HostState->FPCR;
memcpy(&Context->FPRs[0], &HostState->FPRs[0], 32 * sizeof(__uint128_t));
// Retain the action pointer so we can see it when we return
Context->Signal = Signal;
// Save guest state
// We can't guarantee if registers are in context or host GPRs
// So we need to save everything
memcpy(&Context->GuestState, &State->State, sizeof(FEXCore::Core::CPUState));
// Set the new SP
_mcontext->sp = NewSP;
}
void DispatchGenerator::RestoreThreadState(void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
uint64_t OldSP = _mcontext->sp;
uintptr_t NewSP = OldSP;
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
// First thing, reset the guest state
memcpy(&State->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
memcpy(&HostState->FPRs[0], &Context->FPRs[0], 32 * sizeof(__uint128_t));
Context->FPCR = HostState->FPCR;
Context->FPSR = HostState->FPSR;
// Restore GPRs and other state
_mcontext->pstate = Context->PState;
_mcontext->pc = Context->PrevPC;
_mcontext->sp = Context->PrevSP;
memcpy(&_mcontext->regs[0], &Context->GPRs[0], 31 * sizeof(uint64_t));
// Restore the previous signal state
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
}
bool DispatchGenerator::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->pc = AbsoluteLoopTopAddress;
// Set x28 (which is our state register) to point to our guest thread data
_mcontext->regs[28 /* STATE */] = reinterpret_cast<uint64_t>(State);
State->State.State.gregs[X86State::REG_RDI] = Signal;
uint64_t OldGuestSP = State->State.State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
if (!(GuestStack->ss_flags & SS_DISABLE)) {
// If our guest is already inside of the alternative stack
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
if (OldGuestSP >= AltStackBase &&
OldGuestSP <= AltStackEnd) {
// We are already in the alt stack, the rest of the code will handle adjusting this
}
else {
NewGuestSP = AltStackEnd;
}
}
// Back up past the redzone, which is 128bytes
// Don't need this offset if we aren't going to be putting siginfo in to it
NewGuestSP -= 128;
if (GuestAction->sa_flags & SA_SIGINFO) {
// XXX: siginfo_t(RSI), ucontext (RDX)
State->State.State.gregs[X86State::REG_RSI] = 0;
State->State.State.gregs[X86State::REG_RDX] = 0;
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
}
else {
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
}
// Set up the new SP for stack handling
NewGuestSP -= 8;
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
State->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
return true;
}
bool DispatchGenerator::HandleSignalPause(int Signal, void *info, void *ucontext) {
FEXCore::Core::SignalEvent SignalReason = State->SignalReason.load();
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->pc = ThreadPauseHandlerAddress;
// Set our state register to point to our guest thread data
_mcontext->regs[28 /* STATE */] = reinterpret_cast<uint64_t>(State);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
RestoreThreadState(ucontext);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Our thread is stopping
// We don't care about anything at this point
// Set the stack to our starting location when we entered the JIT and get out safely
_mcontext->sp = State->State.ReturningStackLocation;
// Set the new PC
_mcontext->pc = ThreadStopHandlerAddress;
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
return false;
}
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
Generator = new DispatchGenerator(ctx, Thread);
DispatchPtr = Generator->DispatchPtr;
CallbackPtr = Generator->CallbackPtr;
// TODO: Implement this. It is missing from the dispatcher
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = nullptr;
}
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
DispatchGenerator *Gen = Generator;
return Gen->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
}
bool InterpreterCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
DispatchGenerator *Gen = Generator;
return Gen->HandleSignalPause(Signal, info, ucontext);
}
void InterpreterCore::DeleteAsmDispatch() {
delete Generator;
}
}
@@ -2,13 +2,15 @@
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/InternalThreadState.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
namespace FEXCore::CPU {
class DispatchGenerator;
class X86DispatchGenerator;
class Arm64DispatchGenerator;
#define DESTMAP_AS_MAP 0
#if DESTMAP_AS_MAP
@@ -20,16 +22,14 @@ using DestMapType = std::vector<uint32_t>;
class InterpreterCore final : public CPUBackend {
public:
explicit InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
~InterpreterCore() override;
std::string GetName() override { return "Interpreter"; }
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *CompileCode(uint64_t Entry, FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
bool NeedsOpDispatch() override { return true; }
void CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
void DeleteAsmDispatch();
bool HandleSIGBUS(int Signal, void *info, void *ucontext);
@@ -38,8 +38,6 @@ private:
FEXCore::Core::InternalThreadState *State;
uint32_t AllocateTmpSpace(size_t Size);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
template<typename Res>
Res GetDest(void* SSAData, IR::OrderedNodeWrapper Op);
@@ -47,7 +45,7 @@ private:
template<typename Res>
Res GetSrc(void* SSAData, IR::OrderedNodeWrapper Src);
DispatchGenerator *Generator{};
std::unique_ptr<Dispatcher> Dispatcher{};
};
}
@@ -2,9 +2,8 @@
#include "Common/SoftFloat.h"
#include "Interface/Context/Context.h"
#ifdef _M_ARM_64
#include "Interface/Core/ArchHelpers/Arm64.h"
#endif
#include "Interface/Core/ArchHelpers/MContext.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/DebugData.h"
#include "Interface/Core/InternalThreadState.h"
@@ -22,62 +21,64 @@
#include <cmath>
#include <limits>
#include <vector>
#ifdef _M_X86_64
#include <xmmintrin.h>
#endif
#include "InterpreterOps.h"
namespace FEXCore::CPU {
static void InterpreterExecution(FEXCore::Core::InternalThreadState *Thread) {
auto LocalEntry = Thread->LocalIRCache.find(Thread->State.State.rip);
InterpreterOps::InterpretIR(Thread, LocalEntry->second.IR.get(), LocalEntry->second.DebugData.get());
}
static void InterpreterExecution(FEXCore::Core::CpuStateFrame *Frame) {
auto Thread = Frame->Thread;
auto LocalEntry = Thread->LocalIRCache.find(Thread->CurrentFrame->State.rip);
InterpreterOps::InterpretIR(Thread, Thread->CurrentFrame->State.rip, LocalEntry->second.IR.get(), LocalEntry->second.DebugData.get());
}
bool InterpreterCore::HandleSIGBUS(int Signal, void *info, void *ucontext) {
#ifdef _M_ARM_64
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
uint32_t *PC = (uint32_t*)_mcontext->pc;
uint32_t Instr = PC[0];
if ((Instr & FEXCore::ArchHelpers::Arm64::CASPAL_MASK) == FEXCore::ArchHelpers::Arm64::CASPAL_INST) { // CASPAL
if (FEXCore::ArchHelpers::Arm64::HandleCASPAL(_mcontext, info, Instr)) {
// Skip this instruction now
_mcontext->pc += 4;
return true;
}
else {
LogMan::Msg::E("Unhandled JIT SIGBUS CASPAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
return false;
}
}
else if ((Instr & FEXCore::ArchHelpers::Arm64::CASAL_MASK) == FEXCore::ArchHelpers::Arm64::CASAL_INST) { // CASAL
if (FEXCore::ArchHelpers::Arm64::HandleCASAL(_mcontext, info, Instr)) {
// Skip this instruction now
_mcontext->pc += 4;
return true;
}
else {
LogMan::Msg::E("Unhandled JIT SIGBUS CASAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
return false;
}
}
else if ((Instr & FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_MASK) == FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_INST) { // Atomic memory op
if (FEXCore::ArchHelpers::Arm64::HandleAtomicMemOp(_mcontext, info, Instr)) {
// Skip this instruction now
_mcontext->pc += 4;
return true;
}
else {
uint8_t Op = (PC[0] >> 12) & 0xF;
LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x: PC: %p Instruction: 0x%08x\n", Op, PC, PC[0]);
return false;
}
}
constexpr bool is_arm64 = true;
#else
constexpr bool is_arm64 = false;
#endif
if constexpr (is_arm64) {
uint32_t *PC = reinterpret_cast<uint32_t*>(ArchHelpers::Context::GetPc(ucontext));
uint32_t Instr = PC[0];
if ((Instr & FEXCore::ArchHelpers::Arm64::CASPAL_MASK) == FEXCore::ArchHelpers::Arm64::CASPAL_INST) { // CASPAL
if (FEXCore::ArchHelpers::Arm64::HandleCASPAL(ucontext, info, Instr)) {
// Skip this instruction now
ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4);
return true;
}
else {
LogMan::Msg::E("Unhandled JIT SIGBUS CASPAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
return false;
}
}
else if ((Instr & FEXCore::ArchHelpers::Arm64::CASAL_MASK) == FEXCore::ArchHelpers::Arm64::CASAL_INST) { // CASAL
if (FEXCore::ArchHelpers::Arm64::HandleCASAL(ucontext, info, Instr)) {
// Skip this instruction now
ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4);
return true;
}
else {
LogMan::Msg::E("Unhandled JIT SIGBUS CASAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
return false;
}
}
else if ((Instr & FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_MASK) == FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_INST) { // Atomic memory op
if (FEXCore::ArchHelpers::Arm64::HandleAtomicMemOp(ucontext, info, Instr)) {
// Skip this instruction now
ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4);
return true;
}
else {
uint8_t Op = (PC[0] >> 12) & 0xF;
LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x: PC: %p Instruction: 0x%08x\n", Op, PC, PC[0]);
return false;
}
}
}
return false;
}
@@ -91,7 +92,7 @@ InterpreterCore::InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::
CreateAsmDispatch(ctx, Thread);
CTX->SignalDelegation->RegisterHostSignalHandler(SignalDelegator::SIGNAL_FOR_PAUSE, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
InterpreterCore *Core = reinterpret_cast<InterpreterCore*>(Thread->CPUBackend.get());
return Core->HandleSignalPause(Signal, info, ucontext);
return Core->Dispatcher->HandleSignalPause(Signal, info, ucontext);
});
CTX->SignalDelegation->RegisterHostSignalHandler(SIGBUS, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
@@ -101,7 +102,7 @@ InterpreterCore::InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::
auto GuestSignalHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) -> bool {
InterpreterCore *Core = reinterpret_cast<InterpreterCore*>(Thread->CPUBackend.get());
return Core->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
return Core->Dispatcher->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
};
for (uint32_t Signal = 0; Signal < SignalDelegator::MAX_SIGNALS; ++Signal) {
@@ -110,13 +111,7 @@ InterpreterCore::InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::
}
}
InterpreterCore::~InterpreterCore() {
DeleteAsmDispatch();
}
void *InterpreterCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
return reinterpret_cast<void*>(InterpreterExecution);
}
File diff suppressed because it is too large. Load diff
@@ -36,7 +36,7 @@ namespace FEXCore::CPU {
class InterpreterOps {
public:
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData);
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, uint64_t Entry, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData);
static bool GetFallbackHandler(IR::IROp_Header *IROp, FallbackInfo *Info);
};
};
@@ -1,470 +0,0 @@
#include "Common/MathUtils.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
#include <xbyak/xbyak.h>
namespace FEXCore::CPU {
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE r14
class DispatchGenerator : public Xbyak::CodeGenerator {
public:
DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
CPUBackend::AsmDispatch DispatchPtr;
CPUBackend::JITCallback CallbackPtr;
FEXCore::Context::Context::IntCallbackReturn ReturnPtr;
uint64_t ThreadStopHandlerAddress;
uint64_t AbsoluteLoopTopAddress;
uint64_t ThreadPauseHandlerAddress;
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *State;
private:
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
std::stack<uint64_t> SignalFrames;
};
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
// Go to sleep
Thread->StartRunning.Wait();
Thread->State.RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
}
DispatchGenerator::DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE)
, CTX {ctx}
, State {Thread} {
using namespace Xbyak;
using namespace Xbyak::util;
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Bunch of exit state stuff
// x86-64 ABI has the stack aligned when /call/ happens
// Which means the destination has a misaligned stack at that point
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
mov(STATE, rdi);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
mov(qword [rdi + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)], rsp);
Label LoopTop;
Label NoBlock;
Label ExitBlock;
Label ThreadPauseHandler;
L(LoopTop);
AbsoluteLoopTopAddress = getCurr<uint64_t>();
{
mov(r13, Thread->LookupCache->GetPagePointer());
// Load our RIP
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
mov(rax, rdx);
mov(rbx, Thread->LookupCache->GetVirtualMemorySize() - 1);
and_(rax, rbx);
shr(rax, 12);
// Load page pointer
mov(rdi, qword [r13 + rax * 8]);
cmp(rdi, 0);
je(NoBlock);
mov (rax, rdx);
and_(rax, 0x0FFF);
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
// check for aliasing
mov(rcx, qword [rdi + rax + 8]);
cmp(rcx, rdx);
jne(NoBlock);
// Load the block pointer
mov(rax, qword [rdi + rax]);
cmp(rax, 0);
je(NoBlock);
// Real block if we made it here
mov(rdi, STATE);
call(rax);
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
// If the value == 0 then branch to the top
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
je(LoopTop);
// Else we need to pause now
jmp(ThreadPauseHandler);
ud2();
}
else {
jmp(LoopTop);
}
}
{
L(ExitBlock);
ThreadStopHandlerAddress = getCurr<uint64_t>();
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
// Block creation
{
L(NoBlock);
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
// {rdi, rsi, rdx}
mov(rdi, reinterpret_cast<uint64_t>(CTX));
mov(rsi, STATE);
mov(rax, Ptr.Data);
call(rax);
// rdx already contains RIP here
jmp(LoopTop);
}
{
// Pause handler
ThreadPauseHandlerAddress = getCurr<uint64_t>();
L(ThreadPauseHandler);
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
mov(rsi, STATE);
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
call(rax);
// XXX: Unsupported atm
// uint64_t PauseReturnInstruction = getCurr<uint64_t>();
// ud2();
}
{
CallbackPtr = getCurr<CPUBackend::JITCallback>();
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
// First thing we need to move the thread state pointer back in to our register
mov(STATE, rdi);
// XXX: XMM?
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
mov(rax, CTX->X86CodeGen.CallbackReturn);
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
sub(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 16);
mov(rbx, qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])]);
mov(qword [rbx], rax);
// Store RIP to the context state
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], rsi);
// Back to the loop top now
jmp(LoopTop);
}
{
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// rdi = thread
// rsi = rsp
mov(rsp, rsi);
// Now jump back to the thunk
// XXX: XMM?
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
ready();
}
struct ContextBackup {
uint64_t StoredCookie;
// Host State
// RIP and RSP is stored in GPRs here
uint64_t GPRs[NGREG];
_libc_fpstate FPRState;
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
};
void DispatchGenerator::StoreThreadState(int Signal, void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
uint64_t OldSP = _mcontext->gregs[REG_RSP];
uintptr_t NewSP = OldSP;
size_t StackOffset = sizeof(ContextBackup);
// We need to back up behind the host's red zone
// We do this on the guest side as well
NewSP -= 128;
NewSP -= StackOffset;
NewSP = AlignDown(NewSP, 16);
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
Context->StoredCookie = 0x4142434445464748ULL;
// Copy the GPRs
memcpy(&Context->GPRs[0], &_mcontext->gregs[0], NGREG * sizeof(_mcontext->gregs[0]));
// Copy the FPRState
memcpy(&Context->FPRState, _mcontext->fpregs, sizeof(_libc_fpstate));
// XXX: Save 256bit and 512bit AVX register state
// Retain the action pointer so we can see it when we return
Context->Signal = Signal;
// Save guest state
// We can't guarantee if registers are in context or host GPRs
// So we need to save everything
memcpy(&Context->GuestState, &State->State, sizeof(FEXCore::Core::CPUState));
// Set the new SP
_mcontext->gregs[REG_RSP] = NewSP;
SignalFrames.push(NewSP);
}
void DispatchGenerator::RestoreThreadState(void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
uint64_t OldSP = SignalFrames.top();
SignalFrames.pop();
uintptr_t NewSP = OldSP;
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
if (Context->StoredCookie != 0x4142434445464748ULL) {
LogMan::Msg::D("COOKIE WAS NOT CORRECT!\n");
exit(-1);
}
// First thing, reset the guest state
memcpy(&State->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
// Copy the GPRs
memcpy(&_mcontext->gregs[0], &Context->GPRs[0], NGREG * sizeof(_mcontext->gregs[0]));
// Copy the FPRState
memcpy(_mcontext->fpregs, &Context->FPRState, sizeof(_libc_fpstate));
// Restore the previous signal state
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
}
bool DispatchGenerator::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->gregs[REG_RIP] = AbsoluteLoopTopAddress;
// Set our state register to point to our guest thread data
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(State);
uint64_t OldGuestSP = State->State.State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
if (!(GuestStack->ss_flags & SS_DISABLE)) {
// If our guest is already inside of the alternative stack
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
if (OldGuestSP >= AltStackBase &&
OldGuestSP <= AltStackEnd) {
// We are already in the alt stack, the rest of the code will handle adjusting this
}
else {
NewGuestSP = AltStackEnd;
}
}
// Back up past the redzone, which is 128bytes
// Don't need this offset if we aren't going to be putting siginfo in to it
NewGuestSP -= 128;
State->State.State.gregs[X86State::REG_RDI] = Signal;
if (GuestAction->sa_flags & SA_SIGINFO) {
// XXX: siginfo_t(RSI), ucontext (RDX)
State->State.State.gregs[X86State::REG_RSI] = 0;
State->State.State.gregs[X86State::REG_RDX] = 0;
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
}
else {
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
}
// Set up the new SP for stack handling
NewGuestSP -= 8;
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
State->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
return true;
}
bool DispatchGenerator::HandleSignalPause(int Signal, void *info, void *ucontext) {
FEXCore::Core::SignalEvent SignalReason = State->SignalReason.load();
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->gregs[REG_RIP] = ThreadPauseHandlerAddress;
// Set our state register to point to our guest thread data
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(State);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Our thread is stopping
// We don't care about anything at this point
// Set the stack to our starting location when we entered the core and get out safely
_mcontext->gregs[REG_RSP] = State->State.ReturningStackLocation;
// Set the new PC
_mcontext->gregs[REG_RIP] = ThreadStopHandlerAddress;
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
RestoreThreadState(ucontext);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
return false;
}
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
Generator = new DispatchGenerator(ctx, Thread);
DispatchPtr = Generator->DispatchPtr;
CallbackPtr = Generator->CallbackPtr;
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = Generator->ReturnPtr;
}
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
DispatchGenerator *Gen = Generator;
return Gen->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
}
bool InterpreterCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
DispatchGenerator *Gen = Generator;
return Gen->HandleSignalPause(Signal, info, ucontext);
}
void InterpreterCore::DeleteAsmDispatch() {
delete Generator;
}
}
+50 -63
View File
@@ -1,3 +1,8 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -29,7 +34,7 @@ static int64_t LREM(int64_t SrcHigh, int64_t SrcLow, int64_t Divisor) {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(TruncElementPair) {
auto Op = IROp->C<IR::IROp_TruncElementPair>();
@@ -41,7 +46,7 @@ DEF_OP(TruncElementPair) {
mov(Dst.second, Src.second);
break;
}
default: LogMan::Msg::A("Unhandled Truncation size: %d", Op->Size); break;
default: LOGMAN_MSG_A("Unhandled Truncation size: %d", Op->Size); break;
}
}
@@ -54,7 +59,7 @@ DEF_OP(Constant) {
DEF_OP(EntrypointOffset) {
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
auto Constant = IR->GetHeader()->Entry + Op->Offset;
auto Constant = Entry + Op->Offset;
auto Dst = GetReg<RA_64>(Node);
LoadConstant(Dst, Constant);
}
@@ -90,7 +95,7 @@ DEF_OP(Add) {
case 8:
add(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), Const);
break;
default: LogMan::Msg::A("Unsupported Add size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Add size: %d", OpSize);
}
} else {
switch (OpSize) {
@@ -100,7 +105,7 @@ DEF_OP(Add) {
case 8:
add(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unsupported Add size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Add size: %d", OpSize);
}
}
}
@@ -116,7 +121,7 @@ DEF_OP(Sub) {
case 8:
sub(GRS(Node), GRS(Op->Header.Args[0].ID()), Const);
break;
default: LogMan::Msg::A("Unsupported Sub size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Sub size: %d", OpSize);
}
} else {
switch (OpSize) {
@@ -126,7 +131,7 @@ DEF_OP(Sub) {
case 8:
sub(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unsupported Sub size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Sub size: %d", OpSize);
}
}
@@ -142,7 +147,7 @@ DEF_OP(Neg) {
case 8:
neg(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
}
}
@@ -154,12 +159,11 @@ DEF_OP(Mul) {
switch (OpSize) {
case 4:
mul(Dst.W(), GetReg<RA_32>(Op->Header.Args[0].ID()), GetReg<RA_32>(Op->Header.Args[1].ID()));
sxtw(Dst, Dst);
break;
case 8:
mul(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -175,7 +179,7 @@ DEF_OP(UMul) {
case 8:
mul(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -212,7 +216,7 @@ DEF_OP(Div) {
sdiv(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown DIV Size: %d", Size); break;
default: LOGMAN_MSG_A("Unknown DIV Size: %d", Size); break;
}
}
@@ -239,7 +243,7 @@ DEF_OP(UDiv) {
udiv(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown UDIV Size: %d", Size); break;
default: LOGMAN_MSG_A("Unknown UDIV Size: %d", Size); break;
}
}
@@ -286,7 +290,7 @@ DEF_OP(Rem) {
msub(GetReg<RA_64>(Node), TMP1, Divisor, Dividend);
break;
}
default: LogMan::Msg::A("Unknown REM Size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown REM Size: %d", OpSize); break;
}
}
@@ -328,7 +332,7 @@ DEF_OP(URem) {
msub(GetReg<RA_64>(Node), TMP1, Divisor, Dividend);
break;
}
default: LogMan::Msg::A("Unknown UREM Size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown UREM Size: %d", OpSize); break;
}
}
@@ -340,12 +344,12 @@ DEF_OP(MulH) {
sxtw(TMP1, GetReg<RA_64>(Op->Header.Args[0].ID()));
sxtw(TMP2, GetReg<RA_64>(Op->Header.Args[1].ID()));
mul(TMP1, TMP1, TMP2);
sbfx(GetReg<RA_64>(Node), TMP1, 32, 32);
ubfx(GetReg<RA_64>(Node), TMP1, 32, 32);
break;
case 8:
smulh(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -362,7 +366,7 @@ DEF_OP(UMulH) {
case 8:
umulh(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -398,7 +402,6 @@ DEF_OP(Xor) {
DEF_OP(Lshl) {
auto Op = IROp->C<IR::IROp_Lshl>();
uint8_t OpSize = IROp->Size;
uint64_t Const;
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
lsl(GRS(Node), GRS(Op->Header.Args[0].ID()), (unsigned int)Const);
@@ -409,7 +412,6 @@ DEF_OP(Lshl) {
DEF_OP(Lshr) {
auto Op = IROp->C<IR::IROp_Lshr>();
uint8_t OpSize = IROp->Size;
uint64_t Const;
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
lsr(GRS(Node), GRS(Op->Header.Args[0].ID()), (unsigned int)Const);
@@ -460,7 +462,7 @@ DEF_OP(Ror) {
break;
}
default: LogMan::Msg::A("Unhandled ROR size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled ROR size: %d", OpSize);
}
} else {
switch (OpSize) {
@@ -473,7 +475,7 @@ DEF_OP(Ror) {
break;
}
default: LogMan::Msg::A("Unhandled ROR size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled ROR size: %d", OpSize);
}
}
}
@@ -492,7 +494,7 @@ DEF_OP(Extr) {
break;
}
default: LogMan::Msg::A("Unhandled EXTR size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled EXTR size: %d", OpSize);
}
}
@@ -524,14 +526,10 @@ DEF_OP(LDiv) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LDIV);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LDIV));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Result is now in x0
// Fix the stack and any values that were stepped on
@@ -541,7 +539,7 @@ DEF_OP(LDiv) {
mov(GetReg<RA_64>(Node), x0);
break;
}
default: LogMan::Msg::A("Unknown LDIV Size: %d", Size); break;
default: LOGMAN_MSG_A("Unknown LDIV Size: %d", Size); break;
}
}
@@ -571,14 +569,10 @@ DEF_OP(LUDiv) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LUDIV);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LUDIV));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Result is now in x0
// Fix the stack and any values that were stepped on
@@ -588,7 +582,7 @@ DEF_OP(LUDiv) {
mov(GetReg<RA_64>(Node), x0);
break;
}
default: LogMan::Msg::A("Unknown LUDIV Size: %d", Size); break;
default: LOGMAN_MSG_A("Unknown LUDIV Size: %d", Size); break;
}
}
@@ -628,14 +622,10 @@ DEF_OP(LRem) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LREM);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LREM));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Result is now in x0
// Fix the stack and any values that were stepped on
@@ -645,7 +635,7 @@ DEF_OP(LRem) {
mov(GetReg<RA_64>(Node), x0);
break;
}
default: LogMan::Msg::A("Unknown LREM Size: %d", Size); break;
default: LOGMAN_MSG_A("Unknown LREM Size: %d", Size); break;
}
}
@@ -682,14 +672,11 @@ DEF_OP(LURem) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LUREM);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LUREM));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Fix the stack and any values that were stepped on
PopDynamicRegsAndLR();
@@ -698,7 +685,7 @@ DEF_OP(LURem) {
mov(GetReg<RA_64>(Node), x0);
break;
}
default: LogMan::Msg::A("Unknown LUREM Size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown LUREM Size: %d", OpSize); break;
}
}
@@ -712,7 +699,7 @@ DEF_OP(Not) {
case 8:
mvn(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
}
}
@@ -743,7 +730,7 @@ DEF_OP(Popcount) {
// fmov has zero extended, unused bytes are zero
addv(VTMP1.B(), VTMP1.V8B());
break;
default: LogMan::Msg::A("Unsupported Popcount size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Popcount size: %d", OpSize);
}
auto Dst = GetReg<RA_32>(Node);
@@ -792,7 +779,7 @@ DEF_OP(FindMSB) {
clz(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()));
sub(Dst, TMP1, Dst);
break;
default: LogMan::Msg::A("Unknown REV size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
}
}
@@ -813,7 +800,7 @@ DEF_OP(FindTrailingZeros) {
rbit(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
clz(GetReg<RA_64>(Node), GetReg<RA_64>(Node));
break;
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
}
}
@@ -832,7 +819,7 @@ DEF_OP(CountLeadingZeroes) {
case 8:
clz(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
}
}
@@ -850,7 +837,7 @@ DEF_OP(Rev) {
case 8:
rev(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown REV size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
}
}
@@ -872,15 +859,15 @@ DEF_OP(Bfi) {
bfi(TMP1, GetReg<RA_64>(Op->Header.Args[1].ID()), Op->lsb, Op->Width);
mov(GetReg<RA_64>(Node), TMP1);
break;
default: LogMan::Msg::A("Unknown BFI size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown BFI size: %d", OpSize); break;
}
}
DEF_OP(Bfe) {
auto Op = IROp->C<IR::IROp_Bfe>();
uint8_t OpSize = IROp->Size;
LogMan::Throw::A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
LogMan::Throw::A(Op->Width != 0, "Invalid BFE width of 0");
LOGMAN_THROW_A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
LOGMAN_THROW_A(Op->Width != 0, "Invalid BFE width of 0");
auto Dst = GetReg<RA_64>(Node);
ubfx(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()), Op->lsb, Op->Width);
@@ -920,12 +907,12 @@ Condition MapSelectCC(IR::CondClassType Cond) {
case FEXCore::IR::COND_FGT: return Condition::hi;
case FEXCore::IR::COND_FU: return Condition::vs;
case FEXCore::IR::COND_FNU: return Condition::vc;
case FEXCore::IR::COND_VS:;
case FEXCore::IR::COND_VC:;
case FEXCore::IR::COND_VS:
case FEXCore::IR::COND_VC:
case FEXCore::IR::COND_MI:
case FEXCore::IR::COND_PL:
default:
LogMan::Msg::A("Unsupported compare type");
LOGMAN_MSG_A("Unsupported compare type");
return Condition::nv;
}
}
@@ -943,9 +930,9 @@ DEF_OP(Select) {
} else if (IsFPR(Op->Cmp1.ID())) {
fcmp(GRFCMP(Op->Cmp1.ID()), GRFCMP(Op->Cmp2.ID()));
} else {
LogMan::Msg::A("Select: Expected GPR or FPR");
LOGMAN_MSG_A("Select: Expected GPR or FPR");
}
auto cc = MapSelectCC(Op->Cond);
uint64_t const_true, const_false;
@@ -954,7 +941,7 @@ DEF_OP(Select) {
if (is_const_true || is_const_false) {
if (is_const_false != true || is_const_true != true || const_true != 1 || const_false != 0) {
LogMan::Msg::A("Select: Unsupported compare inline parameters");
LOGMAN_MSG_A("Select: Unsupported compare inline parameters");
}
cset(GRS(Node), cc);
} else {
@@ -978,7 +965,7 @@ DEF_OP(VExtractToGPR) {
case 8:
umov(GetReg<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->Idx);
break;
default: LogMan::Msg::A("Unhandled ExtractElementSize: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled ExtractElementSize: %d", OpSize);
}
}
@@ -1022,11 +1009,11 @@ DEF_OP(FCmp) {
fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
}
auto Dst = GetReg<RA_64>(Node);
bool set = false;
if (Op->Flags & (1 << IR::FCMP_FLAG_EQ)) {
LogMan::Throw::A(IR::FCMP_FLAG_EQ == 0, "IR::FCMP_FLAG_EQ must equal 0");
LOGMAN_THROW_A(IR::FCMP_FLAG_EQ == 0, "IR::FCMP_FLAG_EQ must equal 0");
// EQ or unordered
cset(Dst, Condition::eq); // Z = 1
csinc(Dst, Dst, xzr, Condition::vc); // IF !V ? Z : 1
@@ -1057,8 +1044,8 @@ DEF_OP(FCmp) {
#undef DEF_OP
void JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
REGISTER_OP(CONSTANT, Constant);
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
@@ -1,9 +1,15 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(CASPair) {
auto Op = IROp->C<IR::IROp_CASPair>();
uint8_t OpSize = IROp->Size;
@@ -28,7 +34,7 @@ DEF_OP(CASPair) {
mov(Dst.first, TMP3);
mov(Dst.second, TMP4);
break;
default: LogMan::Msg::A("Unsupported: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
}
}
else {
@@ -83,7 +89,7 @@ DEF_OP(CASPair) {
bind(&LoopExpected);
break;
}
default: LogMan::Msg::A("Unsupported: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
}
}
}
@@ -109,7 +115,7 @@ DEF_OP(CAS) {
case 2: casalh(TMP2.W(), Desired.W(), MemOperand(MemSrc)); break;
case 4: casal(TMP2.W(), Desired.W(), MemOperand(MemSrc)); break;
case 8: casal(TMP2.X(), Desired.X(), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unsupported: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
}
mov(GetReg<RA_64>(Node), TMP2);
}
@@ -200,7 +206,7 @@ DEF_OP(CAS) {
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", OpSize);
}
}
}
@@ -216,7 +222,7 @@ DEF_OP(AtomicAdd) {
case 2: staddlh(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
case 4: staddl(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
case 8: staddl(GetReg<RA_64>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -258,7 +264,7 @@ DEF_OP(AtomicAdd) {
cbnz(TMP2, &LoopTop);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -275,7 +281,7 @@ DEF_OP(AtomicSub) {
case 2: staddlh(TMP2.W(), MemOperand(MemSrc)); break;
case 4: staddl(TMP2.W(), MemOperand(MemSrc)); break;
case 8: staddl(TMP2.X(), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -317,7 +323,7 @@ DEF_OP(AtomicSub) {
cbnz(TMP2, &LoopTop);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -334,7 +340,7 @@ DEF_OP(AtomicAnd) {
case 2: stclrlh(TMP2.W(), MemOperand(MemSrc)); break;
case 4: stclrl(TMP2.W(), MemOperand(MemSrc)); break;
case 8: stclrl(TMP2.X(), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -376,7 +382,7 @@ DEF_OP(AtomicAnd) {
cbnz(TMP2, &LoopTop);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -392,7 +398,7 @@ DEF_OP(AtomicOr) {
case 2: stsetlh(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
case 4: stsetl(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
case 8: stsetl(GetReg<RA_64>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -434,7 +440,7 @@ DEF_OP(AtomicOr) {
cbnz(TMP2, &LoopTop);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -450,7 +456,7 @@ DEF_OP(AtomicXor) {
case 2: steorlh(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
case 4: steorl(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
case 8: steorl(GetReg<RA_64>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -492,7 +498,7 @@ DEF_OP(AtomicXor) {
cbnz(TMP2, &LoopTop);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -509,7 +515,7 @@ DEF_OP(AtomicSwap) {
case 2: swplh(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 4: swpl(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 8: swpl(TMP2.X(), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -552,7 +558,7 @@ DEF_OP(AtomicSwap) {
mov(GetReg<RA_64>(Node), TMP2.X());
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -567,7 +573,7 @@ DEF_OP(AtomicFetchAdd) {
case 2: ldaddalh(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 4: ldaddal(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 8: ldaddal(GetReg<RA_64>(Op->Header.Args[1].ID()), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -613,7 +619,7 @@ DEF_OP(AtomicFetchAdd) {
mov(GetReg<RA_64>(Node), TMP2);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -629,7 +635,7 @@ DEF_OP(AtomicFetchSub) {
case 2: ldaddalh(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 4: ldaddal(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 8: ldaddal(TMP2.X(), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -675,7 +681,7 @@ DEF_OP(AtomicFetchSub) {
mov(GetReg<RA_64>(Node), TMP2);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -691,7 +697,7 @@ DEF_OP(AtomicFetchAnd) {
case 2: ldclralh(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 4: ldclral(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 8: ldclral(TMP2.X(), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -737,7 +743,7 @@ DEF_OP(AtomicFetchAnd) {
mov(GetReg<RA_64>(Node), TMP2);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -752,7 +758,7 @@ DEF_OP(AtomicFetchOr) {
case 2: ldsetalh(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 4: ldsetal(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 8: ldsetal(GetReg<RA_64>(Op->Header.Args[1].ID()), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -798,7 +804,7 @@ DEF_OP(AtomicFetchOr) {
mov(GetReg<RA_64>(Node), TMP2);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
@@ -813,7 +819,7 @@ DEF_OP(AtomicFetchXor) {
case 2: ldeoralh(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 4: ldeoral(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
case 8: ldeoral(GetReg<RA_64>(Op->Header.Args[1].ID()), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
else {
@@ -859,14 +865,14 @@ DEF_OP(AtomicFetchXor) {
mov(GetReg<RA_64>(Node), TMP2);
break;
}
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
}
}
}
#undef DEF_OP
void JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
@@ -1,3 +1,9 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include "Interface/Core/InternalThreadState.h"
@@ -8,7 +14,7 @@
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GuestCallDirect) {
LogMan::Msg::D("Unimplemented");
}
@@ -35,7 +41,7 @@ DEF_OP(CallbackReturn) {
// spill back to CTX
SpillStaticRegs();
// First we must reset the stack
ResetStack();
@@ -46,9 +52,9 @@ DEF_OP(CallbackReturn) {
str(w2, MemOperand(x0));
// We need to adjust an additional 8 bytes to get back to the original "misaligned" RSP state
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
add(x2, x2, 8);
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
PopCalleeSavedRegisters();
@@ -67,7 +73,7 @@ DEF_OP(ExitFunction) {
uint64_t NewRIP;
if (IsInlineConstant(Op->NewRIP, &NewRIP) || IsInlineEntrypointOffset(Op->NewRIP, &NewRIP)) {
Literal l_BranchHost{ExitFunctionLinkerAddress};
Literal l_BranchHost{ThreadSharedData.Dispatcher->ExitFunctionLinkerAddress};
Literal l_BranchGuest{NewRIP};
ldr(x0, &l_BranchHost);
@@ -77,9 +83,9 @@ DEF_OP(ExitFunction) {
place(&l_BranchGuest);
} else {
RipReg = GetReg<RA_64>(Op->Header.Args[0].ID());
// L1 Cache
LoadConstant(x0, State->LookupCache->GetL1Pointer());
LoadConstant(x0, ThreadState->LookupCache->GetL1Pointer());
and_(x3, RipReg, LookupCache::L1_ENTRIES_MASK);
add(x0, x0, Operand(x3, Shift::LSL, 4));
@@ -90,8 +96,8 @@ DEF_OP(ExitFunction) {
br(x1);
bind(&FullLookup);
LoadConstant(TMP1, AbsoluteLoopTopAddress);
str(RipReg, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, State.rip)));
LoadConstant(TMP1, ThreadSharedData.Dispatcher->AbsoluteLoopTopAddress);
str(RipReg, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
br(TMP1);
}
}
@@ -131,12 +137,12 @@ Condition MapBranchCC(IR::CondClassType Cond) {
case FEXCore::IR::COND_FGT: return Condition::hi;
case FEXCore::IR::COND_FU: return Condition::vs;
case FEXCore::IR::COND_FNU: return Condition::vc;
case FEXCore::IR::COND_VS:;
case FEXCore::IR::COND_VC:;
case FEXCore::IR::COND_VS:
case FEXCore::IR::COND_VC:
case FEXCore::IR::COND_MI:
case FEXCore::IR::COND_PL:
default:
LogMan::Msg::A("Unsupported compare type");
LOGMAN_MSG_A("Unsupported compare type");
return Condition::nv;
}
}
@@ -163,10 +169,10 @@ DEF_OP(CondJump) {
bool isConst = IsInlineConstant(Op->Cmp2, &Const);
if (isConst && Const == 0 && Op->Cond.Val == FEXCore::IR::COND_EQ) {
LogMan::Throw::A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
LOGMAN_THROW_A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
cbz(GRCMP(Op->Cmp1.ID()), TrueTargetLabel);
} else if (isConst && Const == 0 && Op->Cond.Val == FEXCore::IR::COND_NEQ) {
LogMan::Throw::A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
LOGMAN_THROW_A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
cbnz(GRCMP(Op->Cmp1.ID()), TrueTargetLabel);
} else {
if (IsGPR(Op->Cmp1.ID())) {
@@ -177,12 +183,12 @@ DEF_OP(CondJump) {
} else if (IsFPR(Op->Cmp1.ID())) {
fcmp(GRFCMP(Op->Cmp1.ID()), GRFCMP(Op->Cmp2.ID()));
} else {
LogMan::Msg::A("CondJump: Expected GPR or FPR");
LOGMAN_MSG_A("CondJump: Expected GPR or FPR");
}
b(TrueTargetLabel, MapBranchCC(Op->Cond));
}
if (FalseIter == JumpTargets.end()) {
FalseTargetLabel = &JumpTargets.try_emplace(Op->FalseBlock.ID()).first->second;
}
@@ -217,7 +223,7 @@ DEF_OP(Syscall) {
blr(x3);
add(sp, sp, SPOffset);
// Result is now in x0
// Fix the stack and any values that were stepped on
FillStaticRegs();
@@ -239,16 +245,12 @@ DEF_OP(Thunk) {
mov(x0, GetReg<RA_64>(Op->Header.Args[0].ID()));
#if _M_X86_64
ERROR_AND_DIE("JIT: OP_THUNK not supported with arm simulator")
#else
auto thunkFn = State->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
LoadConstant(x2, (uintptr_t)thunkFn);
blr(x2);
#endif
PopDynamicRegsAndLR();
FillStaticRegs(); // load from ctx after ra64 refill
}
@@ -260,9 +262,9 @@ DEF_OP(ValidateCode) {
int idx = 0;
LoadConstant(GetReg<RA_64>(Node), 0);
LoadConstant(x0, IR->GetHeader()->Entry + Op->Offset);
LoadConstant(x0, Entry + Op->Offset);
LoadConstant(x1, 1);
while (len >= 8)
{
ldr(x2, MemOperand(x0, idx));
@@ -302,17 +304,16 @@ DEF_OP(ValidateCode) {
}
DEF_OP(RemoveCodeEntry) {
auto Op = IROp->C<IR::IROp_RemoveCodeEntry>();
// Arguments are passed as follows:
// X0: Thread
// X1: RIP
PushDynamicRegsAndLR();
mov(x0, STATE);
LoadConstant(x1, IR->GetHeader()->Entry);
LoadConstant(x2, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntry));
LoadConstant(x1, Entry);
LoadConstant(x2, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntryFromJit));
SpillStaticRegs();
blr(x2);
FillStaticRegs();
@@ -323,15 +324,17 @@ DEF_OP(RemoveCodeEntry) {
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
PushDynamicRegsAndLR();
// x0 = CPUID Handler
// x1 = CPUID Function
// x2 = CPUID Leaf
LoadConstant(x0, reinterpret_cast<uint64_t>(&CTX->CPUID));
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[1].ID()));
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t);
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t, uint32_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
@@ -354,8 +357,8 @@ DEF_OP(CPUID) {
}
#undef DEF_OP
void JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(GUESTCALLDIRECT, GuestCallDirect);
REGISTER_OP(GUESTCALLINDIRECT, GuestCallIndirect);
REGISTER_OP(GUESTRETURN, GuestReturn);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VInsGPR) {
auto Op = IROp->C<IR::IROp_VInsGPR>();
mov(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
@@ -25,7 +31,7 @@ DEF_OP(VInsGPR) {
ins(GetDst(Node).V2D(), Op->Index, GetReg<RA_64>(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -46,12 +52,12 @@ DEF_OP(VCastFromGPR) {
case 8:
fmov(GetDst(Node).D(), GetReg<RA_64>(Op->Header.Args[0].ID()).X());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
DEF_OP(Float_FromGPR_U) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Float_FromGPR_S) {
@@ -89,7 +95,7 @@ DEF_OP(Float_FToF) {
fcvt(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown FCVT sizes: 0x%x", Conv);
default: LOGMAN_MSG_A("Unknown FCVT sizes: 0x%x", Conv);
}
}
@@ -102,7 +108,7 @@ DEF_OP(Vector_UToF) {
case 8:
ucvtf(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
@@ -115,7 +121,7 @@ DEF_OP(Vector_SToF) {
case 8:
scvtf(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
@@ -128,7 +134,7 @@ DEF_OP(Vector_FToZU) {
case 8:
fcvtzu(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
@@ -141,7 +147,7 @@ DEF_OP(Vector_FToZS) {
case 8:
fcvtzs(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
@@ -156,7 +162,7 @@ DEF_OP(Vector_FToU) {
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
fcvtzu(GetDst(Node).V2D(), GetDst(Node).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
@@ -171,7 +177,7 @@ DEF_OP(Vector_FToS) {
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
fcvtzs(GetDst(Node).V2D(), GetDst(Node).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
@@ -188,13 +194,13 @@ DEF_OP(Vector_FToF) {
fcvtn(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Conversion Type : 0%04x", Conv); break;
default: LOGMAN_MSG_A("Unknown Conversion Type : 0%04x", Conv); break;
}
}
#undef DEF_OP
void JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(AESImc) {
auto Op = IROp->C<IR::IROp_VAESImc>();
@@ -83,8 +89,8 @@ DEF_OP(AESKeyGenAssist) {
}
#undef DEF_OP
void JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
@@ -1,18 +1,24 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GetHostFlag) {
auto Op = IROp->C<IR::IROp_GetHostFlag>();
ubfx(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), Op->Flag, 1);
}
#undef DEF_OP
void JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
#undef REGISTER_OP
}
File diff suppressed because it is too large. Load diff
+26 -92
View File
@@ -1,9 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#pragma once
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
#include "aarch64/assembler-aarch64.h"
@@ -29,56 +36,18 @@ namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
// All but x29 are caller saved
const std::array<aarch64::Register, 16> SRA64 = {
x4, x5, x6, x7, x8, x9, x10, x11,
x12, x18, x17, x16, x15, x14, x13, x29
};
// All are callee saved
const std::array<aarch64::Register, 9> RA64 = {
x20, x21, x22, x23, x24, x25, x26, x27,
x19
};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA64Pair = {{
{x20, x21},
{x22, x23},
{x24, x25},
{x26, x27},
}};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA32Pair = {{
{w20, w21},
{w22, w23},
{w24, w25},
{w26, w27},
}};
// All are caller saved
const std::array<aarch64::VRegister, 16> SRAFPR = {
v16, v17, v18, v19, v20, v21, v22, v23,
v24, v25, v26, v27, v28, v29, v30, v31
};
// v8..v15 = (lower 64bits) Callee saved
const std::array<aarch64::VRegister, 12> RAFPR = {
/*v0, v1, v2, v3,*/v4, v5, v6, v7, // v0 ~ v3 are used as temps
v8, v9, v10, v11, v12, v13, v14, v15
};
class JITCore final : public CPUBackend, public vixl::aarch64::Assembler {
class Arm64JITCore final : public CPUBackend, public Arm64Emitter {
public:
struct CodeBuffer {
uint8_t *Ptr;
size_t Size;
};
explicit JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
explicit Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
~JITCore() override;
~Arm64JITCore() override;
std::string GetName() override { return "JIT"; }
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *CompileCode(uint64_t Entry, FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
@@ -86,21 +55,22 @@ public:
void ClearCache() override;
bool HandleSIGILL(int Signal, void *info, void *ucontext);
bool HandleSIGBUS(int Signal, void *info, void *ucontext);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
static CodeBuffer AllocateNewCodeBuffer(size_t Size);
CodeBuffer AllocateNewCodeBuffer(size_t Size);
void CopyNecessaryDataForCompileThread(CPUBackend *Original) override;
private:
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
std::unique_ptr<FEXCore::CPU::Dispatcher> Dispatcher;
Label *PendingTargetLabel;
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *State;
FEXCore::Core::InternalThreadState *ThreadState;
FEXCore::IR::IRListView const *IR;
uint64_t Entry;
std::map<IR::OrderedNodeWrapper::NodeOffsetType, aarch64::Label> JumpTargets;
@@ -146,6 +116,8 @@ private:
FEXCore::IR::RegisterClassType GetRegClass(uint32_t Node);
IR::PhysicalRegister GetPhys(uint32_t Node);
bool IsFPR(uint32_t Node);
bool IsGPR(uint32_t Node);
@@ -162,9 +134,6 @@ private:
#if DEBUG
vixl::aarch64::Decoder Decoder;
#endif
vixl::aarch64::CPU CPU;
bool SupportsAtomics{};
bool SupportsRCPC{};
void EmplaceNewCodeBuffer(CodeBuffer Buffer) {
CurrentCodeBuffer = &CodeBuffers.emplace_back(Buffer);
@@ -181,8 +150,6 @@ private:
// For code safety we can't delete code buffers until outside of all signals
std::vector<CodeBuffer> CodeBuffers{};
// This is the codebuffer that our dispatcher lives in
CodeBuffer DispatcherCodeBuffer{};
// This is the current code buffer that we are tracking
CodeBuffer *CurrentCodeBuffer{};
@@ -190,60 +157,24 @@ private:
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 128;
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096 * 2;
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
#if DEBUG
vixl::aarch64::Disassembler Disasm;
#endif
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
void CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread);
void PushCalleeSavedRegisters();
void PopCalleeSavedRegisters();
static uint64_t ExitFunctionLink(JITCore *core, FEXCore::Core::InternalThreadState *Thread, uint64_t *record);
/**
* @name Dispatch Helper functions
* @{ */
uint64_t AbsoluteLoopTopAddressFillSRA{};
uint64_t AbsoluteLoopTopAddress{};
uint64_t ThreadPauseHandlerAddressSpillSRA{};
uint64_t ExitFunctionLinkerAddress{};
uint64_t ThreadPauseHandlerAddress{};
uint64_t ThreadStopHandlerAddressSpillSRA{};
uint64_t ThreadStopHandlerAddress{};
uint64_t PauseReturnInstruction{};
uint32_t SignalHandlerRefCounter{};
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
/** @} */
static uint64_t ExitFunctionLink(Arm64JITCore *core, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record);
struct CompilerSharedData {
uint64_t SignalReturnInstruction{};
uint32_t *SignalHandlerRefCounterPtr{};
FEXCore::CPU::Dispatcher *Dispatcher{};
};
CompilerSharedData ThreadSharedData;
IR::RegisterAllocationPass *RAPass;
IR::RegisterAllocationData *RAData;
uint32_t SpillSlots{};
void SpillStaticRegs();
void FillStaticRegs();
void PushDynamicRegsAndLR();
void PopDynamicRegsAndLR();
void ResetStack();
using OpHandler = void (JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
using OpHandler = void (Arm64JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
std::array<OpHandler, FEXCore::IR::IROps::OP_LAST + 1> OpHandlers {};
void RegisterALUHandlers();
void RegisterAtomicHandlers();
@@ -375,8 +306,11 @@ private:
DEF_OP(StoreMem);
DEF_OP(LoadMemTSO);
DEF_OP(StoreMemTSO);
DEF_OP(ParanoidLoadMemTSO);
DEF_OP(ParanoidStoreMemTSO);
DEF_OP(VLoadMemElement);
DEF_OP(VStoreMemElement);
DEF_OP(CacheLineClear);
///< Misc ops
DEF_OP(EndBlock);
+243 -73
View File
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(LoadContext) {
auto Op = IROp->C<IR::IROp_LoadContext>();
@@ -23,7 +29,7 @@ DEF_OP(LoadContext) {
case 8:
ldr(GetReg<RA_64>(Node), MemOperand(STATE, Op->Offset));
break;
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
}
}
else {
@@ -44,7 +50,7 @@ DEF_OP(LoadContext) {
case 16:
ldr(Dst, MemOperand(STATE, Op->Offset));
break;
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
}
}
}
@@ -66,7 +72,7 @@ DEF_OP(StoreContext) {
case 8:
str(GetReg<RA_64>(Op->Header.Args[0].ID()), MemOperand(STATE, Op->Offset));
break;
default: LogMan::Msg::A("Unhandled StoreContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled StoreContext size: %d", OpSize);
}
}
else {
@@ -87,7 +93,7 @@ DEF_OP(StoreContext) {
case 16:
str(Src, MemOperand(STATE, Op->Offset));
break;
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
}
}
}
@@ -95,61 +101,60 @@ DEF_OP(StoreContext) {
DEF_OP(LoadRegister) {
auto Op = IROp->C<IR::IROp_LoadRegister>();
uint8_t OpSize = IROp->Size;
if (Op->Class == IR::GPRClass) {
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.gregs[0])) / 8;
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.gregs[0])) / 8;
auto regOffs = Op->Offset & 7;
LogMan::Throw::A(regId < SRA64.size(), "out of range regId");
LOGMAN_THROW_A(regId < SRA64.size(), "out of range regId");
auto reg = SRA64[regId];
switch(Op->Header.Size) {
case 1:
LogMan::Throw::A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
ubfx(GetReg<RA_64>(Node), reg, regOffs * 8, 8);
break;
case 2:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
ubfx(GetReg<RA_64>(Node), reg, 0, 16);
break;
case 4:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
if (GetReg<RA_64>(Node).GetCode() != reg.GetCode())
mov(GetReg<RA_32>(Node), reg.W());
break;
case 8:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
if (GetReg<RA_64>(Node).GetCode() != reg.GetCode())
mov(GetReg<RA_64>(Node), reg);
break;
}
} else if (Op->Class == IR::FPRClass) {
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.xmm[0][0])) / 16;
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0])) / 16;
auto regOffs = Op->Offset & 15;
LogMan::Throw::A(regId < SRAFPR.size(), "out of range regId");
LOGMAN_THROW_A(regId < SRAFPR.size(), "out of range regId");
auto guest = SRAFPR[regId];
auto host = GetSrc(Node);
switch(Op->Header.Size) {
case 1:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
mov(host.B(), guest.B());
break;
case 2:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
fmov(host.H(), guest.H());
break;
case 4:
LogMan::Throw::A((regOffs & 3) == 0, "unexpected regOffs");
LOGMAN_THROW_A((regOffs & 3) == 0, "unexpected regOffs");
if (regOffs == 0) {
if (host.GetCode() != guest.GetCode())
fmov(host.S(), guest.S());
@@ -159,7 +164,7 @@ DEF_OP(LoadRegister) {
break;
case 8:
LogMan::Throw::A((regOffs & 7) == 0, "unexpected regOffs");
LOGMAN_THROW_A((regOffs & 7) == 0, "unexpected regOffs");
if (regOffs == 0) {
if (host.GetCode() != guest.GetCode())
mov(host.D(), guest.D());
@@ -169,56 +174,54 @@ DEF_OP(LoadRegister) {
break;
case 16:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
if (host.GetCode() != guest.GetCode())
mov(host.Q(), guest.Q());
break;
}
} else {
LogMan::Throw::A(false, "Unhandled Op->Class %d", Op->Class);
LOGMAN_THROW_A(false, "Unhandled Op->Class %d", Op->Class);
}
}
DEF_OP(StoreRegister) {
auto Op = IROp->C<IR::IROp_StoreRegister>();
uint8_t OpSize = IROp->Size;
if (Op->Class == IR::GPRClass) {
auto regId = Op->Offset / 8 - 1;
auto regOffs = Op->Offset & 7;
LogMan::Throw::A(regId < SRA64.size(), "out of range regId");
LOGMAN_THROW_A(regId < SRA64.size(), "out of range regId");
auto reg = SRA64[regId];
switch(Op->Header.Size) {
case 1:
LogMan::Throw::A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
bfi(reg, GetReg<RA_64>(Op->Value.ID()), regOffs * 8, 8);
break;
case 2:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 16);
break;
case 4:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 32);
break;
case 8:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
if (GetReg<RA_64>(Op->Value.ID()).GetCode() != reg.GetCode())
mov(reg, GetReg<RA_64>(Op->Value.ID()));
break;
}
} else if (Op->Class == IR::FPRClass) {
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.xmm[0][0])) / 16;
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0])) / 16;
auto regOffs = Op->Offset & 15;
LogMan::Throw::A(regId < SRAFPR.size(), "regId out of range");
LOGMAN_THROW_A(regId < SRAFPR.size(), "regId out of range");
auto guest = SRAFPR[regId];
auto host = GetSrc(Op->Value.ID());
@@ -229,28 +232,28 @@ DEF_OP(StoreRegister) {
break;
case 2:
LogMan::Throw::A((regOffs & 1) == 0, "unexpected regOffs");
LOGMAN_THROW_A((regOffs & 1) == 0, "unexpected regOffs");
ins(guest.V8H(), regOffs/2, host.V8H(), 0);
break;
case 4:
LogMan::Throw::A((regOffs & 3) == 0, "unexpected regOffs");
LOGMAN_THROW_A((regOffs & 3) == 0, "unexpected regOffs");
ins(guest.V4S(), regOffs/4, host.V4S(), 0);
break;
case 8:
LogMan::Throw::A((regOffs & 7) == 0, "unexpected regOffs");
LOGMAN_THROW_A((regOffs & 7) == 0, "unexpected regOffs");
ins(guest.V2D(), regOffs / 8, host.V2D(), 0);
break;
case 16:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
if (guest.GetCode() != host.GetCode())
mov(guest.Q(), host.Q());
break;
}
} else {
LogMan::Throw::A(false, "Unhandled Op->Class %d", Op->Class);
LOGMAN_THROW_A(false, "Unhandled Op->Class %d", Op->Class);
}
}
@@ -284,15 +287,15 @@ DEF_OP(LoadContextIndexed) {
ldr(GetReg<RA_64>(Node), MemOperand(TMP1, Op->BaseOffset));
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
}
break;
}
case 16:
LogMan::Msg::A("Invalid Class load of size 16");
LOGMAN_MSG_A("Invalid Class load of size 16");
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
}
}
else {
@@ -329,12 +332,12 @@ DEF_OP(LoadContextIndexed) {
}
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
}
break;
}
default:
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
}
}
}
@@ -370,15 +373,15 @@ DEF_OP(StoreContextIndexed) {
str(value, MemOperand(TMP1, Op->BaseOffset));
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
}
break;
}
case 16:
LogMan::Msg::A("Invalid Class load of size 16");
LOGMAN_MSG_A("Invalid Class load of size 16");
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
}
}
else {
@@ -417,12 +420,12 @@ DEF_OP(StoreContextIndexed) {
}
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
}
break;
}
default:
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
}
}
}
@@ -450,7 +453,7 @@ DEF_OP(SpillRegister) {
str(GetReg<RA_64>(Op->Header.Args[0].ID()), MemOperand(sp, SlotOffset));
break;
}
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
switch (OpSize) {
@@ -466,10 +469,10 @@ DEF_OP(SpillRegister) {
str(GetSrc(Op->Header.Args[0].ID()), MemOperand(sp, SlotOffset));
break;
}
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
}
} else {
LogMan::Msg::A("Unhandled SpillRegister class: %d", Op->Class.Val);
LOGMAN_MSG_A("Unhandled SpillRegister class: %d", Op->Class.Val);
}
}
@@ -496,7 +499,7 @@ DEF_OP(FillRegister) {
ldr(GetReg<RA_64>(Node), MemOperand(sp, SlotOffset));
break;
}
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
switch (OpSize) {
@@ -512,10 +515,10 @@ DEF_OP(FillRegister) {
ldr(GetDst(Node), MemOperand(sp, SlotOffset));
break;
}
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
}
} else {
LogMan::Msg::A("Unhandled FillRegister class: %d", Op->Class.Val);
LOGMAN_MSG_A("Unhandled FillRegister class: %d", Op->Class.Val);
}
}
@@ -530,12 +533,12 @@ DEF_OP(StoreFlag) {
strb(GetReg<RA_64>(Op->Header.Args[0].ID()), MemOperand(STATE, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag));
}
MemOperand JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
MemOperand Arm64JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
if (Offset.IsInvalid()) {
return MemOperand(Base);
} else {
if (OffsetScale != 1 && OffsetScale != AccessSize) {
LogMan::Msg::A("Unhandled GenerateMemOperand OffsetScale: %d", OffsetScale);
LOGMAN_MSG_A("Unhandled GenerateMemOperand OffsetScale: %d", OffsetScale);
}
uint64_t Const;
if (IsInlineConstant(Offset, &Const)) {
@@ -547,10 +550,11 @@ MemOperand JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Bas
case IR::MEM_OFFSET_UXTW.Val: return MemOperand(Base, RegOffset.W(), Extend::UXTW, (int)std::log2(OffsetScale) );
case IR::MEM_OFFSET_SXTW.Val: return MemOperand(Base, RegOffset.W(), Extend::SXTW, (int)std::log2(OffsetScale) );
default: LogMan::Msg::A("Unhandled GenerateMemOperand OffsetType: %d", OffsetType.Val); break;
default: LOGMAN_MSG_A("Unhandled GenerateMemOperand OffsetType: %d", OffsetType.Val); break;
}
}
}
__builtin_unreachable();
}
DEF_OP(LoadMem) {
@@ -574,7 +578,7 @@ DEF_OP(LoadMem) {
case 8:
ldr(Dst, MemSrc);
break;
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
}
else {
@@ -595,7 +599,7 @@ DEF_OP(LoadMem) {
case 16:
ldr(Dst, MemSrc);
break;
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
}
}
@@ -606,7 +610,7 @@ DEF_OP(LoadMemTSO) {
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
if (!Op->Offset.IsInvalid()) {
LogMan::Msg::A("LoadMemTSO: No offset allowed");
LOGMAN_MSG_A("LoadMemTSO: No offset allowed");
}
if (SupportsRCPC && Op->Class == FEXCore::IR::GPRClass) {
@@ -629,7 +633,7 @@ DEF_OP(LoadMemTSO) {
case 8:
ldapr(Dst, MemSrc);
break;
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
nop();
}
@@ -654,7 +658,7 @@ DEF_OP(LoadMemTSO) {
case 8:
ldar(Dst, MemSrc);
break;
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
nop();
}
@@ -675,7 +679,7 @@ DEF_OP(LoadMemTSO) {
case 16:
ldr(Dst, MemSrc);
break;
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
dmb(InnerShareable, BarrierAll);
}
@@ -702,7 +706,7 @@ DEF_OP(StoreMem) {
case 8:
str(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
break;
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
}
else {
@@ -723,7 +727,7 @@ DEF_OP(StoreMem) {
case 16:
str(Src, MemSrc);
break;
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
}
}
@@ -733,7 +737,7 @@ DEF_OP(StoreMemTSO) {
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
if (!Op->Offset.IsInvalid()) {
LogMan::Msg::A("StoreMemTSO: No offset allowed");
LOGMAN_MSG_A("StoreMemTSO: No offset allowed");
}
if (Op->Class == FEXCore::IR::GPRClass) {
@@ -753,7 +757,7 @@ DEF_OP(StoreMemTSO) {
case 8:
stlr(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
break;
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
nop();
}
@@ -777,23 +781,182 @@ DEF_OP(StoreMemTSO) {
case 16:
str(Src, MemSrc);
break;
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
dmb(InnerShareable, BarrierAll);
}
}
DEF_OP(ParanoidLoadMemTSO) {
auto Op = IROp->C<IR::IROp_LoadMemTSO>();
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
if (!Op->Offset.IsInvalid()) {
LOGMAN_MSG_A("LoadMemTSO: No offset allowed");
}
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Size == 1) {
// 8bit load is always aligned to natural alignment
auto Dst = GetReg<RA_64>(Node);
ldarb(Dst, MemSrc);
}
else {
auto Dst = GetReg<RA_64>(Node);
nop();
switch (Op->Size) {
case 2:
ldarh(Dst, MemSrc);
break;
case 4:
ldar(Dst.W(), MemSrc);
break;
case 8:
ldar(Dst, MemSrc);
break;
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
nop();
}
}
else {
auto Dst = GetDst(Node);
switch (Op->Size) {
case 2:
nop();
ldarh(TMP1, MemSrc);
nop();
fmov(Dst, TMP1);
break;
case 4:
nop();
ldar(TMP1.W(), MemSrc);
nop();
fmov(Dst, TMP1);
break;
case 8:
nop();
ldar(TMP1, MemSrc);
nop();
fmov(Dst, TMP1);
break;
case 16:
nop();
ldaxp(TMP1, TMP2, MemSrc);
clrex();
mov(Dst.V2D(), 0, TMP1);
mov(Dst.V2D(), 1, TMP2);
break;
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
}
}
DEF_OP(ParanoidStoreMemTSO) {
auto Op = IROp->C<IR::IROp_StoreMemTSO>();
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
if (!Op->Offset.IsInvalid()) {
LOGMAN_MSG_A("StoreMemTSO: No offset allowed");
}
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Size == 1) {
// 8bit load is always aligned to natural alignment
stlrb(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
}
else {
nop();
switch (Op->Size) {
case 2:
stlrh(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
break;
case 4:
stlr(GetReg<RA_32>(Op->Header.Args[1].ID()), MemSrc);
break;
case 8:
stlr(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
break;
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
nop();
}
}
else {
auto Src = GetSrc(Op->Header.Args[1].ID());
if (Op->Size == 1) {
// 8bit load is always aligned to natural alignment
mov(TMP1, Src.V4S(), 0);
stlrb(TMP1, MemSrc);
}
else {
switch (Op->Size) {
case 2:
mov(TMP1, Src.V4S(), 0);
nop();
stlrh(TMP1, MemSrc);
nop();
break;
case 4:
mov(TMP1, Src.V4S(), 0);
nop();
stlr(TMP1.W(), MemSrc);
nop();
break;
case 8:
mov(TMP1, Src.V2D(), 0);
nop();
stlr(TMP1, MemSrc);
nop();
break;
case 16: {
// Move vector to GPRs
mov(TMP1, Src.V2D(), 0);
mov(TMP2, Src.V2D(), 1);
Label B;
bind(&B);
nop(); // < Overwritten with DMB
// ldaxp must not have both the destination registers be the same
ldaxp(xzr, TMP3, MemSrc); // <- Can hit SIGBUS
nop(); // < Overwritten with DMB
stlxp(TMP3, TMP1, TMP2, MemSrc); // <- Can also hit SIGBUS
cbnz(TMP3, &B); // < Overwritten with DMB
break;
}
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
}
}
}
DEF_OP(VLoadMemElement) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VStoreMemElement) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(CacheLineClear) {
auto Op = IROp->C<IR::IROp_CacheLineClear>();
auto MemReg = GetReg<RA_64>(Op->Header.Args[0].ID());
// Clear dcache only
// icache doesn't matter here since the guest application shouldn't be calling clflush on JIT code.
mov(TMP1, MemReg);
for (size_t i = 0; i < std::max(1U, DCacheLineSize / 64U); ++i) {
dc(DataCacheOp::CVAU, TMP1);
add(TMP1, TMP1, DCacheLineSize);
}
dsb(InnerShareable, BarrierAll);
}
#undef DEF_OP
void JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(LOADCONTEXT, LoadContext);
REGISTER_OP(STORECONTEXT, StoreContext);
REGISTER_OP(LOADREGISTER, LoadRegister);
@@ -806,10 +969,17 @@ void JITCore::RegisterMemoryHandlers() {
REGISTER_OP(STOREFLAG, StoreFlag);
REGISTER_OP(LOADMEM, LoadMem);
REGISTER_OP(STOREMEM, StoreMem);
REGISTER_OP(LOADMEMTSO, LoadMemTSO);
REGISTER_OP(STOREMEMTSO, StoreMemTSO);
if (ParanoidTSO()) {
REGISTER_OP(LOADMEMTSO, ParanoidLoadMemTSO);
REGISTER_OP(STOREMEMTSO, ParanoidStoreMemTSO);
}
else {
REGISTER_OP(LOADMEMTSO, LoadMemTSO);
REGISTER_OP(STOREMEMTSO, StoreMemTSO);
}
REGISTER_OP(VLOADMEMELEMENT, VLoadMemElement);
REGISTER_OP(VSTOREMEMELEMENT, VStoreMemElement);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
#undef REGISTER_OP
}
}
+53 -10
View File
@@ -1,10 +1,23 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
static void PrintValue(uint64_t Value) {
LogMan::Msg::D("Value: 0x%lx", Value);
}
static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) {
LogMan::Msg::D("Value: 0x%016lx'%016lx", ValueUpper, Value);
}
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
@@ -18,7 +31,7 @@ DEF_OP(Fence) {
case IR::Fence_Store.Val:
dmb(FullSystem, BarrierWrites);
break;
default: LogMan::Msg::A("Unknown Fence: %d", Op->Fence); break;
default: LOGMAN_MSG_A("Unknown Fence: %d", Op->Fence); break;
}
}
@@ -29,30 +42,38 @@ DEF_OP(Break) {
case 5: // Guest ud2
hlt(4);
break;
case 1: // Int <imm8>
hlt(4);
break;
case 2: // overflow
hlt(4);
break;
case 3: // int 1
hlt(4);
break;
case 4: { // HLT
// Time to quit
// Set our stack to the starting stack location
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)));
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)));
add(sp, TMP1, 0);
// Now we need to jump to the thread stop handler
LoadConstant(TMP1, ThreadStopHandlerAddressSpillSRA);
LoadConstant(TMP1, ThreadSharedData.Dispatcher->ThreadStopHandlerAddressSpillSRA);
br(TMP1);
break;
}
case 6: { // INT3
ResetStack();
LoadConstant(TMP1, ThreadPauseHandlerAddressSpillSRA);
LoadConstant(TMP1, ThreadSharedData.Dispatcher->ThreadPauseHandlerAddressSpillSRA);
br(TMP1);
break;
}
default: LogMan::Msg::A("Unknown Break reason: %d", Op->Reason);
default: LOGMAN_MSG_A("Unknown Break reason: %d", Op->Reason);
}
}
DEF_OP(GetRoundingMode) {
auto Op = IROp->C<IR::IROp_GetRoundingMode>();
auto Dst = GetReg<RA_64>(Node);
mrs(Dst, FPCR);
lsr(Dst, Dst, 22);
@@ -110,9 +131,31 @@ DEF_OP(SetRoundingMode) {
msr(FPCR, TMP1);
}
DEF_OP(Print) {
auto Op = IROp->C<IR::IROp_Print>();
PushDynamicRegsAndLR();
if (IsGPR(Op->Header.Args[0].ID())) {
mov(x0, GetReg<RA_64>(Op->Header.Args[0].ID()));
LoadConstant(x3, reinterpret_cast<uint64_t>(PrintValue));
}
else {
fmov(x0, GetSrc(Op->Header.Args[0].ID()).V1D());
// Bug in vixl that source vector needs to b V1D rather than V2D?
fmov(x1, GetSrc(Op->Header.Args[0].ID()).V1D(), 1);
LoadConstant(x3, reinterpret_cast<uint64_t>(PrintVectorValue));
}
SpillStaticRegs();
blr(x3);
FillStaticRegs();
PopDynamicRegsAndLR();
}
#undef DEF_OP
void JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
@@ -122,7 +165,7 @@ void JITCore::RegisterMiscHandlers() {
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);
REGISTER_OP(PHIVALUE, NoOp);
REGISTER_OP(PRINT, Unhandled);
REGISTER_OP(PRINT, Print);
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
REGISTER_OP(INVALIDATEFLAGS, NoOp);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(ExtractElementPair) {
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
switch (Op->Header.Size) {
@@ -20,7 +26,7 @@ DEF_OP(ExtractElementPair) {
mov (GetReg<RA_64>(Node), Regs[Op->Element]);
break;
}
default: LogMan::Msg::A("Unknown Size"); break;
default: LOGMAN_MSG_A("Unknown Size"); break;
}
}
@@ -46,7 +52,7 @@ DEF_OP(CreateElementPair) {
RegTmp = x0;
break;
}
default: LogMan::Msg::A("Unknown Size"); break;
default: LOGMAN_MSG_A("Unknown Size"); break;
}
if (Dst.first.GetCode() != RegSecond.GetCode()) {
@@ -68,8 +74,8 @@ DEF_OP(Mov) {
}
#undef DEF_OP
void JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
REGISTER_OP(MOV, Mov);
+162 -121
View File
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VectorZero) {
uint8_t OpSize = IROp->Size;
switch (OpSize) {
@@ -16,7 +22,7 @@ DEF_OP(VectorZero) {
eor(GetDst(Node).V16B(), GetDst(Node).V16B(), GetDst(Node).V16B());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", OpSize); break;
}
}
@@ -26,21 +32,28 @@ DEF_OP(VectorImm) {
uint8_t OpSize = IROp->Size;
uint8_t Elements = OpSize / Op->Header.ElementSize;
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
if (Op->Header.ElementSize == 8) {
// movi with 64bit element size doesn't do what we want here
LoadConstant(TMP1.X(), Op->Immediate);
dup(GetDst(Node).V2D(), TMP1.X());
}
else {
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
}
}
DEF_OP(CreateVector2) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(CreateVector4) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(SplatVector2) {
auto Op = IROp->C<IR::IROp_SplatVector2>();
uint8_t OpSize = IROp->Size;
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
LOGMAN_THROW_A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
uint8_t ElementSize = OpSize / 2;
@@ -51,14 +64,14 @@ DEF_OP(SplatVector2) {
case 8:
dup(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.Size); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.Size); break;
}
}
DEF_OP(SplatVector4) {
auto Op = IROp->C<IR::IROp_SplatVector4>();
uint8_t OpSize = IROp->Size;
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
LOGMAN_THROW_A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
uint8_t ElementSize = OpSize / 4;
@@ -69,7 +82,7 @@ DEF_OP(SplatVector4) {
case 8:
dup(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.Size); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.Size); break;
}
}
@@ -105,7 +118,7 @@ DEF_OP(VMov) {
mov(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", OpSize); break;
}
}
@@ -143,7 +156,7 @@ DEF_OP(VAdd) {
add(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -166,7 +179,7 @@ DEF_OP(VSub) {
sub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -189,7 +202,7 @@ DEF_OP(VUQAdd) {
uqadd(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -212,7 +225,7 @@ DEF_OP(VUQSub) {
uqsub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -235,7 +248,7 @@ DEF_OP(VSQAdd) {
sqadd(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -258,7 +271,7 @@ DEF_OP(VSQSub) {
sqsub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -280,7 +293,7 @@ DEF_OP(VAddP) {
addp(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -301,7 +314,7 @@ DEF_OP(VAddP) {
addp(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -320,7 +333,7 @@ DEF_OP(VAddV) {
case 8:
addp(GetDst(Node).VCast(OpSize * 8, 1), GetSrc(Op->Header.Args[0].ID()).VCast(OpSize * 8, Elements));
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -335,7 +348,7 @@ DEF_OP(VURAvg) {
urhadd(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), GetSrc(Op->Header.Args[1].ID()).V8H());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -350,7 +363,7 @@ DEF_OP(VAbs) {
abs(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -362,7 +375,7 @@ DEF_OP(VAbs) {
case 8:
abs(GetDst(Node).VCast(OpSize * 8, Elements), GetSrc(Op->Header.Args[0].ID()).VCast(OpSize * 8, Elements));
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -381,7 +394,7 @@ DEF_OP(VFAdd) {
fadd(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -395,7 +408,7 @@ DEF_OP(VFAdd) {
fadd(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -411,7 +424,7 @@ DEF_OP(VFAddP) {
faddp(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -429,7 +442,7 @@ DEF_OP(VFSub) {
fsub(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -443,7 +456,7 @@ DEF_OP(VFSub) {
fsub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -462,7 +475,7 @@ DEF_OP(VFMul) {
fmul(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -476,7 +489,7 @@ DEF_OP(VFMul) {
fmul(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -495,7 +508,7 @@ DEF_OP(VFDiv) {
fdiv(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -509,7 +522,7 @@ DEF_OP(VFDiv) {
fdiv(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -521,28 +534,36 @@ DEF_OP(VFMin) {
// Scalar
switch (Op->Header.ElementSize) {
case 4: {
fmin(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
fcmp(GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
fcsel(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S(), Condition::mi);
break;
}
case 8: {
fmin(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
fcsel(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D(), Condition::mi);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
// Vector
switch (Op->Header.ElementSize) {
case 4: {
fmin(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
fcmgt(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
mov(VTMP2.V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V4S(), VTMP2.V4S());
break;
}
case 8: {
fmin(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
fcmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V2D(), VTMP2.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -554,28 +575,36 @@ DEF_OP(VFMax) {
// Scalar
switch (Op->Header.ElementSize) {
case 4: {
fmax(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
fcmp(GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
fcsel(GetDst(Node).S(), GetSrc(Op->Header.Args[1].ID()).S(), GetSrc(Op->Header.Args[0].ID()).S(), Condition::mi);
break;
}
case 8: {
fmax(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
fcsel(GetDst(Node).D(), GetSrc(Op->Header.Args[1].ID()).D(), GetSrc(Op->Header.Args[0].ID()).D(), Condition::mi);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
// Vector
switch (Op->Header.ElementSize) {
case 4: {
fmax(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
fcmgt(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
mov(VTMP2.V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V4S(), VTMP2.V4S());
break;
}
case 8: {
fmax(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
fcmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V2D(), VTMP2.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -596,7 +625,7 @@ DEF_OP(VFRecp) {
fdiv(GetDst(Node).D(), VTMP1.D(), GetSrc(Op->Header.Args[0].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -612,7 +641,7 @@ DEF_OP(VFRecp) {
fdiv(GetDst(Node).V2D(), VTMP1.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -631,7 +660,7 @@ DEF_OP(VFSqrt) {
fsqrt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -645,7 +674,7 @@ DEF_OP(VFSqrt) {
fsqrt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -668,7 +697,7 @@ DEF_OP(VFRSqrt) {
fdiv(GetDst(Node).D(), VTMP1.D(), VTMP2.D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -686,7 +715,7 @@ DEF_OP(VFRSqrt) {
fdiv(GetDst(Node).V2D(), VTMP1.V2D(), VTMP2.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -707,7 +736,7 @@ DEF_OP(VNeg) {
case 8:
neg(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
}
}
@@ -721,7 +750,7 @@ DEF_OP(VFNeg) {
case 8:
fneg(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
}
}
@@ -746,10 +775,13 @@ DEF_OP(VUMin) {
break;
}
case 8: {
umin(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
cmhi(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V2D(), VTMP2.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -769,10 +801,13 @@ DEF_OP(VSMin) {
break;
}
case 8: {
smin(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
cmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V2D(), VTMP2.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -792,10 +827,13 @@ DEF_OP(VUMax) {
break;
}
case 8: {
umax(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
cmhi(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V2D(), VTMP2.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -815,10 +853,13 @@ DEF_OP(VSMax) {
break;
}
case 8: {
smax(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
cmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
mov(GetDst(Node).V2D(), VTMP2.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -839,7 +880,7 @@ DEF_OP(VZip) {
zip1(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -860,7 +901,7 @@ DEF_OP(VZip) {
zip1(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -882,7 +923,7 @@ DEF_OP(VZip2) {
zip2(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -903,7 +944,7 @@ DEF_OP(VZip2) {
zip2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -936,7 +977,7 @@ DEF_OP(VCMPEQ) {
cmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -958,7 +999,7 @@ DEF_OP(VCMPEQ) {
cmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -977,7 +1018,7 @@ DEF_OP(VCMPEQZ) {
cmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -999,7 +1040,7 @@ DEF_OP(VCMPEQZ) {
cmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1018,7 +1059,7 @@ DEF_OP(VCMPGT) {
cmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1040,7 +1081,7 @@ DEF_OP(VCMPGT) {
cmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1059,7 +1100,7 @@ DEF_OP(VCMPGTZ) {
cmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1081,7 +1122,7 @@ DEF_OP(VCMPGTZ) {
cmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1100,7 +1141,7 @@ DEF_OP(VCMPLTZ) {
cmlt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1122,7 +1163,7 @@ DEF_OP(VCMPLTZ) {
cmlt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1141,7 +1182,7 @@ DEF_OP(VFCMPEQ) {
fcmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1159,7 +1200,7 @@ DEF_OP(VFCMPEQ) {
fcmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1178,7 +1219,7 @@ DEF_OP(VFCMPNEQ) {
fcmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
mvn(GetDst(Node).V8B(), GetDst(Node).V8B());
}
@@ -1197,7 +1238,7 @@ DEF_OP(VFCMPNEQ) {
fcmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
mvn(GetDst(Node).V16B(), GetDst(Node).V16B());
}
@@ -1217,7 +1258,7 @@ DEF_OP(VFCMPLT) {
fcmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[1].ID()).D(), GetSrc(Op->Header.Args[0].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1235,7 +1276,7 @@ DEF_OP(VFCMPLT) {
fcmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1254,7 +1295,7 @@ DEF_OP(VFCMPGT) {
fcmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1272,7 +1313,7 @@ DEF_OP(VFCMPGT) {
fcmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1291,7 +1332,7 @@ DEF_OP(VFCMPLE) {
fcmge(GetDst(Node).D(), GetSrc(Op->Header.Args[1].ID()).D(), GetSrc(Op->Header.Args[0].ID()).D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1309,7 +1350,7 @@ DEF_OP(VFCMPLE) {
fcmge(GetDst(Node).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1332,7 +1373,7 @@ DEF_OP(VFCMPORD) {
orr(GetDst(Node).V8B(), VTMP1.V8B(), VTMP2.V8B());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1356,7 +1397,7 @@ DEF_OP(VFCMPORD) {
orr(GetDst(Node).V16B(), VTMP1.V16B(), VTMP2.V16B());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1381,7 +1422,7 @@ DEF_OP(VFCMPUNO) {
mvn(GetDst(Node).V8B(), GetDst(Node).V8B());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -1408,21 +1449,21 @@ DEF_OP(VFCMPUNO) {
mvn(GetDst(Node).V16B(), GetDst(Node).V16B());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
DEF_OP(VUShl) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VUShr) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VSShr) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VUShlS) {
@@ -1449,7 +1490,7 @@ DEF_OP(VUShlS) {
ushl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), VTMP1.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1481,7 +1522,7 @@ DEF_OP(VUShrS) {
ushl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), VTMP1.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1513,13 +1554,13 @@ DEF_OP(VSShrS) {
sshl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), VTMP1.V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
DEF_OP(VInsElement) {
auto Op = IROp->C<IR::IROp_VInsElement>();
auto reg = GetSrc(Op->Header.Args[0].ID());
if (GetDst(Node).GetCode() != reg.GetCode()) {
@@ -1544,7 +1585,7 @@ DEF_OP(VInsElement) {
mov(reg.V2D(), Op->DestIdx, GetSrc(Op->Header.Args[1].ID()).V2D(), Op->SrcIdx);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
if (GetDst(Node).GetCode() != reg.GetCode()) {
@@ -1554,7 +1595,7 @@ DEF_OP(VInsElement) {
DEF_OP(VInsScalarElement) {
auto Op = IROp->C<IR::IROp_VInsScalarElement>();
auto reg = GetSrc(Op->Header.Args[0].ID());
if (GetDst(Node).GetCode() != reg.GetCode()) {
@@ -1579,7 +1620,7 @@ DEF_OP(VInsScalarElement) {
mov(reg.V2D(), Op->DestIdx, GetSrc(Op->Header.Args[1].ID()).V2D(), 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
if (GetDst(Node).GetCode() != reg.GetCode()) {
@@ -1603,7 +1644,7 @@ DEF_OP(VExtractElement) {
case 8:
mov(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->Index);
break;
default: LogMan::Msg::A("Unhandled ExtractElementSize: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled ExtractElementSize: %d", OpSize);
}
}
@@ -1718,7 +1759,7 @@ DEF_OP(VUShrI) {
ushr(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->BitShift);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1743,7 +1784,7 @@ DEF_OP(VSShrI) {
sshr(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), std::min((uint8_t)(Op->Header.ElementSize * 8 - 1), Op->BitShift));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1771,7 +1812,7 @@ DEF_OP(VShlI) {
shl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->BitShift);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -1792,7 +1833,7 @@ DEF_OP(VUShrNI) {
shrn(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->BitShift);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1812,7 +1853,7 @@ DEF_OP(VUShrNI2) {
shrn2(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V2D(), Op->BitShift);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
mov(GetDst(Node), VTMP1);
@@ -1835,7 +1876,7 @@ DEF_OP(VSXTL) {
case 8:
sxtl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1851,7 +1892,7 @@ DEF_OP(VSXTL2) {
case 8:
sxtl2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1867,7 +1908,7 @@ DEF_OP(VUXTL) {
case 8:
uxtl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1883,7 +1924,7 @@ DEF_OP(VUXTL2) {
case 8:
uxtl2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1899,7 +1940,7 @@ DEF_OP(VSQXTN) {
case 4:
sqxtn(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1921,7 +1962,7 @@ DEF_OP(VSQXTN2) {
sqxtn(VTMP2.V2S(), GetSrc(Op->Header.Args[1].ID()).V2D());
ins(VTMP1.V4S(), 1, VTMP2.V4S(), 0);
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
else {
@@ -1935,7 +1976,7 @@ DEF_OP(VSQXTN2) {
case 4:
sqxtn2(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
mov(GetDst(Node), VTMP1);
@@ -1953,7 +1994,7 @@ DEF_OP(VSQXTUN) {
case 4:
sqxtun(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1975,7 +2016,7 @@ DEF_OP(VSQXTUN2) {
sqxtun(VTMP2.V2S(), GetSrc(Op->Header.Args[1].ID()).V2D());
ins(VTMP1.V4S(), 1, VTMP2.V4S(), 0);
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
else {
@@ -1989,7 +2030,7 @@ DEF_OP(VSQXTUN2) {
case 4:
sqxtun2(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
mov(GetDst(Node), VTMP1);
@@ -2014,7 +2055,7 @@ DEF_OP(VMul) {
mul(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -2033,7 +2074,7 @@ DEF_OP(VUMull) {
umull(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
}
}
@@ -2052,7 +2093,7 @@ DEF_OP(VSMull) {
smull(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
}
}
@@ -2071,7 +2112,7 @@ DEF_OP(VUMull2) {
umull2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
}
}
@@ -2090,7 +2131,7 @@ DEF_OP(VSMull2) {
smull2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
}
}
@@ -2107,13 +2148,13 @@ DEF_OP(VTBL1) {
tbl(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
break;
}
default: LogMan::Msg::A("Unknown OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown OpSize: %d", OpSize); break;
}
}
#undef DEF_OP
void JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(CREATEVECTOR2, CreateVector2);
+2 -1
View File
@@ -11,5 +11,6 @@ struct InternalThreadState;
namespace FEXCore::CPU {
class CPUBackend;
FEXCore::CPU::CPUBackend *CreateJITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
FEXCore::CPU::CPUBackend *CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
FEXCore::CPU::CPUBackend *CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
}
+44 -38
View File
@@ -1,8 +1,14 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(TruncElementPair) {
auto Op = IROp->C<IR::IROp_TruncElementPair>();
@@ -14,7 +20,7 @@ DEF_OP(TruncElementPair) {
mov(Dst.second, Src.second);
break;
}
default: LogMan::Msg::A("Unhandled Truncation size: %d", Op->Size); break;
default: LOGMAN_MSG_A("Unhandled Truncation size: %d", Op->Size); break;
}
}
@@ -26,7 +32,7 @@ DEF_OP(Constant) {
DEF_OP(EntrypointOffset) {
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
auto Constant = IR->GetHeader()->Entry + Op->Offset;
auto Constant = Entry + Op->Offset;
mov(GetDst<RA_64>(Node), Constant);
}
@@ -64,7 +70,7 @@ DEF_OP(Add) {
case 8:
add(rax, Const);
break;
default: LogMan::Msg::A("Unhandled Add size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled Add size: %d", OpSize);
break;
}
} else {
@@ -75,7 +81,7 @@ DEF_OP(Add) {
case 8:
add(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled Add size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled Add size: %d", OpSize);
break;
}
}
@@ -97,7 +103,7 @@ DEF_OP(Sub) {
case 8:
sub(rax, Const);
break;
default: LogMan::Msg::A("Unhandled Sub size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled Sub size: %d", OpSize);
break;
}
} else {
@@ -108,7 +114,7 @@ DEF_OP(Sub) {
case 8:
sub(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled Sub size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled Sub size: %d", OpSize);
break;
}
}
@@ -130,7 +136,7 @@ DEF_OP(Neg) {
Src = GetSrc<RA_64>(Op->Header.Args[0].ID());
Dst = GetDst<RA_64>(Node);
break;
default: LogMan::Msg::A("Unhandled Neg size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled Neg size: %d", OpSize);
break;
}
mov(Dst, Src);
@@ -154,7 +160,7 @@ DEF_OP(Mul) {
imul(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
mov(Dst, rax);
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -173,7 +179,7 @@ DEF_OP(UMul) {
mul(GetSrc<RA_64>(Op->Header.Args[1].ID()));
mov(GetDst<RA_64>(Node), rax);
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -212,7 +218,7 @@ DEF_OP(Div) {
mov(GetDst<RA_64>(Node), rax);
break;
}
default: LogMan::Msg::A("Unknown UDIV Size: %d", Size); break;
default: LOGMAN_MSG_A("Unknown UDIV Size: %d", Size); break;
}
}
@@ -255,7 +261,7 @@ DEF_OP(UDiv) {
mov(GetDst<RA_64>(Node), rax);
break;
}
default: LogMan::Msg::A("Unknown UDIV OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown UDIV OpSize: %d", OpSize); break;
}
}
@@ -292,7 +298,7 @@ DEF_OP(Rem) {
mov(GetDst<RA_64>(Node), rdx);
break;
}
default: LogMan::Msg::A("Unknown UDIV Size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown UDIV Size: %d", OpSize); break;
}
}
@@ -335,7 +341,7 @@ DEF_OP(URem) {
mov(GetDst<RA_64>(Node), rdx);
break;
}
default: LogMan::Msg::A("Unknown UDIV OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown UDIV OpSize: %d", OpSize); break;
}
}
@@ -354,7 +360,7 @@ DEF_OP(MulH) {
imul(GetSrc<RA_64>(Op->Header.Args[1].ID()));
mov(GetDst<RA_64>(Node), rdx);
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -373,7 +379,7 @@ DEF_OP(UMulH) {
mul(GetSrc<RA_64>(Op->Header.Args[1].ID()));
mov(GetDst<RA_64>(Node), rdx);
break;
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
}
}
@@ -435,7 +441,7 @@ DEF_OP(Lshl) {
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
shl(GetDst<RA_64>(Node), Const);
break;
default: LogMan::Msg::A("Unknown LSHL Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown LSHL Size: %d\n", OpSize); break;
};
} else {
mov(rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
@@ -450,7 +456,7 @@ DEF_OP(Lshl) {
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
shl(GetDst<RA_64>(Node), cl);
break;
default: LogMan::Msg::A("Unknown LSHL Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown LSHL Size: %d\n", OpSize); break;
};
}
}
@@ -482,7 +488,7 @@ DEF_OP(Lshr) {
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
shr(GetDst<RA_64>(Node), Const);
break;
default: LogMan::Msg::A("Unknown Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown Size: %d\n", OpSize); break;
};
} else {
@@ -506,7 +512,7 @@ DEF_OP(Lshr) {
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
shr(GetDst<RA_64>(Node), cl);
break;
default: LogMan::Msg::A("Unknown Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown Size: %d\n", OpSize); break;
};
}
}
@@ -540,7 +546,7 @@ DEF_OP(Ashr) {
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
sar(GetDst<RA_64>(Node), Const);
break;
default: LogMan::Msg::A("Unknown ASHR Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown ASHR Size: %d\n", OpSize); break;
};
} else {
@@ -565,7 +571,7 @@ DEF_OP(Ashr) {
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
sar(GetDst<RA_64>(Node), cl);
break;
default: LogMan::Msg::A("Unknown ASHR Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown ASHR Size: %d\n", OpSize); break;
};
}
}
@@ -590,7 +596,7 @@ DEF_OP(Ror) {
ror(rax, Const);
break;
}
default: LogMan::Msg::A("Unknown ROR Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown ROR Size: %d\n", OpSize); break;
}
} else {
mov (rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
@@ -606,7 +612,7 @@ DEF_OP(Ror) {
ror(rax, cl);
break;
}
default: LogMan::Msg::A("Unknown ROR Size: %d\n", OpSize); break;
default: LOGMAN_MSG_A("Unknown ROR Size: %d\n", OpSize); break;
}
}
mov(GetDst<RA_64>(Node), rax);
@@ -662,7 +668,7 @@ DEF_OP(LDiv) {
mov(GetDst<RA_64>(Node), rax);
break;
}
default: LogMan::Msg::A("Unknown LDIV OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown LDIV OpSize: %d", OpSize); break;
}
}
@@ -694,7 +700,7 @@ DEF_OP(LUDiv) {
mov(GetDst<RA_64>(Node), rax);
break;
}
default: LogMan::Msg::A("Unknown LUDIV OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown LUDIV OpSize: %d", OpSize); break;
}
}
@@ -726,7 +732,7 @@ DEF_OP(LRem) {
mov(GetDst<RA_64>(Node), rdx);
break;
}
default: LogMan::Msg::A("Unknown LREM OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown LREM OpSize: %d", OpSize); break;
}
}
@@ -758,7 +764,7 @@ DEF_OP(LURem) {
mov(GetDst<RA_64>(Node), rdx);
break;
}
default: LogMan::Msg::A("Unknown LUDIV OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown LUDIV OpSize: %d", OpSize); break;
}
}
@@ -823,7 +829,7 @@ DEF_OP(FindMSB) {
case 8:
bsr(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown OpSize: %d", OpSize);
default: LOGMAN_MSG_A("Unknown OpSize: %d", OpSize);
}
}
@@ -847,7 +853,7 @@ DEF_OP(FindTrailingZeros) {
mov(rax, 0x40);
cmovz(GetDst<RA_64>(Node), rax);
break;
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
}
}
@@ -870,7 +876,7 @@ DEF_OP(CountLeadingZeroes) {
lzcnt(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
}
}
else {
@@ -909,7 +915,7 @@ DEF_OP(CountLeadingZeroes) {
mov(GetDst<RA_64>(Node), rax);
break;
}
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
}
}
}
@@ -931,7 +937,7 @@ DEF_OP(Rev) {
mov (GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
bswap(GetDst<RA_64>(Node).cvt64());
break;
default: LogMan::Msg::A("Unknown REV size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
}
}
@@ -966,7 +972,7 @@ DEF_OP(Bfe) {
auto Op = IROp->C<IR::IROp_Bfe>();
uint8_t OpSize = IROp->Size;
LogMan::Throw::A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
LOGMAN_THROW_A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
auto Dst = GetDst<RA_64>(Node);
@@ -1067,7 +1073,7 @@ DEF_OP(Select) {
if (is_const_true || is_const_false) {
if (is_const_false != true || is_const_true != true || const_true != 1 || const_false != 0) {
LogMan::Msg::A("Select: Unsupported compare inline parameters");
LOGMAN_MSG_A("Select: Unsupported compare inline parameters");
}
(this->*SetCC)(al);
movzx(Dst, al);
@@ -1098,7 +1104,7 @@ DEF_OP(VExtractToGPR) {
pextrq(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()), Op->Idx);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1168,8 +1174,8 @@ DEF_OP(FCmp) {
#undef DEF_OP
void JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
REGISTER_OP(CONSTANT, Constant);
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
@@ -1,8 +1,14 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(CASPair) {
auto Op = IROp->C<IR::IROp_CAS>();
uint8_t OpSize = IROp->Size;
@@ -49,7 +55,7 @@ DEF_OP(CASPair) {
mov(Dst.second, rdx);
break;
}
default: LogMan::Msg::A("Unsupported: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
}
}
@@ -68,7 +74,6 @@ DEF_OP(CAS) {
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Header.Args[2].ID());
mov(rdx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
mov(rax, GetSrc<RA_64>(Op->Header.Args[0].ID()));
// RCX now contains pointer
@@ -76,31 +81,31 @@ DEF_OP(CAS) {
// RDX contains our desired
lock();
switch (OpSize) {
case 1: {
cmpxchg(byte [MemReg], dl);
movzx(rax, al);
cmpxchg(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
movzx(GetDst<RA_64>(Node), al);
break;
}
case 2: {
cmpxchg(word [MemReg], dx);
movzx(rax, ax);
cmpxchg(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
movzx(GetDst<RA_64>(Node), ax);
break;
}
case 4: {
cmpxchg(dword [MemReg], edx);
cmpxchg(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
// RAX now contains the result
mov (GetDst<RA_64>(Node), eax);
break;
}
case 8: {
cmpxchg(qword [MemReg], rdx);
cmpxchg(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
// RAX now contains the result
mov (GetDst<RA_64>(Node), rax);
break;
}
default: LogMan::Msg::A("Unsupported: %d", OpSize);
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
}
// RAX now contains the result
mov (GetDst<RA_64>(Node), rax);
}
DEF_OP(AtomicAdd) {
@@ -122,7 +127,7 @@ DEF_OP(AtomicAdd) {
case 8:
add(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
}
}
@@ -144,7 +149,7 @@ DEF_OP(AtomicSub) {
case 8:
sub(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
}
}
@@ -166,7 +171,7 @@ DEF_OP(AtomicAnd) {
case 8:
and_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
}
}
@@ -188,7 +193,7 @@ DEF_OP(AtomicOr) {
case 8:
or_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
}
}
@@ -210,7 +215,7 @@ DEF_OP(AtomicXor) {
case 8:
xor_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
}
}
@@ -222,17 +227,17 @@ DEF_OP(AtomicSwap) {
switch (Op->Size) {
case 1:
mov(GetDst<RA_8>(Node), GetSrc<RA_8>(Op->Header.Args[1].ID()));
movzx(GetDst<RA_64>(Node), GetSrc<RA_8>(Op->Header.Args[1].ID()));
lock();
xchg(byte [MemReg], GetDst<RA_8>(Node));
break;
case 2:
mov(GetDst<RA_16>(Node), GetSrc<RA_16>(Op->Header.Args[1].ID()));
movzx(GetDst<RA_64>(Node), GetSrc<RA_16>(Op->Header.Args[1].ID()));
lock();
xchg(word [MemReg], GetDst<RA_16>(Node));
break;
case 4:
mov(GetDst<RA_32>(Node), GetSrc<RA_32>(Op->Header.Args[1].ID()));
mov(GetDst<RA_64>(Node), GetSrc<RA_32>(Op->Header.Args[1].ID()));
lock();
xchg(dword [MemReg], GetDst<RA_32>(Node));
break;
@@ -241,7 +246,7 @@ DEF_OP(AtomicSwap) {
lock();
xchg(qword [MemReg], GetDst<RA_64>(Node));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
}
}
@@ -251,13 +256,13 @@ DEF_OP(AtomicFetchAdd) {
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Header.Args[0].ID());
switch (Op->Size) {
case 1:
mov(cl, GetSrc<RA_8>(Op->Header.Args[1].ID()));
movzx(rcx, GetSrc<RA_8>(Op->Header.Args[1].ID()));
lock();
xadd(byte [MemReg], cl);
movzx(GetDst<RA_32>(Node), cl);
break;
case 2:
mov(cx, GetSrc<RA_16>(Op->Header.Args[1].ID()));
movzx(rcx, GetSrc<RA_16>(Op->Header.Args[1].ID()));
lock();
xadd(word [MemReg], cx);
movzx(GetDst<RA_32>(Node), cx);
@@ -266,7 +271,7 @@ DEF_OP(AtomicFetchAdd) {
mov(ecx, GetSrc<RA_32>(Op->Header.Args[1].ID()));
lock();
xadd(dword [MemReg], ecx);
mov(GetDst<RA_32>(Node), ecx);
mov(GetDst<RA_64>(Node), ecx);
break;
case 8:
mov(rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
@@ -274,7 +279,7 @@ DEF_OP(AtomicFetchAdd) {
xadd(qword [MemReg], rcx);
mov(GetDst<RA_64>(Node), rcx);
break;
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
}
}
@@ -311,7 +316,7 @@ DEF_OP(AtomicFetchSub) {
xadd(qword [MemReg], rcx);
mov(GetDst<RA_64>(Node), rcx);
break;
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
}
}
@@ -389,7 +394,7 @@ DEF_OP(AtomicFetchAnd) {
mov(GetDst<RA_64>(Node), TMP3.cvt64());
break;
}
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
}
}
@@ -466,7 +471,7 @@ DEF_OP(AtomicFetchOr) {
mov(GetDst<RA_64>(Node), TMP3.cvt64());
break;
}
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
}
}
@@ -543,13 +548,13 @@ DEF_OP(AtomicFetchXor) {
mov(GetDst<RA_64>(Node), TMP3.cvt64());
break;
}
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
}
}
#undef DEF_OP
void JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
@@ -1,3 +1,9 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -6,7 +12,7 @@
#include <Interface/HLE/Thunks/Thunks.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GuestCallDirect) {
LogMan::Msg::D("Unimplemented");
}
@@ -40,7 +46,7 @@ DEF_OP(CallbackReturn) {
sub(dword [rax], 1);
// We need to adjust an additional 8 bytes to get back to the original "misaligned" RSP state
add(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 8);
add(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])], 8);
// Now jump back to the thunk
// XXX: XMM?
@@ -75,12 +81,12 @@ DEF_OP(ExitFunction) {
jmp(qword[rax]);
L(l_BranchHost);
dq(ExitFunctionLinkerAddress);
dq(ThreadSharedData.Dispatcher->ExitFunctionLinkerAddress);
L(l_BranchGuest);
dq(NewRIP);
} else {
Xbyak::Reg RipReg = GetSrc<RA_64>(Op->NewRIP.ID());
// L1 Cache
mov(rcx, ThreadState->LookupCache->GetL1Pointer());
mov(rax, RipReg);
@@ -89,14 +95,14 @@ DEF_OP(ExitFunction) {
shl(rax, 4);
Xbyak::RegExp LookupBase = rcx + rax;
cmp(qword[LookupBase + 8], RipReg);
jne(FullLookup);
jmp(qword[LookupBase + 0]);
L(FullLookup);
mov(rax, AbsoluteLoopTopAddress);
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], RipReg);
mov(rax, ThreadSharedData.Dispatcher->AbsoluteLoopTopAddress);
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.rip)], RipReg);
jmp(rax);
}
@@ -227,7 +233,7 @@ DEF_OP(Thunk) {
sub(rsp, 8); // Align
mov(rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
mov(rax, reinterpret_cast<uintptr_t>(thunkFn));
@@ -247,7 +253,7 @@ DEF_OP(ValidateCode) {
int idx = 0;
xor_(GetDst<RA_64>(Node), GetDst<RA_64>(Node));
mov(rax, IR->GetHeader()->Entry + Op->Offset);
mov(rax, Entry + Op->Offset);
mov(rbx, 1);
while (len >= 4) {
cmp(dword[rax + idx], *(uint32_t*)(OldCode + idx));
@@ -271,8 +277,6 @@ DEF_OP(ValidateCode) {
}
DEF_OP(RemoveCodeEntry) {
auto Op = IROp->C<IR::IROp_RemoveCodeEntry>();
auto NumPush = RA64.size();
for (auto &Reg : RA64)
@@ -282,11 +286,11 @@ DEF_OP(RemoveCodeEntry) {
sub(rsp, 8); // Align
mov(rdi, STATE);
mov(rax, IR->GetHeader()->Entry); // imm64 move
mov(rax, Entry); // imm64 move
mov(rsi, rax);
mov(rax, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntry));
mov(rax, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntryFromJit));
call(rax);
if (NumPush & 1)
@@ -299,7 +303,7 @@ DEF_OP(RemoveCodeEntry) {
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t Function);
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t Function, uint32_t Leaf);
union {
ClassPtrType ClassPtr;
uint64_t Raw;
@@ -316,6 +320,7 @@ DEF_OP(CPUID) {
// Result: RAX, RDX. 4xi32
mov (rsi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
mov (rdx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
mov (rdi, reinterpret_cast<uint64_t>(&CTX->CPUID));
auto NumPush = RA64.size();
@@ -341,8 +346,8 @@ DEF_OP(CPUID) {
}
#undef DEF_OP
void JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(GUESTCALLDIRECT, GuestCallDirect);
REGISTER_OP(GUESTCALLINDIRECT, GuestCallIndirect);
REGISTER_OP(GUESTRETURN, GuestReturn);
@@ -1,9 +1,15 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VInsGPR) {
auto Op = IROp->C<IR::IROp_VInsGPR>();
movapd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
@@ -25,7 +31,7 @@ DEF_OP(VInsGPR) {
pinsrq(GetDst(Node), GetSrc<RA_64>(Op->Header.Args[1].ID()), Op->Index);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -46,12 +52,12 @@ DEF_OP(VCastFromGPR) {
case 8:
vmovq(GetDst(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()).cvt64());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
DEF_OP(Float_FromGPR_U) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Float_FromGPR_S) {
@@ -89,12 +95,12 @@ DEF_OP(Float_FToF) {
cvtsd2ss(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown FCVT sizes: 0x%x", Conv);
default: LOGMAN_MSG_A("Unknown FCVT sizes: 0x%x", Conv);
}
}
DEF_OP(Vector_UToF) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Vector_SToF) {
@@ -115,12 +121,12 @@ DEF_OP(Vector_SToF) {
cvtsi2sd(xmm15, rax);
movlhps(GetDst(Node), xmm15);
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
DEF_OP(Vector_FToZU) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Vector_FToZS) {
@@ -132,12 +138,12 @@ DEF_OP(Vector_FToZS) {
case 8:
cvttpd2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
DEF_OP(Vector_FToU) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Vector_FToS) {
@@ -149,7 +155,7 @@ DEF_OP(Vector_FToS) {
case 8:
cvtpd2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
@@ -166,13 +172,13 @@ DEF_OP(Vector_FToF) {
cvtpd2ps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Conversion Type : 0%04x", Conv); break;
default: LOGMAN_MSG_A("Unknown Conversion Type : 0%04x", Conv); break;
}
}
#undef DEF_OP
void JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
@@ -1,8 +1,14 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(AESImc) {
auto Op = IROp->C<IR::IROp_VAESImc>();
@@ -35,8 +41,8 @@ DEF_OP(AESKeyGenAssist) {
}
#undef DEF_OP
void JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
@@ -1,9 +1,15 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GetHostFlag) {
auto Op = IROp->C<IR::IROp_GetHostFlag>();
@@ -14,8 +20,8 @@ DEF_OP(GetHostFlag) {
}
#undef DEF_OP
void JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
#undef REGISTER_OP
}
+85 -620
View File
@@ -1,5 +1,13 @@
/*
$info$
tags: backend|x86-64
desc: Main glue logic of the x86-64 splatter backend
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/Core/InternalThreadState.h"
@@ -7,6 +15,7 @@
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Core/UContext.h>
#include <FEXCore/Utils/Allocator.h>
#include <cmath>
#include <signal.h>
@@ -22,324 +31,29 @@ CodeBuffer AllocateNewCodeBuffer(size_t Size) {
CodeBuffer Buffer;
Buffer.Size = Size;
Buffer.Ptr = static_cast<uint8_t*>(
mmap(nullptr,
FEXCore::Allocator::mmap(nullptr,
Buffer.Size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0));
LogMan::Throw::A(Buffer.Ptr != reinterpret_cast<uint8_t*>(~0ULL), "Couldn't allocate code buffer");
LOGMAN_THROW_A(Buffer.Ptr != reinterpret_cast<uint8_t*>(~0ULL), "Couldn't allocate code buffer");
return Buffer;
}
void FreeCodeBuffer(CodeBuffer Buffer) {
munmap(Buffer.Ptr, Buffer.Size);
FEXCore::Allocator::munmap(Buffer.Ptr, Buffer.Size);
}
}
namespace FEXCore::CPU {
struct ContextBackup {
uint64_t StoredCookie;
// Host State
// RIP and RSP is stored in GPRs here
uint64_t GPRs[NGREG];
_libc_fpstate FPRState;
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
};
void JITCore::StoreThreadState(int Signal, void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
uint64_t OldSP = _mcontext->gregs[REG_RSP];
uintptr_t NewSP = OldSP;
size_t StackOffset = sizeof(ContextBackup);
// We need to back up behind the host's red zone
// We do this on the guest side as well
NewSP -= 128;
NewSP -= StackOffset;
NewSP = AlignDown(NewSP, 16);
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
Context->StoredCookie = 0x4142434445464748ULL;
// Copy the GPRs
memcpy(&Context->GPRs[0], &_mcontext->gregs[0], NGREG * sizeof(_mcontext->gregs[0]));
// Copy the FPRState
memcpy(&Context->FPRState, _mcontext->fpregs, sizeof(_libc_fpstate));
// XXX: Save 256bit and 512bit AVX register state
// Retain the action pointer so we can see it when we return
Context->Signal = Signal;
// Save guest state
// We can't guarantee if registers are in context or host GPRs
// So we need to save everything
memcpy(&Context->GuestState, &ThreadState->State, sizeof(FEXCore::Core::CPUState));
// Set the new SP
_mcontext->gregs[REG_RSP] = NewSP;
SignalFrames.push(NewSP);
}
void JITCore::RestoreThreadState(void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
uint64_t OldSP = SignalFrames.top();
SignalFrames.pop();
uintptr_t NewSP = OldSP;
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
if (Context->StoredCookie != 0x4142434445464748ULL) {
LogMan::Msg::D("COOKIE WAS NOT CORRECT!\n");
exit(-1);
}
// First thing, reset the guest state
memcpy(&ThreadState->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
// Copy the GPRs
memcpy(&_mcontext->gregs[0], &Context->GPRs[0], NGREG * sizeof(_mcontext->gregs[0]));
// Copy the FPRState
memcpy(_mcontext->fpregs, &Context->FPRState, sizeof(_libc_fpstate));
// Restore the previous signal state
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
}
bool JITCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->gregs[REG_RIP] = AbsoluteLoopTopAddress;
// Set our state register to point to our guest thread data
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(ThreadState);
// Ref count our faults
// We use this to track if it is safe to clear cache
++SignalHandlerRefCounter;
uint64_t OldGuestSP = ThreadState->State.State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
if (!(GuestStack->ss_flags & SS_DISABLE)) {
// If our guest is already inside of the alternative stack
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
if (OldGuestSP >= AltStackBase &&
OldGuestSP <= AltStackEnd) {
// We are already in the alt stack, the rest of the code will handle adjusting this
}
else {
NewGuestSP = AltStackEnd;
}
}
// Back up past the redzone, which is 128bytes
// Don't need this offset if we aren't going to be putting siginfo in to it
NewGuestSP -= 128;
if (GuestAction->sa_flags & SA_SIGINFO) {
// Setup ucontext a bit
if (CTX->Config.Is64BitMode) {
NewGuestSP -= sizeof(FEXCore::x86_64::ucontext_t);
uint64_t UContextLocation = NewGuestSP;
FEXCore::x86_64::ucontext_t *guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
// We have extended float information
guest_uctx->uc_flags |= FEXCore::x86_64::UC_FP_XSTATE;
// Pointer to where the fpreg memory is
guest_uctx->uc_mcontext.fpregs = &guest_uctx->__fpregs_mem;
#define COPY_REG(x) \
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x] = ThreadState->State.State.gregs[X86State::REG_##x];
COPY_REG(R8);
COPY_REG(R9);
COPY_REG(R10);
COPY_REG(R11);
COPY_REG(R12);
COPY_REG(R13);
COPY_REG(R14);
COPY_REG(R15);
COPY_REG(RDI);
COPY_REG(RSI);
COPY_REG(RBP);
COPY_REG(RBX);
COPY_REG(RDX);
COPY_REG(RAX);
COPY_REG(RCX);
COPY_REG(RSP);
#undef COPY_REG
// Copy float registers
memcpy(guest_uctx->__fpregs_mem._st, ThreadState->State.State.mm, sizeof(ThreadState->State.State.mm));
memcpy(guest_uctx->__fpregs_mem._xmm, ThreadState->State.State.xmm, sizeof(ThreadState->State.State.xmm));
// FCW store default
guest_uctx->__fpregs_mem.fcw = ThreadState->State.State.FCW;
// Reconstruct FSW
guest_uctx->__fpregs_mem.fsw =
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) |
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) |
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14);
// Copy over signal stack information
guest_uctx->uc_stack.ss_flags = GuestStack->ss_flags;
guest_uctx->uc_stack.ss_sp = GuestStack->ss_sp;
guest_uctx->uc_stack.ss_size = GuestStack->ss_size;
// XXX: siginfo_t(RSI)
ThreadState->State.State.gregs[X86State::REG_RSI] = 0x4142434445460000;
ThreadState->State.State.gregs[X86State::REG_RDX] = UContextLocation;
}
else {
// XXX: 32bit Support
NewGuestSP -= sizeof(FEXCore::x86::ucontext_t);
uint64_t UContextLocation = 0; // NewGuestSP;
NewGuestSP -= sizeof(FEXCore::x86::siginfo_t);
uint64_t SigInfoLocation = 0; // NewGuestSP;
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = UContextLocation;
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = SigInfoLocation;
}
ThreadState->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
}
else {
ThreadState->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
}
if (CTX->Config.Is64BitMode) {
ThreadState->State.State.gregs[X86State::REG_RDI] = Signal;
// Set up the new SP for stack handling
NewGuestSP -= 8;
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
ThreadState->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
}
else {
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
LogMan::Throw::A(CTX->X86CodeGen.SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
ThreadState->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
}
return true;
}
void JITCore::CopyNecessaryDataForCompileThread(CPUBackend *Original) {
JITCore *Core = reinterpret_cast<JITCore*>(Original);
void X86JITCore::CopyNecessaryDataForCompileThread(CPUBackend *Original) {
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Original);
ThreadSharedData = Core->ThreadSharedData;
}
bool JITCore::HandleSIGILL(int Signal, void *info, void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
if (_mcontext->gregs[REG_RIP] == ThreadSharedData.SignalHandlerReturnAddress) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
return true;
}
if (_mcontext->gregs[REG_RIP] == PauseReturnInstruction) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
return true;
}
return false;
}
bool JITCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
FEXCore::Core::SignalEvent SignalReason = ThreadState->SignalReason.load();
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->gregs[REG_RIP] = ThreadPauseHandlerAddress;
// Set our state register to point to our guest thread data
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(ThreadState);
// Ref count our faults
// We use this to track if it is safe to clear cache
++SignalHandlerRefCounter;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Our thread is stopping
// We don't care about anything at this point
// Set the stack to our starting location when we entered the JIT and get out safely
_mcontext->gregs[REG_RSP] = ThreadState->State.ReturningStackLocation;
// Our ref counting doesn't matter anymore
SignalHandlerRefCounter = 0;
// Set the new PC
_mcontext->gregs[REG_RIP] = ThreadStopHandlerAddress;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
return false;
}
void JITCore::PushRegs() {
void X86JITCore::PushRegs() {
for (auto &Xmm : RAXMM_x) {
sub(rsp, 16);
movaps(ptr[rsp], Xmm);
@@ -353,7 +67,7 @@ void JITCore::PushRegs() {
sub(rsp, 8); // Align
}
void JITCore::PopRegs() {
void X86JITCore::PopRegs() {
auto NumPush = RA64.size();
if (NumPush & 1)
@@ -367,11 +81,11 @@ void JITCore::PopRegs() {
}
}
void JITCore::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
void X86JITCore::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
FallbackInfo Info;
if (!InterpreterOps::GetFallbackHandler(IROp, &Info)) {
auto Name = FEXCore::IR::GetName(IROp->Op);
LogMan::Msg::A("Unhandled IR Op: %s", std::string(Name).c_str());
LOGMAN_MSG_A("Unhandled IR Op: %s", std::string(Name).c_str());
} else {
switch(Info.ABI) {
case FABI_VOID_U16: {
@@ -569,22 +283,20 @@ void JITCore::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
case FABI_UNKNOWN:
default:
auto Name = FEXCore::IR::GetName(IROp->Op);
LogMan::Msg::A("Unhandled IR Fallback abi: %s %d", std::string(Name).c_str(), Info.ABI);
LOGMAN_MSG_A("Unhandled IR Fallback abi: %s %d", std::string(Name).c_str(), Info.ABI);
}
}
}
void JITCore::Op_NoOp(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
void X86JITCore::Op_NoOp(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
}
JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread)
X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread)
: CodeGenerator(Buffer.Size, Buffer.Ptr, nullptr)
, CTX {ctx}
, ThreadState {Thread}
, InitialCodeBuffer {Buffer}
{
ThreadSharedData.SignalHandlerRefCounterPtr = &SignalHandlerRefCounter;
CurrentCodeBuffer = &InitialCodeBuffer;
RAPass = Thread->PassManager->GetRAPass();
@@ -600,7 +312,7 @@ JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadSt
}
for (uint32_t i = 0; i < FEXCore::IR::IROps::OP_LAST + 1; ++i) {
OpHandlers[i] = &JITCore::Op_Unhandled;
OpHandlers[i] = &X86JITCore::Op_Unhandled;
}
RegisterALUHandlers();
@@ -615,22 +327,32 @@ JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadSt
RegisterEncryptionHandlers();
if (!CompileThread) {
CreateCustomDispatch(Thread);
DispatcherConfig config;
config.ExitFunctionLink = reinterpret_cast<uintptr_t>(&ExitFunctionLink);
config.ExitFunctionLinkThis = reinterpret_cast<uintptr_t>(this);
Dispatcher = std::make_unique<X86Dispatcher>(CTX, ThreadState, config);
DispatchPtr = Dispatcher->DispatchPtr;
CallbackPtr = Dispatcher->CallbackPtr;
ThreadSharedData.SignalHandlerRefCounterPtr = &Dispatcher->SignalHandlerRefCounter;
ThreadSharedData.SignalHandlerReturnAddress = Dispatcher->SignalHandlerReturnAddress;
ThreadSharedData.Dispatcher = Dispatcher.get();
// This will register the host signal handler per thread, which is fine
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
JITCore *Core = reinterpret_cast<JITCore*>(Thread->CPUBackend.get());
return Core->HandleSIGILL(Signal, info, ucontext);
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Thread->CPUBackend.get());
return Core->Dispatcher->HandleSIGILL(Signal, info, ucontext);
});
CTX->SignalDelegation->RegisterHostSignalHandler(SignalDelegator::SIGNAL_FOR_PAUSE, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
JITCore *Core = reinterpret_cast<JITCore*>(Thread->CPUBackend.get());
return Core->HandleSignalPause(Signal, info, ucontext);
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Thread->CPUBackend.get());
return Core->Dispatcher->HandleSignalPause(Signal, info, ucontext);
});
auto GuestSignalHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) -> bool {
JITCore *Core = reinterpret_cast<JITCore*>(Thread->CPUBackend.get());
return Core->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Thread->CPUBackend.get());
return Core->Dispatcher->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
};
for (uint32_t Signal = 0; Signal < SignalDelegator::MAX_SIGNALS; ++Signal) {
@@ -639,20 +361,17 @@ JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadSt
}
}
JITCore::~JITCore() {
X86JITCore::~X86JITCore() {
for (auto CodeBuffer : CodeBuffers) {
FreeCodeBuffer(CodeBuffer);
}
CodeBuffers.clear();
if (DispatcherCodeBuffer.Ptr) {
// Dispatcher may not exist if this is a compile thread
FreeCodeBuffer(DispatcherCodeBuffer);
}
FreeCodeBuffer(InitialCodeBuffer);
}
void JITCore::ClearCache() {
void X86JITCore::ClearCache() {
if (*ThreadSharedData.SignalHandlerRefCounterPtr == 0) {
if (!CodeBuffers.empty()) {
// If we have more than one code buffer we are tracking then walk them and delete
@@ -686,30 +405,30 @@ void JITCore::ClearCache() {
// We have signal handlers that have generated code
// This means that we can not safely clear the code at this point in time
// Allocate some new code buffers that we can switch over to instead
auto NewCodeBuffer = AllocateNewCodeBuffer(JITCore::INITIAL_CODE_SIZE);
auto NewCodeBuffer = AllocateNewCodeBuffer(X86JITCore::INITIAL_CODE_SIZE);
EmplaceNewCodeBuffer(NewCodeBuffer);
setNewBuffer(NewCodeBuffer.Ptr, NewCodeBuffer.Size);
}
}
IR::PhysicalRegister JITCore::GetPhys(uint32_t Node) {
IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) {
auto PhyReg = RAData->GetNodeRegister(Node);
LogMan::Throw::A(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
LOGMAN_THROW_A(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
return PhyReg;
}
bool JITCore::IsFPR(uint32_t Node) {
bool X86JITCore::IsFPR(uint32_t Node) {
return RAData->GetNodeRegister(Node).Class == IR::FPRClass.Val;
}
bool JITCore::IsGPR(uint32_t Node) {
bool X86JITCore::IsGPR(uint32_t Node) {
return RAData->GetNodeRegister(Node).Class == IR::GPRClass.Val;
}
template<uint8_t RAType>
Xbyak::Reg JITCore::GetSrc(uint32_t Node) {
Xbyak::Reg X86JITCore::GetSrc(uint32_t Node) {
// rax, rcx, rdx, rsi, r8, r9,
// r10
// Callee Saved
@@ -728,24 +447,24 @@ Xbyak::Reg JITCore::GetSrc(uint32_t Node) {
}
template
Xbyak::Reg JITCore::GetSrc<JITCore::RA_64>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_64>(uint32_t Node);
template
Xbyak::Reg JITCore::GetSrc<JITCore::RA_32>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_32>(uint32_t Node);
template
Xbyak::Reg JITCore::GetSrc<JITCore::RA_16>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_16>(uint32_t Node);
template
Xbyak::Reg JITCore::GetSrc<JITCore::RA_8>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_8>(uint32_t Node);
Xbyak::Xmm JITCore::GetSrc(uint32_t Node) {
Xbyak::Xmm X86JITCore::GetSrc(uint32_t Node) {
auto PhyReg = GetPhys(Node);
return RAXMM_x[PhyReg.Reg];
}
template<uint8_t RAType>
Xbyak::Reg JITCore::GetDst(uint32_t Node) {
Xbyak::Reg X86JITCore::GetDst(uint32_t Node) {
auto PhyReg = GetPhys(Node);
if (RAType == RA_64)
return RA64[PhyReg.Reg].cvt64();
@@ -760,19 +479,19 @@ Xbyak::Reg JITCore::GetDst(uint32_t Node) {
}
template
Xbyak::Reg JITCore::GetDst<JITCore::RA_64>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_64>(uint32_t Node);
template
Xbyak::Reg JITCore::GetDst<JITCore::RA_32>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_32>(uint32_t Node);
template
Xbyak::Reg JITCore::GetDst<JITCore::RA_16>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_16>(uint32_t Node);
template
Xbyak::Reg JITCore::GetDst<JITCore::RA_8>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_8>(uint32_t Node);
template<uint8_t RAType>
std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair(uint32_t Node) {
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair(uint32_t Node) {
auto PhyReg = GetPhys(Node);
if (RAType == RA_64)
return RA64Pair[PhyReg.Reg];
@@ -781,17 +500,17 @@ std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair(uint32_t Node) {
}
template
std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair<JITCore::RA_64>(uint32_t Node);
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_64>(uint32_t Node);
template
std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair<JITCore::RA_32>(uint32_t Node);
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_32>(uint32_t Node);
Xbyak::Xmm JITCore::GetDst(uint32_t Node) {
Xbyak::Xmm X86JITCore::GetDst(uint32_t Node) {
auto PhyReg = GetPhys(Node);
return RAXMM_x[PhyReg.Reg];
}
bool JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) {
@@ -805,13 +524,13 @@ bool JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Va
}
}
bool JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
bool X86JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) {
auto Op = OpHeader->C<IR::IROp_InlineEntrypointOffset>();
if (Value) {
*Value = IR->GetHeader()->Entry + Op->Offset;
*Value = Entry + Op->Offset;
}
return true;
} else {
@@ -819,7 +538,7 @@ bool JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint
}
}
std::tuple<JITCore::SetCC, JITCore::CMovCC, JITCore::JCC> JITCore::GetCC(IR::CondClassType cond) {
std::tuple<X86JITCore::SetCC, X86JITCore::CMovCC, X86JITCore::JCC> X86JITCore::GetCC(IR::CondClassType cond) {
switch (cond.Val) {
case FEXCore::IR::COND_EQ: return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
case FEXCore::IR::COND_NEQ: return { &CodeGenerator::setne, &CodeGenerator::cmovne, &CodeGenerator::jne };
@@ -844,7 +563,7 @@ std::tuple<JITCore::SetCC, JITCore::CMovCC, JITCore::JCC> JITCore::GetCC(IR::Con
case FEXCore::IR::COND_VS:
case FEXCore::IR::COND_VC:
default:
LogMan::Msg::A("Unsupported compare type");
LOGMAN_MSG_A("Unsupported compare type");
break;
}
@@ -852,12 +571,12 @@ std::tuple<JITCore::SetCC, JITCore::CMovCC, JITCore::JCC> JITCore::GetCC(IR::Con
return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
}
void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
JumpTargets.clear();
uint32_t SSACount = IR->GetSSACount();
this->Entry = Entry;
this->RAData = RAData;
auto HeaderOp = IR->GetHeader();
// Fairly excessive buffer range to make sure we don't overflow
uint32_t BufferRange = SSACount * 16;
@@ -865,7 +584,7 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
ThreadState->CTX->ClearCodeCache(ThreadState, false);
}
void *Entry = getCurr<void*>();
void *GuestEntry = getCurr<void*>();
this->IR = IR;
if (CTX->GetGdbServerStatus()) {
@@ -874,20 +593,20 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
mov(rax, reinterpret_cast<uint64_t>(CTX));
// If the value == 0 then branch to the top
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
je(RunBlock);
// Else we need to pause now
mov(rax, ThreadPauseHandlerAddress);
mov(rax, ThreadSharedData.Dispatcher->ThreadPauseHandlerAddress);
jmp(rax);
ud2();
L(RunBlock);
}
LogMan::Throw::A(RAData != nullptr, "Needs RA");
LOGMAN_THROW_A(RAData != nullptr, "Needs RA");
SpillSlots = RAData->SpillSlots();
@@ -896,7 +615,7 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
}
#ifdef BLOCKSTATS
BlockSamplingData::BlockData *SamplingData = CTX->BlockData->GetBlockData(HeaderOp->Entry);
BlockSamplingData::BlockData *SamplingData = CTX->BlockData->GetBlockData(Entry);
if (GetSamplingData) {
mov(rcx, reinterpret_cast<uintptr_t>(SamplingData));
rdtsc();
@@ -944,7 +663,7 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
using namespace FEXCore::IR;
{
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
LOGMAN_THROW_A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
uint32_t Node = IR->GetID(BlockNode);
auto IsTarget = JumpTargets.find(Node);
@@ -1013,40 +732,29 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
}
PendingTargetLabel = nullptr;
void *Exit = getCurr<void*>();
void *GuestExit = getCurr<void*>();
this->IR = nullptr;
ready();
if (DebugData) {
DebugData->HostCodeSize = reinterpret_cast<uintptr_t>(Exit) - reinterpret_cast<uintptr_t>(Entry);
DebugData->HostCodeSize = reinterpret_cast<uintptr_t>(GuestExit) - reinterpret_cast<uintptr_t>(GuestEntry);
}
return Entry;
return GuestEntry;
}
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
// Go to sleep
Thread->StartRunning.Wait();
Thread->State.RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
}
uint64_t JITCore::ExitFunctionLink(JITCore *core, FEXCore::Core::InternalThreadState *Thread, uint64_t *record) {
uint64_t X86JITCore::ExitFunctionLink(X86JITCore *core, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record) {
auto Thread = Frame->Thread;
auto GuestRip = record[1];
auto HostCode = Thread->LookupCache->FindBlock(GuestRip);
if (!HostCode) {
Thread->State.State.rip = GuestRip;
return core->AbsoluteLoopTopAddress;
Thread->CurrentFrame->State.rip = GuestRip;
return core->ThreadSharedData.Dispatcher->AbsoluteLoopTopAddress;
}
auto LinkerAddress = core->ExitFunctionLinkerAddress;
auto LinkerAddress = core->ThreadSharedData.Dispatcher->ExitFunctionLinkerAddress;
Thread->LookupCache->AddBlockLink(GuestRip, (uintptr_t)record, [record, LinkerAddress]{
// undo the link
record[0] = LinkerAddress;
@@ -1056,250 +764,7 @@ uint64_t JITCore::ExitFunctionLink(JITCore *core, FEXCore::Core::InternalThreadS
return HostCode;
}
void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
DispatcherCodeBuffer = AllocateNewCodeBuffer(MAX_DISPATCHER_CODE_SIZE);
setNewBuffer(DispatcherCodeBuffer.Ptr, DispatcherCodeBuffer.Size);
// Temp registers
// rax, rcx, rdx, rsi, r8, r9,
// r10, r11
//
// Callee Saved
// rbx, rbp, r12, r13, r14, r15
//
// 1St Argument: rdi <ThreadState>
// XMM:
// All temp
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Bunch of exit state stuff
// x86-64 ABI has the stack aligned when /call/ happens
// Which means the destination has a misaligned stack at that point
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
mov(STATE, rdi);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
mov(qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)], rsp);
Label LoopTop;
Label FullLookup;
Label NoBlock;
Label ThreadPauseHandler{};
L(LoopTop);
AbsoluteLoopTopAddress = getCurr<uint64_t>();
{
// Load our RIP
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
// L1 Cache
mov(r13, Thread->LookupCache->GetL1Pointer());
mov(rax, rdx);
and_(rax, LookupCache::L1_ENTRIES_MASK);
shl(rax, 4);
cmp(qword[r13 + rax + 8], rdx);
jne(FullLookup);
jmp(qword[r13 + rax + 0]);
L(FullLookup);
mov(r13, Thread->LookupCache->GetPagePointer());
// Full lookup
mov(rax, rdx);
mov(rbx, Thread->LookupCache->GetVirtualMemorySize() - 1);
and_(rax, rbx);
shr(rax, 12);
// Load page pointer
mov(rdi, qword [r13 + rax * 8]);
cmp(rdi, 0);
je(NoBlock);
mov (rax, rdx);
and_(rax, 0x0FFF);
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
// check for aliasing
mov(rcx, qword [rdi + rax + 8]);
cmp(rcx, rdx);
jne(NoBlock);
// Load the block pointer
mov(rax, qword [rdi + rax]);
cmp(rax, 0);
je(NoBlock);
// Update L1
mov(r13, Thread->LookupCache->GetL1Pointer());
mov(rcx, rdx);
and_(rcx, LookupCache::L1_ENTRIES_MASK);
shl(rcx, 1);
mov(qword[r13 + rcx*8 + 8], rdx);
mov(qword[r13 + rcx*8 + 0], rax);
// Real block if we made it here
jmp(rax);
}
{
ThreadStopHandlerAddress = getCurr<uint64_t>();
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
{
ExitFunctionLinkerAddress = getCurr<uint64_t>();
// {rdi, rsi, rdx}
mov(rdi, (uintptr_t)this);
mov(rsi, STATE);
mov(rdx, rax); // rax is set at the block end
mov(rax, (uintptr_t)&ExitFunctionLink);
call(rax);
jmp(rax);
}
Label FallbackCore;
// Block creation
{
L(NoBlock);
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
// {rdi, rsi, rdx}
mov(rdi, reinterpret_cast<uint64_t>(CTX));
mov(rsi, STATE);
mov(rax, Ptr.Data);
call(rax);
// RAX contains nulptr or block ptr here
cmp(rax, 0);
je(FallbackCore);
// rdx already contains RIP here
jmp(LoopTop);
}
{
L(FallbackCore);
ud2();
}
{
// Signal return handler
ThreadSharedData.SignalHandlerReturnAddress = getCurr<uint64_t>();
ud2();
}
{
// Pause handler
ThreadPauseHandlerAddress = getCurr<uint64_t>();
L(ThreadPauseHandler);
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
mov(rsi, STATE);
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
call(rax);
PauseReturnInstruction = getCurr<uint64_t>();
ud2();
}
{
CallbackPtr = getCurr<CPUBackend::JITCallback>();
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
// First thing we need to move the thread state pointer back in to our register
mov(STATE, rdi);
// XXX: XMM?
// Make sure to adjust the refcounter so we don't clear the cache now
mov(rax, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
add(dword [rax], 1);
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
mov(rax, CTX->X86CodeGen.CallbackReturn);
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
sub(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 16);
mov(rbx, qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])]);
mov(qword [rbx], rax);
// Store RIP to the context state
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], rsi);
// Back to the loop top now
jmp(LoopTop);
}
#if ENABLE_JITSYMBOLS
std::string Name = "Dispatch_" + std::to_string(::gettid());
CTX->Symbols.Register(DispatcherCodeBuffer.Ptr, DispatcherCodeBuffer.Size, Name);
#endif
ready();
setNewBuffer(InitialCodeBuffer.Ptr, InitialCodeBuffer.Size);
}
FEXCore::CPU::CPUBackend *CreateJITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
return new JITCore(ctx, Thread, AllocateNewCodeBuffer(CompileThread ? JITCore::MAX_CODE_SIZE : JITCore::INITIAL_CODE_SIZE), CompileThread);
FEXCore::CPU::CPUBackend *CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
return new X86JITCore(ctx, Thread, AllocateNewCodeBuffer(CompileThread ? X86JITCore::MAX_CODE_SIZE : X86JITCore::INITIAL_CODE_SIZE), CompileThread);
}
}
@@ -1 +0,0 @@
+22 -32
View File
@@ -1,11 +1,18 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#pragma once
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/BlockSamplingData.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/JIT/x86_64/JIT.h"
#include "Common/MathUtils.h"
#define XBYAK64
#include <xbyak/xbyak.h>
#include <xbyak/xbyak_util.h>
@@ -54,12 +61,12 @@ const std::array<std::pair<Xbyak::Reg, Xbyak::Reg>, 4> RA64Pair = {{ {rsi, r8},
const std::array<Xbyak::Reg, 11> RAXMM = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
const std::array<Xbyak::Xmm, 11> RAXMM_x = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
class JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
class X86JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
public:
explicit JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
~JITCore() override;
explicit X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
~X86JITCore() override;
std::string GetName() override { return "JIT"; }
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *CompileCode(uint64_t Entry, FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
@@ -69,10 +76,6 @@ public:
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 256;
bool HandleSIGILL(int Signal, void *info, void *ucontext);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
void CopyNecessaryDataForCompileThread(CPUBackend *Original) override;
private:
@@ -80,6 +83,8 @@ private:
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *ThreadState;
FEXCore::IR::IRListView const *IR;
std::unique_ptr<FEXCore::CPU::Dispatcher> Dispatcher;
uint64_t Entry;
std::unordered_map<IR::OrderedNodeWrapper::NodeOffsetType, Label> JumpTargets;
Xbyak::util::Cpu Features{};
@@ -128,7 +133,6 @@ private:
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr);
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value);
void CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread);
IR::RegisterAllocationPass *RAPass;
FEXCore::IR::RegisterAllocationData *RAData;
@@ -136,13 +140,11 @@ private:
bool GetSamplingData {true};
#endif
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096 * 1;
void EmplaceNewCodeBuffer(CodeBuffer Buffer) {
CurrentCodeBuffer = &CodeBuffers.emplace_back(Buffer);
}
static uint64_t ExitFunctionLink(JITCore* code, FEXCore::Core::InternalThreadState *Thread, uint64_t *record);
static uint64_t ExitFunctionLink(X86JITCore* code, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record);
// This is the initial code buffer that we will fall back to
// In a program without signals and code clearing, we will typically
@@ -153,39 +155,26 @@ private:
// For code safety we can't delete code buffers until outside of all signals
std::vector<CodeBuffer> CodeBuffers{};
// This is the codebuffer that our dispatcher lives in
CodeBuffer DispatcherCodeBuffer{};
// This is the current code buffer that we are tracking
CodeBuffer *CurrentCodeBuffer{};
uint64_t AbsoluteLoopTopAddress{};
uint64_t ExitFunctionLinkerAddress{};
uint64_t ThreadStopHandlerAddress{};
uint64_t ThreadPauseHandlerAddress{};
uint64_t PauseReturnInstruction{};
uint32_t SignalHandlerRefCounter{};
struct CompilerSharedData {
uint64_t SignalHandlerReturnAddress{};
uint32_t *SignalHandlerRefCounterPtr{};
FEXCore::CPU::Dispatcher *Dispatcher{};
};
CompilerSharedData ThreadSharedData;
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
std::stack<uint64_t> SignalFrames;
uint32_t SpillSlots{};
using SetCC = void (JITCore::*)(const Operand& op);
using CMovCC = void (JITCore::*)(const Reg& reg, const Operand& op);
using JCC = void (JITCore::*)(const Label& label, LabelType type);
using SetCC = void (X86JITCore::*)(const Operand& op);
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
using JCC = void (X86JITCore::*)(const Label& label, LabelType type);
std::tuple<SetCC, CMovCC, JCC> GetCC(IR::CondClassType cond);
using OpHandler = void (JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
using OpHandler = void (X86JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
std::array<OpHandler, FEXCore::IR::IROps::OP_LAST + 1> OpHandlers {};
void RegisterALUHandlers();
void RegisterAtomicHandlers();
@@ -210,7 +199,7 @@ private:
///< ALU Ops
DEF_OP(TruncElementPair);
DEF_OP(Constant);
DEF_OP(Constant);
DEF_OP(EntrypointOffset);
DEF_OP(InlineConstant);
DEF_OP(InlineEntrypointOffset);
@@ -319,6 +308,7 @@ private:
DEF_OP(StoreMem);
DEF_OP(VLoadMemElement);
DEF_OP(VStoreMemElement);
DEF_OP(CacheLineClear);
///< Misc ops
DEF_OP(EndBlock);
@@ -1,3 +1,9 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -5,7 +11,7 @@
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(LoadContext) {
auto Op = IROp->C<IR::IROp_LoadContext>();
@@ -30,10 +36,10 @@ DEF_OP(LoadContext) {
}
break;
case 16: {
LogMan::Msg::A("Invalid GPR load of size 16");
LOGMAN_MSG_A("Invalid GPR load of size 16");
}
break;
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
}
}
else {
@@ -63,7 +69,7 @@ DEF_OP(LoadContext) {
movups(GetDst(Node), xword [STATE + Op->Offset]);
}
break;
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
}
}
}
@@ -94,7 +100,7 @@ DEF_OP(StoreContext) {
case 16:
LogMan::Msg::D("Invalid store size of 16");
break;
default: LogMan::Msg::A("Unhandled StoreContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled StoreContext size: %d", OpSize);
}
}
else {
@@ -123,7 +129,7 @@ DEF_OP(StoreContext) {
movups(xword [STATE + Op->Offset], GetSrc(Op->Header.Args[0].ID()));
}
break;
default: LogMan::Msg::A("Unhandled StoreContext size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled StoreContext size: %d", OpSize);
}
}
}
@@ -154,15 +160,15 @@ DEF_OP(LoadContextIndexed) {
mov(GetDst<RA_64>(Node), qword [rax + index * Op->Stride]);
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
}
break;
}
case 16:
LogMan::Msg::A("Invalid Class load of size 16");
LOGMAN_MSG_A("Invalid Class load of size 16");
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
}
}
@@ -189,7 +195,7 @@ DEF_OP(LoadContextIndexed) {
vmovq(GetDst(Node), qword [rax + index * Op->Stride]);
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
}
break;
}
@@ -217,12 +223,12 @@ DEF_OP(LoadContextIndexed) {
movups(GetDst(Node), xword [STATE + rax]);
break;
default:
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
}
break;
}
default:
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
}
}
}
@@ -242,13 +248,13 @@ DEF_OP(StoreContextIndexed) {
case 4:
case 8: {
if (!(size == 1 || size == 2 || size == 4 || size == 8)) {
LogMan::Msg::A("Unhandled StoreContextIndexed size: %d", Op->Size);
LOGMAN_MSG_A("Unhandled StoreContextIndexed size: %d", Op->Size);
}
mov(AddressFrame(Op->Size * 8) [rax + index * Op->Stride], value);
break;
}
default:
LogMan::Msg::A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
}
}
else {
@@ -273,7 +279,7 @@ DEF_OP(StoreContextIndexed) {
vmovq(AddressFrame(Op->Size * 8) [rax + index * Op->Stride], value);
break;
default:
LogMan::Msg::A("Unhandled StoreContextIndexed size: %d", size);
LOGMAN_MSG_A("Unhandled StoreContextIndexed size: %d", size);
}
break;
}
@@ -301,12 +307,12 @@ DEF_OP(StoreContextIndexed) {
movups(xword [STATE + rax], value);
break;
default:
LogMan::Msg::A("Unhandled StoreContextIndexed size: %d", size);
LOGMAN_MSG_A("Unhandled StoreContextIndexed size: %d", size);
}
break;
}
default:
LogMan::Msg::A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
LOGMAN_MSG_A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
}
}
}
@@ -334,7 +340,7 @@ DEF_OP(SpillRegister) {
mov(qword [rsp + SlotOffset], GetSrc<RA_64>(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
switch (OpSize) {
@@ -350,10 +356,10 @@ DEF_OP(SpillRegister) {
movaps(xword [rsp + SlotOffset], GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
}
} else {
LogMan::Msg::A("Unhandled SpillRegister class: %d", Op->Class.Val);
LOGMAN_MSG_A("Unhandled SpillRegister class: %d", Op->Class.Val);
}
@@ -382,7 +388,7 @@ DEF_OP(FillRegister) {
mov(GetDst<RA_64>(Node), qword [rsp + SlotOffset]);
break;
}
default: LogMan::Msg::A("Unhandled FillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled FillRegister size: %d", OpSize);
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
switch (OpSize) {
@@ -398,10 +404,10 @@ DEF_OP(FillRegister) {
movaps(GetDst(Node), xword [rsp + SlotOffset]);
break;
}
default: LogMan::Msg::A("Unhandled FillRegister size: %d", OpSize);
default: LOGMAN_MSG_A("Unhandled FillRegister size: %d", OpSize);
}
} else {
LogMan::Msg::A("Unhandled FillRegister class: %d", Op->Class.Val);
LOGMAN_MSG_A("Unhandled FillRegister class: %d", Op->Class.Val);
}
}
@@ -419,16 +425,16 @@ DEF_OP(StoreFlag) {
mov(byte [STATE + (offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag)], al);
}
Xbyak::RegExp JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
Xbyak::RegExp X86JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
if (Offset.IsInvalid()) {
return Base;
} else {
if (OffsetScale != 1 && OffsetScale != 2 && OffsetScale != 4 && OffsetScale != 8) {
LogMan::Msg::A("Unhandled GenerateModRM OffsetScale: %d", OffsetScale);
LOGMAN_MSG_A("Unhandled GenerateModRM OffsetScale: %d", OffsetScale);
}
if (OffsetType != IR::MEM_OFFSET_SXTX) {
LogMan::Msg::A("Unhandled GenerateModRM OffsetType: %d", OffsetType.Val);
LOGMAN_MSG_A("Unhandled GenerateModRM OffsetType: %d", OffsetType.Val);
}
uint64_t Const;
@@ -469,7 +475,7 @@ DEF_OP(LoadMem) {
mov(Dst, qword [MemPtr]);
}
break;
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
}
else
@@ -505,7 +511,7 @@ DEF_OP(LoadMem) {
}
}
break;
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
}
}
}
@@ -531,7 +537,7 @@ DEF_OP(StoreMem) {
case 8:
mov(qword [MemPtr], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
}
else {
@@ -554,22 +560,30 @@ DEF_OP(StoreMem) {
else
movups(xword [MemPtr], GetSrc(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
}
}
}
DEF_OP(VLoadMemElement) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VStoreMemElement) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(CacheLineClear) {
auto Op = IROp->C<IR::IROp_CacheLineClear>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
clflush(ptr [MemReg]);
}
#undef DEF_OP
void JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(LOADCONTEXT, LoadContext);
REGISTER_OP(STORECONTEXT, StoreContext);
REGISTER_OP(LOADREGISTER, Unhandled); // SRA specific, not supported on this backend
@@ -586,6 +600,7 @@ void JITCore::RegisterMemoryHandlers() {
REGISTER_OP(STOREMEMTSO, StoreMem);
REGISTER_OP(VLOADMEMELEMENT, VLoadMemElement);
REGISTER_OP(VSTOREMEMELEMENT, VStoreMemElement);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
#undef REGISTER_OP
}
}
+25 -10
View File
@@ -1,3 +1,9 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -6,7 +12,7 @@ static void PrintValue(uint64_t Value) {
LogMan::Msg::D("Value: 0x%lx", Value);
}
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
@@ -20,7 +26,7 @@ DEF_OP(Fence) {
case IR::Fence_Store.Val:
sfence();
break;
default: LogMan::Msg::A("Unknown Fence: %d", Op->Fence); break;
default: LOGMAN_MSG_A("Unknown Fence: %d", Op->Fence); break;
}
}
@@ -31,13 +37,22 @@ DEF_OP(Break) {
case 5: // Guest ud2
ud2();
break;
case 1: // Int <imm8>
ud2();
break;
case 2: // overflow
ud2();
break;
case 3: // int 1
ud2();
break;
case 4: { // HLT
// Time to quit
// Set our stack to the starting stack location
mov(rsp, qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)]);
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
// Now we need to jump to the thread stop handler
mov(TMP1, ThreadStopHandlerAddress);
mov(TMP1, ThreadSharedData.Dispatcher->ThreadStopHandlerAddress);
jmp(TMP1);
break;
}
@@ -50,21 +65,21 @@ DEF_OP(Break) {
}
// This jump target needs to be a constant offset here
mov(TMP1, ThreadPauseHandlerAddress);
mov(TMP1, ThreadSharedData.Dispatcher->ThreadPauseHandlerAddress);
jmp(TMP1);
}
else {
// If we don't have a gdb server attached then....crash?
// Treat this case like HLT
mov(rsp, qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)]);
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
// Now we need to jump to the thread stop handler
mov(TMP1, ThreadStopHandlerAddress);
mov(TMP1, ThreadSharedData.Dispatcher->ThreadStopHandlerAddress);
jmp(TMP1);
}
break;
}
default: LogMan::Msg::A("Unknown Break reason: %d", Op->Reason);
default: LOGMAN_MSG_A("Unknown Break reason: %d", Op->Reason);
}
}
@@ -125,8 +140,8 @@ DEF_OP(Print) {
}
#undef DEF_OP
void JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
@@ -1,9 +1,15 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(ExtractElementPair) {
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
switch (Op->Header.Size) {
@@ -19,7 +25,7 @@ DEF_OP(ExtractElementPair) {
mov (GetDst<RA_64>(Node), Regs[Op->Element]);
break;
}
default: LogMan::Msg::A("Unknown Size"); break;
default: LOGMAN_MSG_A("Unknown Size"); break;
}
}
@@ -45,7 +51,7 @@ DEF_OP(CreateElementPair) {
RegTmp = rax;
break;
}
default: LogMan::Msg::A("Unknown Size"); break;
default: LOGMAN_MSG_A("Unknown Size"); break;
}
if (Dst.first != RegSecond) {
@@ -67,8 +73,8 @@ DEF_OP(Mov) {
}
#undef DEF_OP
void JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
REGISTER_OP(MOV, Mov);
+128 -106
View File
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VectorZero) {
auto Dst = GetDst(Node);
vpxor(Dst, Dst, Dst);
@@ -56,24 +62,24 @@ DEF_OP(VectorImm) {
}
DEF_OP(CreateVector2) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(CreateVector4) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(SplatVector) {
auto Op = IROp->C<IR::IROp_SplatVector2>();
uint8_t OpSize = IROp->Size;
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
LOGMAN_THROW_A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
uint8_t Elements = 0;
switch (Op->Header.Op) {
case IR::OP_SPLATVECTOR4: Elements = 4; break;
case IR::OP_SPLATVECTOR2: Elements = 2; break;
default: LogMan::Msg::A("Uknown Splat size"); break;
default: LOGMAN_MSG_A("Uknown Splat size"); break;
}
uint8_t ElementSize = OpSize / Elements;
@@ -86,7 +92,7 @@ DEF_OP(SplatVector) {
case 8:
movddup(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.Size); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.Size); break;
}
}
@@ -124,7 +130,7 @@ DEF_OP(VMov) {
movaps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", OpSize); break;
}
}
@@ -162,7 +168,7 @@ DEF_OP(VAdd) {
vpaddq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -185,7 +191,7 @@ DEF_OP(VSub) {
vpsubq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -200,7 +206,7 @@ DEF_OP(VUQAdd) {
vpaddusw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -215,7 +221,7 @@ DEF_OP(VUQSub) {
vpsubusw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -230,7 +236,7 @@ DEF_OP(VSQAdd) {
vpaddsw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -245,7 +251,7 @@ DEF_OP(VSQSub) {
vpsubsw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -282,7 +288,7 @@ DEF_OP(VAddP) {
case 4:
vphaddd(GetDst(Node), xmm15, xmm14);
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -311,7 +317,7 @@ DEF_OP(VAddP) {
case 4:
vphaddd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -343,7 +349,7 @@ DEF_OP(VAddV) {
pinsrd(xmm15, eax, 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
movaps(Dest, xmm15);
@@ -360,7 +366,7 @@ DEF_OP(VURAvg) {
vpavgw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -383,7 +389,7 @@ DEF_OP(VAbs) {
vpabsq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -402,7 +408,7 @@ DEF_OP(VFAdd) {
vaddsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -416,7 +422,7 @@ DEF_OP(VFAdd) {
vaddpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -430,7 +436,7 @@ DEF_OP(VFAddP) {
case 8:
vhaddpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -449,7 +455,7 @@ DEF_OP(VFSub) {
vsubsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -463,7 +469,7 @@ DEF_OP(VFSub) {
vsubpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -483,7 +489,7 @@ DEF_OP(VFMul) {
vmulsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -497,7 +503,7 @@ DEF_OP(VFMul) {
vmulpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -517,7 +523,7 @@ DEF_OP(VFDiv) {
vdivsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -531,7 +537,7 @@ DEF_OP(VFDiv) {
vdivpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -551,7 +557,7 @@ DEF_OP(VFMin) {
vminsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -565,7 +571,7 @@ DEF_OP(VFMin) {
vminpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -585,7 +591,7 @@ DEF_OP(VFMax) {
vmaxsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -599,7 +605,7 @@ DEF_OP(VFMax) {
vmaxpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -617,7 +623,7 @@ DEF_OP(VFRecp) {
vdivss(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -630,7 +636,7 @@ DEF_OP(VFRecp) {
vdivps(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -650,7 +656,7 @@ DEF_OP(VFSqrt) {
vsqrtsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -664,7 +670,7 @@ DEF_OP(VFSqrt) {
vsqrtpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -690,7 +696,7 @@ DEF_OP(VFRSqrt) {
divsd(GetDst(Node), xmm15);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -704,7 +710,7 @@ DEF_OP(VFRSqrt) {
divps(GetDst(Node), xmm15);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
@@ -729,7 +735,7 @@ DEF_OP(VNeg) {
vpsubq(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -750,7 +756,7 @@ DEF_OP(VFNeg) {
vxorpd(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -762,20 +768,36 @@ DEF_OP(VNot) {
DEF_OP(VUMin) {
auto Op = IROp->C<IR::IROp_VUMin>();
switch (Op->Header.ElementSize) {
case 1: {
vpminub(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
if (Op->Header.Size == Op->Header.ElementSize) {
switch (Op->Header.ElementSize) {
case 8: {
// This isn't very nice on x86 until AVX-512
pextrq(TMP1, GetSrc(Op->Header.Args[0].ID()), 0);
pextrq(TMP2, GetSrc(Op->Header.Args[1].ID()), 0);
cmp(TMP1, TMP2);
cmovb(TMP2, TMP1);
pinsrq(GetDst(Node), TMP2, 0);
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
case 2: {
vpminuw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
else {
switch (Op->Header.ElementSize) {
case 1: {
vpminub(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
case 2: {
vpminuw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
case 4: {
vpminud(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
case 4: {
vpminud(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -794,7 +816,7 @@ DEF_OP(VSMin) {
vpminsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -813,7 +835,7 @@ DEF_OP(VUMax) {
vpmaxud(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -832,7 +854,7 @@ DEF_OP(VSMax) {
vpmaxsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -857,7 +879,7 @@ DEF_OP(VZip) {
punpcklqdq(xmm15, GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
movapd(GetDst(Node), xmm15);
}
@@ -884,7 +906,7 @@ DEF_OP(VZip2) {
vpunpckhdq(GetDst(Node), xmm15, xmm14);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
@@ -905,7 +927,7 @@ DEF_OP(VZip2) {
punpckhqdq(xmm15, GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
movapd(GetDst(Node), xmm15);
}
@@ -934,7 +956,7 @@ DEF_OP(VCMPEQ) {
case 8:
vpcmpeqq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
@@ -955,7 +977,7 @@ DEF_OP(VCMPEQZ) {
case 8:
vpcmpeqq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), xmm15);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
@@ -975,7 +997,7 @@ DEF_OP(VCMPGT) {
case 8:
vpcmpgtq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
@@ -996,7 +1018,7 @@ DEF_OP(VCMPGTZ) {
case 8:
vpcmpgtq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), xmm15);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
@@ -1017,7 +1039,7 @@ DEF_OP(VCMPLTZ) {
case 8:
vpcmpgtq(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
@@ -1033,7 +1055,7 @@ DEF_OP(VFCMPEQ) {
case 8:
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 0);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
else {
@@ -1044,7 +1066,7 @@ DEF_OP(VFCMPEQ) {
case 8:
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 0);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
}
@@ -1061,7 +1083,7 @@ DEF_OP(VFCMPNEQ) {
case 8:
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 4);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
@@ -1073,7 +1095,7 @@ DEF_OP(VFCMPNEQ) {
case 8:
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 4);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
}
@@ -1090,7 +1112,7 @@ DEF_OP(VFCMPLT) {
case 8:
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 1);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
else {
@@ -1101,7 +1123,7 @@ DEF_OP(VFCMPLT) {
case 8:
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 1);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
}
@@ -1118,7 +1140,7 @@ DEF_OP(VFCMPGT) {
case 8:
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[1].ID()), GetSrc(Op->Header.Args[0].ID()), 1);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
else {
@@ -1129,7 +1151,7 @@ DEF_OP(VFCMPGT) {
case 8:
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[1].ID()), GetSrc(Op->Header.Args[0].ID()), 1);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
}
@@ -1146,7 +1168,7 @@ DEF_OP(VFCMPLE) {
case 8:
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 2);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
else {
@@ -1157,7 +1179,7 @@ DEF_OP(VFCMPLE) {
case 8:
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 2);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
}
@@ -1174,7 +1196,7 @@ DEF_OP(VFCMPORD) {
case 8:
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 7);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
else {
@@ -1185,7 +1207,7 @@ DEF_OP(VFCMPORD) {
case 8:
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 7);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
}
@@ -1202,7 +1224,7 @@ DEF_OP(VFCMPUNO) {
case 8:
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 3);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
else {
@@ -1213,21 +1235,21 @@ DEF_OP(VFCMPUNO) {
case 8:
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 3);
break;
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
}
}
}
DEF_OP(VUShl) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VUShr) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VSShr) {
LogMan::Msg::A("Unimplemented");
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(VUShlS) {
@@ -1246,7 +1268,7 @@ DEF_OP(VUShlS) {
vpsllq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1266,7 +1288,7 @@ DEF_OP(VUShrS) {
vpsrlq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1283,7 +1305,7 @@ DEF_OP(VSShrS) {
break;
}
case 8: // Doesn't exist on x86
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1316,7 +1338,7 @@ DEF_OP(VInsElement) {
pinsrq(xmm15, rax, Op->DestIdx);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
movapd(GetDst(Node), xmm15);
@@ -1351,7 +1373,7 @@ DEF_OP(VInsScalarElement) {
pinsrq(xmm15, rax, Op->DestIdx);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
movapd(GetDst(Node), xmm15);
@@ -1381,7 +1403,7 @@ DEF_OP(VExtractElement) {
pinsrq(GetDst(Node), rax, 0);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1432,7 +1454,7 @@ DEF_OP(VUShrI) {
psrlq(GetDst(Node), Op->BitShift);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1448,7 +1470,7 @@ DEF_OP(VSShrI) {
psrad(GetDst(Node), Op->BitShift);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1468,7 +1490,7 @@ DEF_OP(VShlI) {
psllq(GetDst(Node), Op->BitShift);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1498,7 +1520,7 @@ DEF_OP(VUShrNI) {
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
vmovq(xmm15, rax);
@@ -1534,7 +1556,7 @@ DEF_OP(VUShrNI2) {
mov(rcx, 0x0B'0A'09'08'03'02'01'00); // Upper
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
vmovq(xmm15, rax);
@@ -1561,7 +1583,7 @@ DEF_OP(VSXTL) {
case 8:
pmovsxdq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1580,7 +1602,7 @@ DEF_OP(VSXTL2) {
case 8:
pmovsxdq(GetDst(Node), GetDst(Node));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1596,7 +1618,7 @@ DEF_OP(VUXTL) {
case 8:
pmovzxdq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1615,7 +1637,7 @@ DEF_OP(VUXTL2) {
case 8:
pmovzxdq(GetDst(Node), GetDst(Node));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
}
@@ -1628,7 +1650,7 @@ DEF_OP(VSQXTN) {
case 2:
packssdw(xmm15, GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
psrldq(xmm15, 8);
movaps(GetDst(Node), xmm15);
@@ -1647,7 +1669,7 @@ DEF_OP(VSQXTN2) {
case 2:
packssdw(xmm15, GetSrc(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
if (OpSize == 8) {
@@ -1665,7 +1687,7 @@ DEF_OP(VSQXTUN) {
case 2:
packusdw(xmm15, GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
psrldq(xmm15, 8);
movaps(GetDst(Node), xmm15);
@@ -1684,7 +1706,7 @@ DEF_OP(VSQXTUN2) {
case 2:
packusdw(xmm15, GetSrc(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
}
if (OpSize == 8) {
psrldq(xmm15, OpSize / 2);
@@ -1704,7 +1726,7 @@ DEF_OP(VMul) {
vpmulld(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1735,7 +1757,7 @@ DEF_OP(VUMull) {
vpmuludq(GetDst(Node), xmm14, xmm15);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1770,7 +1792,7 @@ DEF_OP(VSMull) {
vpmuldq(GetDst(Node), xmm14, xmm15);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1806,7 +1828,7 @@ DEF_OP(VUMull2) {
vpmuludq(GetDst(Node), xmm14, xmm15);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1846,7 +1868,7 @@ DEF_OP(VSMull2) {
vpmuldq(GetDst(Node), xmm14, xmm15);
break;
}
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
@@ -1864,13 +1886,13 @@ DEF_OP(VTBL1) {
vpshufb(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
break;
}
default: LogMan::Msg::A("Unknown OpSize: %d", OpSize); break;
default: LOGMAN_MSG_A("Unknown OpSize: %d", OpSize); break;
}
}
#undef DEF_OP
void JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(CREATEVECTOR2, CreateVector2);
+17 -8
View File
@@ -1,6 +1,15 @@
/*
$info$
tags: glue|block-database
desc: Stores information about blocks, and provides C++ implementations to lookup the blocks
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Core.h"
#include "Interface/Core/LookupCache.h"
#include <FEXCore/Utils/Allocator.h>
#include <sys/mman.h>
namespace FEXCore {
@@ -19,27 +28,27 @@ LookupCache::LookupCache(FEXCore::Context::Context *CTX)
// Allocate a region of memory that we can use to back our block pointers
// We need one pointer per page of virtual memory
// At 64GB of virtual memory this will allocate 128MB of virtual memory space
PagePointer = reinterpret_cast<uintptr_t>(mmap(nullptr, ctx->Config.VirtualMemSize / 4096 * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
PagePointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::mmap(nullptr, ctx->Config.VirtualMemSize / 4096 * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
// Allocate our memory backing our pages
// We need 32KB per guest page (One pointer per byte)
// XXX: We can drop down to 16KB if we store 4byte offsets from the code base
// We currently limit to 128MB of real memory for caching for the total cache size.
// Can end up being inefficient if we compile a small number of blocks per page
PageMemory = reinterpret_cast<uintptr_t>(mmap(nullptr, CODE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
LogMan::Throw::A(PageMemory != -1ULL, "Failed to allocate page memory");
PageMemory = reinterpret_cast<uintptr_t>(FEXCore::Allocator::mmap(nullptr, CODE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
LOGMAN_THROW_A(PageMemory != -1ULL, "Failed to allocate page memory");
// L1 Cache
L1Pointer = reinterpret_cast<uintptr_t>(mmap(nullptr, L1_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
LogMan::Throw::A(L1Pointer != -1ULL, "Failed to allocate L1Pointer");
L1Pointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::mmap(nullptr, L1_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
LOGMAN_THROW_A(L1Pointer != -1ULL, "Failed to allocate L1Pointer");
VirtualMemSize = ctx->Config.VirtualMemSize;
}
LookupCache::~LookupCache() {
munmap(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8);
munmap(reinterpret_cast<void*>(PageMemory), CODE_SIZE);
munmap(reinterpret_cast<void*>(L1Pointer), L1_SIZE);
FEXCore::Allocator::munmap(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8);
FEXCore::Allocator::munmap(reinterpret_cast<void*>(PageMemory), CODE_SIZE);
FEXCore::Allocator::munmap(reinterpret_cast<void*>(L1Pointer), L1_SIZE);
}
void LookupCache::HintUsedRange(uint64_t Address, uint64_t Size) {
+8 -5
View File
@@ -39,13 +39,17 @@ public:
void AddBlockMapping(uint64_t Address, void *HostCode, uint64_t Start, uint64_t Length) {
auto InsertPoint = BlockList.emplace(Address, (uintptr_t)HostCode);
LogMan::Throw::A(InsertPoint.second == true, "Dupplicate block mapping added");
LOGMAN_THROW_A(InsertPoint.second == true, "Dupplicate block mapping added");
for (auto CurrentPage = Start >> 12, EndPage = (Start + Length) >> 12; CurrentPage <= EndPage; CurrentPage++) {
CodePages[CurrentPage].push_back(Address);
}
// no need to update L1 or L2, they will get updated on first lookup
// There is no need to update L1 or L2, they will get updated on first lookup
// However, adding to L1 here increases performance
auto &L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
L1Entry.GuestCode = Address;
L1Entry.HostCode = (uintptr_t)HostCode;
}
void Erase(uint64_t Address) {
@@ -105,9 +109,8 @@ private:
void CacheBlockMapping(uint64_t Address, uintptr_t HostCode) {
// Do L1
auto &L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
if (L1Entry.GuestCode == Address) {
L1Entry.GuestCode = L1Entry.HostCode = 0;
}
L1Entry.GuestCode = Address;
L1Entry.HostCode = HostCode;
// Do ful map
auto FullAddress = Address;
File diff suppressed because it is too large. Load diff
+9 -6
View File
@@ -49,7 +49,7 @@ public:
OrderedNode* GetNewJumpBlock(uint64_t RIP) {
auto it = JumpTargets.find(RIP);
LogMan::Throw::A(it != JumpTargets.end(), "Couldn't find block generated for 0x%lx", RIP);
LOGMAN_THROW_A(it != JumpTargets.end(), "Couldn't find block generated for 0x%lx", RIP);
return it->second.BlockEntry;
}
@@ -59,7 +59,7 @@ public:
it->second.HaveEmitted = true;
if (CurrentCodeBlock->Wrapped(ListData.Begin()).ID() == it->second.BlockEntry->Wrapped(ListData.Begin()).ID()) return;
if (CurrentCodeBlock->Wrapped(DualListData.ListBegin()).ID() == it->second.BlockEntry->Wrapped(DualListData.ListBegin()).ID()) return;
// We have hit a RIP that is a jump target
// Thus we need to end up in a new block
@@ -81,14 +81,14 @@ public:
// rdi, 0x8
// cmp qword [rdi-8], 0
// jne .label
if (!BlockSetRIP) {
if (LastOp && !BlockSetRIP) {
auto it = JumpTargets.find(NextRIP);
if (it == JumpTargets.end() && LastOp) {
if (it == JumpTargets.end()) {
uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4;
// If we don't have a jump target to a new block then we have to leave
// Set the RIP to the next instruction and leave
auto RelocatedNextRIP = _EntrypointOffset(NextRIP - Current_Header->Entry, GPRSize);
auto RelocatedNextRIP = _EntrypointOffset(NextRIP - Entry, GPRSize);
_ExitFunction(RelocatedNextRIP);
}
else if (it != JumpTargets.end()) {
@@ -104,6 +104,7 @@ public:
OpDispatchBuilder(FEXCore::Context::Context *ctx);
void ResetWorkingList();
void ResetDecodeFailure() { DecodeFailure = false; }
bool HadDecodeFailure() { return DecodeFailure; }
void BeginFunction(uint64_t RIP, std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
@@ -458,6 +459,8 @@ public:
template<uint8_t FenceType>
void FenceOp(OpcodeArgs);
void StoreFenceOrCLFlush(OpcodeArgs);
void PSADBW(OpcodeArgs);
void AESImcOp(OpcodeArgs);
@@ -475,8 +478,8 @@ public:
OrderedNode *GetPackedRFLAG(bool Lower8);
void SetMultiblock(bool _Multiblock) { Multiblock = _Multiblock; }
bool GetMultiblock() { return Multiblock; }
bool HandledLock = false;
private:
bool DecodeFailure{false};
FEXCore::IR::IROp_IRHeader *Current_Header{};
+13 -5
View File
@@ -1,4 +1,12 @@
/*
$info$
tags: glue|x86-guest-code
desc: Guest-side assembly helpers used by the backends
$end_info$
*/
#include "Interface/Core/X86HelperGen.h"
#include <FEXCore/Utils/Allocator.h>
#include <cstring>
#include <stdlib.h>
@@ -24,15 +32,15 @@ X86GeneratedCode::X86GeneratedCode() {
}
X86GeneratedCode::~X86GeneratedCode() {
munmap(CodePtr, CODE_SIZE);
FEXCore::Allocator::munmap(CodePtr, CODE_SIZE);
}
void* X86GeneratedCode::AllocateGuestCodeSpace(size_t Size) {
FEXCore::Config::Value<bool> Is64BitMode{FEXCore::Config::CONFIG_IS64BIT_MODE, 0};
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
if (Is64BitMode()) {
// 64bit mode can have its sigret handler anywhere
return mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return FEXCore::Allocator::mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
// First 64bit page
@@ -42,14 +50,14 @@ void* X86GeneratedCode::AllocateGuestCodeSpace(size_t Size) {
// We need to have the sigret handler in the lower 32bits of memory space
// Scan top down and try to allocate a location
for (size_t Location = 0xFFFF'E000; Location != 0x0; Location -= 0x1000) {
void *Ptr = mmap(reinterpret_cast<void*>(Location), Size, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
void *Ptr = FEXCore::Allocator::mmap(reinterpret_cast<void*>(Location), Size, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (Ptr != MAP_FAILED &&
reinterpret_cast<uintptr_t>(Ptr) >= LOCATION_MAX) {
// Failed to map in the lower 32bits
// Try again
// Can happen in the case that host kernel ignores MAP_FIXED_NOREPLACE
munmap(Ptr, Size);
FEXCore::Allocator::munmap(Ptr, Size);
continue;
}
+6
View File
@@ -1,3 +1,9 @@
/*
$info$
tags: glue|x86-guest-code
$end_info$
*/
#pragma once
#include <FEXCore/Config/Config.h>
+7
View File
@@ -1,3 +1,10 @@
/*
$info$
meta: frontend|x86-tables ~ Metadata that drives the frontend x86/64 decoding
tags: frontend|x86-tables
$end_info$
*/
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Core/Context.h>
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
#include <FEXCore/Core/Context.h>
@@ -279,13 +285,13 @@ void InitializeBaseTables(Context::OperatingMode Mode) {
{0xEA, 1, X86InstInfo{"JMPF", TYPE_INST, FLAGS_NONE, 0, nullptr}},
};
GenerateTable(BaseOps, BaseOpTable, sizeof(BaseOpTable) / sizeof(BaseOpTable[0]));
GenerateTable(BaseOps, BaseOpTable, std::size(BaseOpTable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(BaseOps, BaseOpTable_64, sizeof(BaseOpTable_64) / sizeof(BaseOpTable_64[0]));
GenerateTable(BaseOps, BaseOpTable_64, std::size(BaseOpTable_64));
}
else {
GenerateTable(BaseOps, BaseOpTable_32, sizeof(BaseOpTable_32) / sizeof(BaseOpTable_32[0]));
GenerateTable(BaseOps, BaseOpTable_32, std::size(BaseOpTable_32));
}
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -38,6 +44,6 @@ void InitializeDDDTables() {
{0xB7, 1, X86InstInfo{"PMULHRW", TYPE_3DNOW_INST, FLAGS_MODRM, 0, nullptr}},
};
GenerateTable(DDDNowOps, DDDNowOpTable, sizeof(DDDNowOpTable) / sizeof(DDDNowOpTable[0]));
GenerateTable(DDDNowOps, DDDNowOpTable, std::size(DDDNowOpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -20,6 +26,6 @@ void InitializeEVEXTables() {
{0xE7, 1, X86InstInfo{"VMOVNTDQ", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
};
GenerateTable(EVEXTableOps, EVEXTable, sizeof(EVEXTable) / sizeof(EVEXTable[0]));
GenerateTable(EVEXTableOps, EVEXTable, std::size(EVEXTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -54,7 +60,7 @@ void InitializeH0F38Tables() {
{OPD(PF_38_66, 0x25), 1, X86InstInfo{"PMOVSXDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x28), 1, X86InstInfo{"PMULDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x29), 1, X86InstInfo{"PCMPEQQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x2A), 1, X86InstInfo{"MOVNTDQA", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x2A), 1, X86InstInfo{"MOVNTDQA", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x2B), 1, X86InstInfo{"PACKUSDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x30), 1, X86InstInfo{"PMOVZXBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
@@ -92,6 +98,6 @@ void InitializeH0F38Tables() {
};
#undef OPD
GenerateTable(H0F38TableOps, H0F38Table, sizeof(H0F38Table) / sizeof(H0F38Table[0]));
GenerateTable(H0F38TableOps, H0F38Table, std::size(H0F38Table));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -48,10 +54,10 @@ void InitializeH0F3ATables(Context::OperatingMode Mode) {
#undef OPD
GenerateTable(H0F3ATableOps, H0F3ATable, sizeof(H0F3ATable) / sizeof(H0F3ATable[0]));
GenerateTable(H0F3ATableOps, H0F3ATable, std::size(H0F3ATable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(H0F3ATableOps, H0F3ATable_64, sizeof(H0F3ATable_64) / sizeof(H0F3ATable_64[0]));
GenerateTable(H0F3ATableOps, H0F3ATable_64, std::size(H0F3ATable_64));
}
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -152,12 +158,12 @@ void InitializePrimaryGroupTables(Context::OperatingMode Mode) {
#undef OPD
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable, sizeof(PrimaryGroupOpTable) / sizeof(PrimaryGroupOpTable[0]));
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable, std::size(PrimaryGroupOpTable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_64, sizeof(PrimaryGroupOpTable_64) / sizeof(PrimaryGroupOpTable_64[0]));
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_64, std::size(PrimaryGroupOpTable_64));
}
else {
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_32, sizeof(PrimaryGroupOpTable_32) / sizeof(PrimaryGroupOpTable_32[0]));
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_32, std::size(PrimaryGroupOpTable_32));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -477,7 +483,7 @@ void InitializeSecondaryGroupTables() {
};
#undef OPD
GenerateTable(SecondInstGroupOps, SecondaryExtensionOpTable, sizeof(SecondaryExtensionOpTable) / sizeof(SecondaryExtensionOpTable[0]));
GenerateTable(SecondInstGroupOps, SecondaryExtensionOpTable, std::size(SecondaryExtensionOpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -46,6 +52,6 @@ void InitializeSecondaryModRMTables() {
{((3 << 3) | 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
};
GenerateTable(SecondModRMTableOps, SecondaryModRMExtensionOpTable, sizeof(SecondaryModRMExtensionOpTable) / sizeof(SecondaryModRMExtensionOpTable[0]));
GenerateTable(SecondModRMTableOps, SecondaryModRMExtensionOpTable, std::size(SecondaryModRMExtensionOpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -571,18 +577,18 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
{0xFF, 1, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}},
};
GenerateTable(SecondBaseOps, TwoByteOpTable, sizeof(TwoByteOpTable) / sizeof(TwoByteOpTable[0]));
GenerateTable(SecondBaseOps, TwoByteOpTable, std::size(TwoByteOpTable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(SecondBaseOps, TwoByteOpTable_64, sizeof(TwoByteOpTable_64) / sizeof(TwoByteOpTable_64[0]));
GenerateTable(SecondBaseOps, TwoByteOpTable_64, std::size(TwoByteOpTable_64));
}
else {
GenerateTable(SecondBaseOps, TwoByteOpTable_32, sizeof(TwoByteOpTable_32) / sizeof(TwoByteOpTable_32[0]));
GenerateTable(SecondBaseOps, TwoByteOpTable_32, std::size(TwoByteOpTable_32));
}
GenerateTableWithCopy(RepModOps, RepModOpTable, sizeof(RepModOpTable) / sizeof(RepModOpTable[0]), SecondBaseOps);
GenerateTableWithCopy(RepNEModOps, RepNEModOpTable, sizeof(RepNEModOpTable) / sizeof(RepNEModOpTable[0]), SecondBaseOps);
GenerateTableWithCopy(OpSizeModOps, OpSizeModOpTable, sizeof(OpSizeModOpTable) / sizeof(OpSizeModOpTable[0]), SecondBaseOps);
GenerateTableWithCopy(RepModOps, RepModOpTable, std::size(RepModOpTable), SecondBaseOps);
GenerateTableWithCopy(RepNEModOps, RepNEModOpTable, std::size(RepNEModOpTable), SecondBaseOps);
GenerateTableWithCopy(OpSizeModOps, OpSizeModOpTable, std::size(OpSizeModOpTable), SecondBaseOps);
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -500,7 +506,7 @@ void InitializeVEXTables() {
};
#undef OPD
GenerateTable(VEXTableOps, VEXTable, sizeof(VEXTable) / sizeof(VEXTable[0]));
GenerateTable(VEXTableGroupOps, VEXGroupTable, sizeof(VEXGroupTable) / sizeof(VEXGroupTable[0]));
GenerateTable(VEXTableOps, VEXTable, std::size(VEXTable));
GenerateTable(VEXTableGroupOps, VEXGroupTable, std::size(VEXGroupTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#pragma once
#include <FEXCore/Debug/X86Tables.h>
@@ -27,7 +33,7 @@ static inline void GenerateTable(X86InstInfo *FinalTable, U8U8InfoStruct const *
auto OpNum = Op.first;
X86InstInfo const &Info = Op.Info;
for (uint32_t i = 0; i < Op.second; ++i) {
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
FinalTable[OpNum + i] = Info;
#ifndef NDEBUG
++Total;
@@ -44,7 +50,7 @@ static inline void GenerateTable(X86InstInfo *FinalTable, U16U8InfoStruct const
auto OpNum = Op.first;
X86InstInfo const &Info = Op.Info;
for (uint32_t i = 0; i < Op.second; ++i) {
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
FinalTable[OpNum + i] = Info;
#ifndef NDEBUG
++Total;
@@ -61,7 +67,7 @@ static inline void GenerateTableWithCopy(X86InstInfo *FinalTable, U8U8InfoStruct
auto OpNum = Op.first;
X86InstInfo const &Info = Op.Info;
for (uint32_t i = 0; i < Op.second; ++i) {
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
if (Info.Type == TYPE_COPY_OTHER) {
FinalTable[OpNum + i] = OtherLocal[OpNum + i];
}
@@ -83,7 +89,7 @@ static inline void GenerateX87Table(X86InstInfo *FinalTable, U16U8InfoStruct con
auto OpNum = Op.first;
X86InstInfo const &Info = Op.Info;
for (uint32_t i = 0; i < Op.second; ++i) {
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
if ((OpNum & 0b11'000'000) == 0b11'000'000) {
// If the mod field is 0b11 then it is a regular op
FinalTable[OpNum + i] = Info;
@@ -91,7 +97,7 @@ static inline void GenerateX87Table(X86InstInfo *FinalTable, U16U8InfoStruct con
else {
// If the mod field is !0b11 then this instruction is duplicated through the whole mod [0b00, 0b10] range
// and the modrm.rm space because that is used part of the instruction encoding
LogMan::Throw::A((OpNum & 0b11'000'000) == 0, "Only support mod field of zero in this path");
LOGMAN_THROW_A((OpNum & 0b11'000'000) == 0, "Only support mod field of zero in this path");
for (uint16_t mod = 0b00'000'000; mod < 0b11'000'000; mod += 0b01'000'000) {
for (uint16_t rm = 0b000; rm < 0b1'000; ++rm) {
FinalTable[(OpNum | mod | rm) + i] = Info;
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -254,6 +260,6 @@ void InitializeX87Tables() {
#undef OPD
#undef OPDReg
GenerateX87Table(X87Ops, X87OpTable, sizeof(X87OpTable) / sizeof(X87OpTable[0]));
GenerateX87Table(X87Ops, X87OpTable, std::size(X87OpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -119,7 +125,7 @@ void InitializeXOPTables() {
};
#undef OPD
GenerateTable(XOPTableOps, XOPTable, sizeof(XOPTable) / sizeof(XOPTable[0]));
GenerateTable(XOPTableGroupOps, XOPGroupTable, sizeof(XOPGroupTable) / sizeof(XOPGroupTable[0]));
GenerateTable(XOPTableOps, XOPTable, std::size(XOPTable));
GenerateTable(XOPTableGroupOps, XOPGroupTable, std::size(XOPGroupTable));
}
}
+13 -7
View File
@@ -1,3 +1,10 @@
/*
$info$
meta: glue|thunks ~ FEXCore side of thunks: Registration, Lookup
tags: glue|thunks
$end_info$
*/
#include <FEXCore/Utils/LogManager.h>
#include "Thunks.h"
@@ -23,7 +30,7 @@ static thread_local FEXCore::Core::InternalThreadState *Thread;
namespace FEXCore {
struct ExportEntry { uint8_t *sha256; ThunkedFunction* Fn; };
class ThunkHandler_impl final: public ThunkHandler {
@@ -41,9 +48,8 @@ namespace FEXCore {
Set arg0/1 to arg regs, use CTX::HandleCallback to handle the callback
*/
static void CallCallback(void *callback, void *arg0, void* arg1) {
Thread->State.State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0;
Thread->State.State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1;
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0;
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1;
Thread->CTX->HandleCallback((uintptr_t)callback);
}
@@ -57,10 +63,10 @@ namespace FEXCore {
auto Name = Args->Name;
auto CallbackThunks = Args->CallbackThunks;
auto SOName = CTX->Config.ThunkLibsPath + "/" + (const char*)Name + "-host.so";
auto SOName = CTX->Config.ThunkHostLibsPath() + "/" + (const char*)Name + "-host.so";
LogMan::Msg::D("Load lib: %s -> %s", Name, SOName.c_str());
auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW);
if (!Handle) {
@@ -78,7 +84,7 @@ namespace FEXCore {
LogMan::Msg::E("Load lib: failed to find export %s", InitSym.c_str());
return;
}
auto Exports = InitFN((void*)&CallCallback, CallbackThunks);
auto That = reinterpret_cast<ThunkHandler_impl*>(CTX->ThunkHandler.get());
+6
View File
@@ -1,3 +1,9 @@
/*
$info$
tags: glue|thunks
$end_info$
*/
#pragma once
#include <FEXCore/IR/IR.h>
+16 -3
View File
@@ -79,7 +79,6 @@
"Blocks"
],
"Args": [
"uint64_t", "Entry",
"uint32_t", "BlockCount"
]
},
@@ -793,6 +792,17 @@
]
},
"CacheLineClear": {
"Desc": ["Does a 64 byte cacheline clear at the address specified"
],
"HasSideEffects": true,
"OpClass": "Memory",
"SSAArgs": "1",
"SSANames": [
"Addr"
]
},
"Add": {
"Desc": [ "Integer Add",
"Will truncate to 64 or 32bits"
@@ -1138,7 +1148,11 @@
"DestClass": "GPRPair",
"FixedDestSize": "8",
"NumElements": "2",
"SSAArgs": "1"
"SSAArgs": "2",
"SSANames": [
"Function",
"Leaf"
]
},
"Bfi": {
@@ -1559,7 +1573,6 @@
"Depending on backend, may only support GPR printing"
],
"OpClass": "Misc",
"DestSize": "GetOpSize(ssa0)",
"SSAArgs": "1",
"SSANames": [
"Value"
+9 -2
View File
@@ -1,3 +1,10 @@
/*
$info$
meta: ir|dumper ~ IR -> Text
tags: ir|dumper
$end_info$
*/
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/LogManager.h>
@@ -24,6 +31,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
*out << "#0x" << std::hex << Arg;
}
[[maybe_unused]]
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, const char* Arg) {
*out << Arg;
}
@@ -91,7 +99,7 @@ static void PrintArg(std::stringstream *out, IRListView const* IR, OrderedNodeWr
*out << "%ssa" << std::to_string(Arg.ID());
if (RAData) {
auto PhyReg = RAData->GetNodeRegister(Arg.ID());
switch (PhyReg.Class) {
case FEXCore::IR::GPRClass.Val: *out << "(GPR"; break;
case FEXCore::IR::GPRFixedClass.Val: *out << "(GPRFixed"; break;
@@ -159,7 +167,6 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationDa
++CurrentIndent;
AddIndent();
*out << "(%ssa0) " << "IRHeader ";
*out << "#0x" << std::hex << HeaderOp->Entry << ", ";
*out << "%ssa" << HeaderOp->Blocks.ID() << ", ";
*out << "#" << std::dec << HeaderOp->BlockCount << std::endl;
+23 -17
View File
@@ -1,19 +1,25 @@
/*
$info$
meta: ir|emitter ~ C++ Functions to generate IR. See IR.json for spec.
tags: ir|emitter
$end_info$
*/
#include <FEXCore/IR/IREmitter.h>
namespace FEXCore::IR {
void IREmitter::ResetWorkingList() {
Data.Reset();
ListData.Reset();
DualListData.Reset();
CodeBlocks.clear();
CurrentWriteCursor = nullptr;
// This is necessary since we do "null" pointer checks
InvalidNode = reinterpret_cast<OrderedNode*>(ListData.Allocate(sizeof(OrderedNode)));
InvalidNode = reinterpret_cast<OrderedNode*>(DualListData.ListAllocate(sizeof(OrderedNode)));
memset(InvalidNode, 0, sizeof(OrderedNode));
CurrentCodeBlock = nullptr;
}
void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode, AllNodesIterator After, AllNodesIterator End) {
uintptr_t ListBegin = ListData.Begin();
uintptr_t ListBegin = DualListData.ListBegin();
auto NodeId = Node->Wrapped(ListBegin).ID();
while (After != End) {
@@ -38,8 +44,8 @@ void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode,
}
void IREmitter::ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg) {
uintptr_t ListBegin = ListData.Begin();
uintptr_t DataBegin = Data.Begin();
uintptr_t ListBegin = DualListData.ListBegin();
uintptr_t DataBegin = DualListData.DataBegin();
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
OrderedNodeWrapper OldArgWrapper = IROp->Args[Arg];
@@ -50,8 +56,8 @@ void IREmitter::ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode
}
void IREmitter::RemoveArgUses(OrderedNode *Node) {
uintptr_t ListBegin = ListData.Begin();
uintptr_t DataBegin = Data.Begin();
uintptr_t ListBegin = DualListData.ListBegin();
uintptr_t DataBegin = DualListData.DataBegin();
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
@@ -65,7 +71,7 @@ void IREmitter::RemoveArgUses(OrderedNode *Node) {
void IREmitter::Remove(OrderedNode *Node) {
RemoveArgUses(Node);
Node->Unlink(ListData.Begin());
Node->Unlink(DualListData.ListBegin());
}
IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode* insertAfter) {
@@ -76,14 +82,14 @@ IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode
if (insertAfter) {
LinkCodeBlocks(insertAfter, CodeNode);
} else {
LogMan::Throw::A(CurrentCodeBlock != nullptr, "CurrentCodeBlock must not be null here");
LOGMAN_THROW_A(CurrentCodeBlock != nullptr, "CurrentCodeBlock must not be null here");
// Find last block
auto LastBlock = CurrentCodeBlock;
while (LastBlock->Header.Next.GetNode(ListData.Begin()) != InvalidNode)
LastBlock = LastBlock->Header.Next.GetNode(ListData.Begin());
while (LastBlock->Header.Next.GetNode(DualListData.ListBegin()) != InvalidNode)
LastBlock = LastBlock->Header.Next.GetNode(DualListData.ListBegin());
// Append it after the last block
LinkCodeBlocks(LastBlock, CodeNode);
}
@@ -95,12 +101,12 @@ IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode
void IREmitter::SetCurrentCodeBlock(OrderedNode *Node) {
CurrentCodeBlock = Node;
LogMan::Throw::A(Node->Op(Data.Begin())->Op == OP_CODEBLOCK, "Node wasn't codeblock. It was '%s'", std::string(IR::GetName(Node->Op(Data.Begin())->Op)).c_str());
SetWriteCursor(Node->Op(Data.Begin())->CW<IROp_CodeBlock>()->Begin.GetNode(ListData.Begin()));
LOGMAN_THROW_A(Node->Op(DualListData.DataBegin())->Op == OP_CODEBLOCK, "Node wasn't codeblock. It was '%s'", std::string(IR::GetName(Node->Op(DualListData.DataBegin())->Op)).c_str());
SetWriteCursor(Node->Op(DualListData.DataBegin())->CW<IROp_CodeBlock>()->Begin.GetNode(DualListData.ListBegin()));
}
void IREmitter::ReplaceWithConstant(OrderedNode *Node, uint64_t Value) {
auto Header = Node->Op(Data.Begin());
auto Header = Node->Op(DualListData.DataBegin());
if (IRSizes[Header->Op] >= sizeof(IROp_Constant)) {
// Unlink any arguments the node currently has
+14 -9
View File
@@ -1,3 +1,10 @@
/*
$info$
meta: ir|parser ~ Text -> IR
tags: ir|parser
$end_info$
*/
#include <string>
#include <vector>
#include <istream>
@@ -490,13 +497,11 @@ class IRParser: public FEXCore::IR::IREmitter {
return false;
}
auto Entry = DecodeValue<uint64_t>(Def.Args[0]);
auto CodeBlockCount = DecodeValue<uint64_t>(Def.Args[2]);
auto CodeBlockCount = DecodeValue<uint64_t>(Def.Args[1]);
if (!CheckPrintError(Def, Entry.first)) return false;
if (!CheckPrintError(Def, CodeBlockCount.first)) return false;
IRHeader = _IRHeader(InvalidNode, Entry.second, CodeBlockCount.second);
IRHeader = _IRHeader(InvalidNode, CodeBlockCount.second);
}
SetWriteCursor(nullptr); // isolate the header from everything following
@@ -515,7 +520,7 @@ class IRParser: public FEXCore::IR::IREmitter {
if (i == 1) {
// First code block is the entry block
// Link the header to the first block
IRHeader.first->Blocks = CodeBlock.Node->Wrapped(ListData.Begin());
IRHeader.first->Blocks = CodeBlock.Node->Wrapped(DualListData.ListBegin());
}
CodeBlocks.emplace_back(CodeBlock.Node);
}
@@ -548,7 +553,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
CurrentBlock = Def.Node;
CurrentBlockOp = CurrentBlock->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
CurrentBlockOp = CurrentBlock->Op(DualListData.DataBegin())->CW<FEXCore::IR::IROp_CodeBlock>();
break;
}
@@ -566,7 +571,7 @@ class IRParser: public FEXCore::IR::IREmitter {
if (!CheckPrintError(Def, Adjust.first)) return false;
Def.Node = _BeginBlock(Adjust.second);
CurrentBlockOp->Begin = Def.Node->Wrapped(ListData.Begin());
CurrentBlockOp->Begin = Def.Node->Wrapped(DualListData.ListBegin());
break;
}
@@ -583,7 +588,7 @@ class IRParser: public FEXCore::IR::IREmitter {
if (!CheckPrintError(Def, Adjust.first)) return false;
Def.Node = _EndBlock(Adjust.second);
CurrentBlockOp->Last = Def.Node->Wrapped(ListData.Begin());
CurrentBlockOp->Last = Def.Node->Wrapped(DualListData.ListBegin());
CurrentBlock = nullptr;
CurrentBlockOp = nullptr;
@@ -610,7 +615,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
if (Def.HasDefinition) {
auto IROp = Def.Node->Op(Data.Begin());
auto IROp = Def.Node->Op(DualListData.DataBegin());
if (Def.Size.Elements()) {
IROp->Size = Def.Size.Bytes() * Def.Size.Elements();
IROp->ElementSize = Def.Size.Bytes();
+16 -1
View File
@@ -1,3 +1,11 @@
/*
$info$
meta: ir|opts ~ IR to IR Optimization
tags: ir|opts
desc: Defines which passes are run, and runs them
$end_info$
*/
#include "Interface/IR/PassManager.h"
#include "Interface/IR/Passes.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -7,10 +15,17 @@
namespace FEXCore::IR {
void PassManager::AddDefaultPasses(bool InlineConstants, bool StaticRegisterAllocation) {
FEXCore::Config::Value<bool> DisablePasses{FEXCore::Config::CONFIG_DEBUG_DISABLE_OPTIMIZATION_PASSES, false};
FEX_CONFIG_OPT(DisablePasses, O0);
if (!DisablePasses()) {
InsertPass(CreateContextLoadStoreElimination());
if (Is64BitMode()) {
// This needs to run after RCLSE
// This only matters for 64-bit code since these instructions don't exist in 32-bit
InsertPass(CreateLongDivideEliminationPass());
}
InsertPass(CreateDeadStoreElimination());
InsertPass(CreatePassDeadCodeElimination());
InsertPass(CreateConstProp(InlineConstants));
+9
View File
@@ -1,5 +1,12 @@
/*
$info$
tags: ir|opts
$end_info$
*/
#pragma once
#include <FEXCore/Config/Config.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/IR/IREmitter.h>
@@ -77,6 +84,8 @@ private:
ValidationPasses.emplace_back(Pass);
}
#endif
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
};
}
+1
View File
@@ -14,6 +14,7 @@ FEXCore::IR::Pass* CreatePassDeadCodeElimination();
FEXCore::IR::Pass* CreateIRCompaction();
FEXCore::IR::RegisterAllocationPass* CreateRegisterAllocationPass(FEXCore::IR::Pass* CompactionPass, bool OptimizeSRA);
FEXCore::IR::Pass* CreateStaticRegisterAllocationPass();
FEXCore::IR::Pass* CreateLongDivideEliminationPass();
namespace Validation {
FEXCore::IR::Pass* CreateIRValidation();
+17 -10
View File
@@ -1,3 +1,11 @@
/*
$info$
tags: ir|opts
desc: ConstProp, ZExt elim, addressgen coalesce, const pooling, fcmp reduction, const inlining
$end_info$
*/
#if defined(_M_ARM_64)
//aarch64 heuristics
#include "aarch64/assembler-aarch64.h"
@@ -158,13 +166,8 @@ bool ConstProp::Run(IREmitter *IREmit) {
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
auto Header = CurrentIR.GetHeader();
auto OriginalWriteCursor = IREmit->GetWriteCursor();
auto HeaderOp = CurrentIR.GetHeader();
{
// constants are pooled per block
@@ -197,7 +200,8 @@ bool ConstProp::Run(IREmitter *IREmit) {
auto SelectOp = SelectOpHdr->CW<IR::IROp_Select>();
// the value isn't used after the select otherwise
if (SelectOpHdr->Op == OP_SELECT && SelectOpNode->NumUses == 1
// make sure the sizes match
if (SelectOpHdr->Size == UnaryOpHdr->Size && SelectOpHdr->Op == OP_SELECT && SelectOpNode->NumUses == 1
&& IREmit->IsValueConstant(SelectOp->TrueVal)
&& IREmit->IsValueConstant(SelectOp->FalseVal)) {
@@ -256,7 +260,7 @@ bool ConstProp::Run(IREmitter *IREmit) {
auto ghf = IROp->CW<IR::IROp_GetHostFlag>();
auto fcmp = IREmit->GetOpHeader(ghf->GPR)->CW<IR::IROp_FCmp>();
LogMan::Throw::A(fcmp->Header.Op == OP_FCMP || fcmp->Header.Op == OP_F80CMP, "Unexpected OP_GETHOSTFLAG source");
LOGMAN_THROW_A(fcmp->Header.Op == OP_FCMP || fcmp->Header.Op == OP_F80CMP, "Unexpected OP_GETHOSTFLAG source");
if(fcmp->Header.Op == OP_FCMP) {
fcmp->Flags |= 1 << ghf->Flag;
}
@@ -459,7 +463,7 @@ bool ConstProp::Run(IREmitter *IREmit) {
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) &&
IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
LOGMAN_MSG_A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
}
break;
}
@@ -475,7 +479,7 @@ bool ConstProp::Run(IREmitter *IREmit) {
uint64_t Constant1;
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
LOGMAN_MSG_A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
}
break;
}
@@ -743,8 +747,11 @@ bool ConstProp::Run(IREmitter *IREmit) {
}
}
}
break;
}
default: break;
default:
break;
}
}
Loaded 100 of 338 files, more files were not shown because too many files have changed in this diff. Show more