Compare commits

...
168 Commits
Author SHA1 Message Date
Ryan Houdek 448c4ec797 Docs: Update for release FEX-2106 2021-06-10 12:59:56 -07:00
Ryan Houdek 08fad8fb0d Merge pull request #1067 from Sonicadvance1/fix_32bit_crash
Fixes 32-bit applications crashing
2021-06-10 12:57:21 -07:00
Ryan Houdek 29083a0b41 Makes sure the TestHarnessRunner also hits the shutdown path 2021-06-10 12:47:05 -07:00
Ryan Houdek a1f4ca873c Have FEXLoader use the new freeing functions
It now calls the new functions for handling a cleaner shutdown
2021-06-10 12:47:05 -07:00
Ryan Houdek c3b0e4820b Allocates the signal handlers altstack using FEX's allocator
Using malloc and free was causing a bad crash
2021-06-10 12:47:05 -07:00
Ryan Houdek bb96955e05 Switches from FILE to raw int for Logging message
Due to our current allocation strategy. This variable was ending up in a weird state
where jemalloc allocated it using the glibc allocator.
On shutdown this was causing it to try and deallocate through FEX's allocator...Which
is very broken.

This lets linux clean up in this case, at least until the allocator lines are more
strongly written
2021-06-10 12:47:05 -07:00
Ryan Houdek 16bb64aaab Merge pull request #1063 from Sonicadvance1/fix_crash
Fixes crash in 32-bit pselect6 and pselect6_time64
2021-06-10 12:41:08 -07:00
Ryan Houdek b9e53b6c46 Merge pull request #1069 from lioncash/unex
Syscalls/Thread: Replace std::unexpected with std::terminate
2021-06-09 20:11:58 -07:00
Lioncash 616aa46c6b Syscalls/Thread: Replace std::unexpected with std::terminate
std::unexpected was deprecated in C++11 and removed from the standard in
C++17. The default unexpected_handler calls std::terminate, so this is
identical behavior.
2021-06-09 22:59:21 -04:00
Ryan Houdek c98fb7ae45 Merge pull request #1068 from lioncash/unique
Core: return unique_ptr by default for cores
2021-06-09 19:19:22 -07:00
Lioncash 6c5abc8819 Core: Make CustomCPUFactory instances return unique_ptr
Makes the ownership intentions of the function explicit.
2021-06-09 21:53:07 -04:00
Lioncash f36726ecf6 Core: return unique_ptr by default for cores
Communicates ownership semantics in the API.
2021-06-09 21:41:43 -04:00
Ryan Houdek 6d20ae5dc6 Merge pull request #1062 from lioncash/parser
IRParser: Minor cleanup
2021-06-09 18:34:27 -07:00
Ryan Houdek e469031c40 Merge pull request #1064 from lioncash/syscall
x32/FD: Construct vectors in place
2021-06-09 18:26:08 -07:00
Ryan Houdek b9a8c383fd Merge pull request #1065 from lioncash/poll
{x32, x64}/EPoll: Prevent edge-case out-of-bounds access scenarios
2021-06-09 18:19:25 -07:00
Ryan Houdek 57df4c6d61 Merge pull request #1066 from lioncash/signal
SignalDelegator: Minor changes
2021-06-09 18:17:55 -07:00
Ryan Houdek c5cfb45f48 In ELFCodeLoader2, changes Sections variable over to a unique_ptr
This class is partially allocated on either side of the allocator fence.
Moves `Sections` over to a unique_ptr so we can clear this variable prior to shutdown.
The rest of the class is allocated prior to switching to FEX allocator
2021-06-09 18:05:57 -07:00
Ryan Houdek b6417153d7 Hook more glibc hooks and allow clearing hooks in Allocator
Due to a disjoint mechanism inside of glibc we need to override both the glibc
publicly visible allocation functions AND the hooks.

We had broken the overriding when we changed the default visibility. So first fix that.
Then only override to FEX's allocators once we are ready for it.
Then also replace the hooks.

Additionally have a way to clear the hooks back to default.
2021-06-09 18:03:27 -07:00
Ryan Houdek 599b579192 Adds a ShutdownStaticTables context function
Due to a mixture of allocators this needs to be shutdown in the correct location.
A little bit dirty but is necessary without a full refactor
2021-06-09 18:00:54 -07:00
Lioncash 9ed0572fa4 IRParser: Make use of unique_ptr for Parse()
Communicates ownership semantics to the user of the API a little better,
and makes it harder to accidentally leak memory.
2021-06-09 21:00:30 -04:00
Lioncash 00b0222129 IRParser: Make use of find character overloads
Results in a tiny bit better codegen. May as well, since the changes are
essentially 'free'.
2021-06-09 21:00:30 -04:00
Lioncash 32cc98856f IRParser: Add missing default return in DecodeErrorToString()
Prevents undefined behavior in the event anything actually reaches this.
2021-06-09 21:00:30 -04:00
Lioncash dee7441795 IRParser: Make input string to DecodeValue a const reference
No specializations modify this, and even if they did, it would be a
little confusing to statefully modify the string this way.

Instead, we can make it read-only.
2021-06-09 21:00:29 -04:00
Lioncash f6a6f2eac8 x32/FD: Construct vectors in place
The syscall is defined as taking in an array, so we can construct the
std::vector in place and surround it.

This also fixes an edge case where an out of bounds access on the
Host_iovec vectors could occur if these syscalls were called with an
iovcnt of zero (.at would cause an exception to be thrown).

Looking into the syscalls for readv and writev, these just return early
if a size of zero is passed in.
2021-06-09 21:00:01 -04:00
Lioncash c10e139964 x64/EPoll: Prevent edge-case out-of-bounds access scenarios
Similarly to #1064, an application could potentially pass in bad values
that could result in assertions/exceptions being thrown, so we can
guard against that.

Despite the manual saying that applications *must* not pass
maxevents less than or equal to zero, against all odds, this is
still a handled case in the kernel, so some application out there
may rely on this behavior.
2021-06-09 20:59:44 -04:00
Lioncash 379b200d05 x32/EPoll: Prevent edge-case out-of-bounds access scenarios
Similarly to #1064, an application could potentially pass in bad values
that could result in assertions/exceptions being thrown, so we can guard
against that.

Despite the manual saying that applications *must* not pass maxevents
less than or equal to zero, against all odds, this is still a handled
case in the kernel, so some application out there may rely on this behavior.
2021-06-09 20:59:44 -04:00
Lioncash b4cde28137 SignalDelegator: Move virtual destructor to base class
Ensures that destruction behavior will always handle the polymorphic
case, no matter where it occurs in the hierarchy.
2021-06-09 20:59:21 -04:00
Lioncash 3f88150581 SignalDelegator: Make std::vector into a constexpr array
Minor change, but gets rid of some heap usage.
2021-06-09 20:59:20 -04:00
Lioncash e95076f321 SignalDelegator: Migrate from std::unexpected() to std::terminate()
std::unexpected() was deprecated in C++11 and removed from the standard
in C++17. The default unexpected_handler would call std::terminate
anyway, and given we don't explicitly set a termination handler (as far
as I can tell), this retains identical behavior.
2021-06-09 20:59:20 -04:00
Lioncash e2a16c53b6 SignalDelegator: std::move std::function instances
std::function is allowed to allocate if the size of captures exceeds its
internal storage buffer. It's unlikely this is regularly going to be the
case, but we can allow for avoiding it where necessary.
2021-06-09 20:59:20 -04:00
Lioncash ee89b7a794 SignalDelegator: Leverage std::array
Allows for better detection of out of bounds accesses, as library
implementations generally allow conditional enabling of bounds checks
through preprocessor defines.
2021-06-09 20:59:20 -04:00
Ryan Houdek 5ac9b45528 Merge pull request #1059 from Sonicadvance1/fix_rounding
Fixes floating point conversion rounding bugs
2021-06-09 17:58:35 -07:00
Ryan Houdek 6a94011b00 Fixes crash in 32-bit pselect6 and pselect6_time64
The kernel can be provided a sigmaskpack pointer without a sigset inside of it.

This was causing a crash in some random applications
2021-06-08 21:20:16 -07:00
Ryan Houdek 104fabc11f Merge pull request #1061 from lioncash/dump
IRParser: Make use of fmt where applicable
2021-06-08 07:10:10 -07:00
Ryan Houdek 15b2eb48e5 Disable rounding tests that only fail on Nvidia Xavier
Spooky rounding behaviour changes.
2021-06-08 06:54:11 -07:00
Ryan Houdek 62c446a5f9 Adds more unit tests for conversion operations 2021-06-08 06:54:11 -07:00
Ryan Houdek d57a034220 Fixes floating point conversion rounding bugs
When floats and doubles were converting to integers we weren't doing the correct transformation.
AArch64 provides direct ops for all four of the op types. So lets use them
  - f32 -> int64
  - f32 -> int32
  - f64 -> int64
  - f64 -> int32

Doesn't fully fix the case of overflow for AArch64 since overflow behaviour is different.
x86 returns 0x8000'0000 or 0x8000'0000'0000'0000 while AArch64 saturates to the maximum signed
integers. Can't work around that without checking overflow flags.

This does solve the typical case though.
Fixes audio problems in all FMod games.
2021-06-08 06:54:10 -07:00
Ryan Houdek 18fe049976 Extends a couple tests to ensure correct zext 2021-06-08 06:54:08 -07:00
Stefanos Kornilios Mitsis Poiitidis c6e09ddec5 Merge pull request #1046 from Sonicadvance1/more_sse41
Implements SSE4.1
2021-06-08 16:52:12 +03:00
Lioncash 05ee4c363b IRParser: Make use of fmt where applicable 2021-06-08 09:47:13 -04:00
Lioncash 6f2f4a9bc0 IRDumper: Make use of std::string_view over std::string where applicable
Same behavior, but with a smaller footprint (and the ability to be
constant data).
2021-06-08 09:36:59 -04:00
Ryan Houdek f699726409 Merge pull request #1060 from lioncash/byte
Utils: Add header for bit-related utilities
2021-06-08 05:40:05 -07:00
Lioncash a2b8fedba5 Utils: Add header for bit-related utilities
Places a layer of separation around the remaining compiler builtins.
2021-06-08 08:25:32 -04:00
Ryan Houdek ed2336c4f2 Disables some new sse4.1 gcc tests
The SSE 4.1 path of these tests seemingly work, it's the setup code before the
test that is broken.

We will need to find out why these are failing later
2021-06-08 02:44:40 -07:00
Ryan Houdek 748c3182a7 Fixes a typo in interpreter LSHL
This was causing uint32_t pointers to sign extend on 32-bit
New tests being run were hitting this when we were expecting them to zero extend
2021-06-08 02:44:40 -07:00
Ryan Houdek dfc454873c Moves AOTIR cache queue object to be inside the context
This fixes a crash that occurs due to mixing memory allocators.
This PR has tickled the allocation just enough that it broke
2021-06-08 02:44:40 -07:00
Ryan Houdek 1b06a06a4e Makes RoundType a unique type
This allows us to have the IRDumper have unique output for the type
2021-06-08 02:44:40 -07:00
Ryan Houdek b2ad8d73ac Adds unit test for FEX frontend decoder
This could potentially show up as being ch instead of edi.
Ensure we don't regress behaviour in the future
2021-06-08 02:44:39 -07:00
Ryan Houdek fec078f924 Fixes bug in interpreter VInsGPR
If the source GPR had data that was larger than the element it would overwrite other elements
Mask it correctly. This then matches behaviour with the other CPU backends
2021-06-08 02:44:39 -07:00
Ryan Houdek 38f1cded2c Implements unit tests for all the SSE4.1 instructions 2021-06-08 02:44:39 -07:00
Ryan Houdek a313253510 Enables SSE 4.1 in CPUID 2021-06-08 02:44:39 -07:00
Ryan Houdek 7b2b2a3d07 Implements all the remaining SSE4.1 instructions
Theres a fair number of these so I won't describe them all.

A couple highlights are MPSADBW and PHMINPOSUW.
These don't really match with AArch64 very well so their IR is a bit ugly.
2021-06-08 02:44:39 -07:00
Ryan Houdek 910a25b0ed Cleans up some vector ops that don't need dedicated functions
All of these ops can use the VectorALUOp and VectorUnaryOp templated functions
2021-06-08 02:44:39 -07:00
Ryan Houdek 75bae81331 Describe the remaining SSE4.1 ops in the tables 2021-06-08 02:44:39 -07:00
Ryan Houdek 02abbd216a Fixes an issue with debug printing vectors
We need to save the vector registers otherwise we will corrupt them.
Also in the case of printing a vector register, fall down the specialized path

Only useful when debugging
2021-06-08 02:44:39 -07:00
Ryan Houdek 8b7a7e91c9 Implements new IR ops
Adds VBic, VUMinV, VPopcount, VUnZip, VUnZip2, VDupElement, Vector_FToI, and VUABDL

We will need these for the SSE4.1 ops
2021-06-08 02:44:39 -07:00
Ryan Houdek 130f82a310 Merge pull request #1058 from lioncash/builtins
General: Place more compiler specifics into CompilerDefs.h
2021-06-07 05:31:07 -07:00
Lioncash 18e0f2636f General: Make use of the <bit> header where applicable
Since C++20, a bunch of bit manipulation functions finally have a common
interface, so lets make use of those
2021-06-07 08:16:51 -04:00
Lioncash 2b9029623e General: Abstract trapping behind a define
Provides a layer of separation from direct usages of compiler builtins.
2021-06-07 06:18:44 -04:00
Lioncash 6798afe91f General: Abstract unreachable behind a define
Provides a layer of separation from direct use of compiler built-ins.
2021-06-07 06:13:26 -04:00
Ryan Houdek 4fa8522d9e Merge pull request #1057 from lioncash/cprop
ConstProp: Separate out constituent chunks of const prop
2021-06-05 14:08:40 -07:00
Lioncash 58272ffc47 ConstProp: Separate out constituent chunks of const prop
Makes it nicer to find where each part of the pass is, and also see how
the pass is operating at a high level.
2021-06-05 16:52:03 -04:00
Ryan Houdek 4c275ecc08 Merge pull request #1056 from lioncash/compiler-defs
Utils: Add CompilerDefs.h for compiler-specifics
2021-06-05 12:57:51 -07:00
Ryan Houdek 294ec80aaf Merge pull request #1055 from lioncash/enum-cls
InternalThreadState: Make SignalEvent an enum class
2021-06-05 12:55:10 -07:00
Ryan Houdek e78d3d1a80 Merge pull request #1054 from lioncash/fmt-1
GdbServer: Migrate logging/string handling to fmt where applicable
2021-06-05 12:54:11 -07:00
Lioncash 262389f4ea ConstProp: Mark internal functions as static
Allows them to have internal linkage.
2021-06-05 15:25:35 -04:00
Lioncash 341b811642 Utils: Add CompilerDefs.h for compiler-specifics
Puts a layer of separation around compiler specifics (and also makes
them nicer to write).
2021-06-05 13:55:54 -04:00
Lioncash 32744d074e InternalThreadState: Make SignalEvent an enum class
Makes the enumeration strongly typed, preventing implicit conversions,
minimizing the potential chances of an invalid value being used by
accident.
2021-06-05 10:52:56 -04:00
Lioncash 5f91bbe28d GdbServer: Migrate code to fmt
Simplifies a bunch of string manipulation code.

Much nicer to grok than stream formatting in many cases.
2021-06-05 10:34:59 -04:00
Ryan Houdek 69035348f5 Merge pull request #1053 from lioncash/alias
CodeLoader: Add type aliases for mapper and unmapper functions
2021-06-05 06:16:27 -07:00
Lioncash be46db10e4 CodeLoader: Add virtual destructor
Ensures that no matter the context the hierarchy tree is used
polymorphically, that the deallocation will always be well-defined.

Gets rid of a potential bug vector.
2021-06-05 08:54:09 -04:00
Lioncash aa2c18d8cc CodeLoader: Add type aliases for mapper and unmapper functions
Centralizes the long types in one place for less reading.
2021-06-05 08:51:40 -04:00
Ryan Houdek 0a7f0a3441 Merge pull request #1052 from lioncash/fmtimpl
LogManager: Add fmt-capable logging functions
2021-06-05 05:46:56 -07:00
Lioncash ca2b04b309 LogManager: Add fmt-capable logging functions
Addresses #146 a little more by providing an interface to perform
fmt-compatible logging.

No more, will people on the project be tormented by classic printf
features like:

- Accidentally passing in a non-trivial type
- PRI macros
- Not being able to add support for custom types
- Mixing up signed/unsigned printf formatting specifiers accidentally

fmt-capable versions of the logging functions are named the same as the
existing functions, just with a "Fmt" or _FMT suffix (depending on
whether or not it's a function being used or a macro, respectively).
2021-06-05 08:29:48 -04:00
Ryan Houdek 376892793f Merge pull request #1051 from lioncash/view
IRParser: Convert array of std::string over to array of std::string_view
2021-06-05 04:05:28 -07:00
Stefanos Kornilios Mitsis Poiitidis 61f73cf0bf Merge pull request #1050 from Sonicadvance1/fix_semctl_shmctl
Fixes semctl and msgctl
2021-06-05 13:54:30 +03:00
Stefanos Kornilios Mitsis Poiitidis 9716a4f0cf Merge pull request #1049 from Sonicadvance1/drm_headers
Adds drm headers to an external repository
2021-06-05 13:53:16 +03:00
Lioncash 35f3777797 IRParser: Make use of insert_or_assign where applicable
Avoids some minor potential default constructions that get overwritten
immediately.
2021-06-05 06:43:17 -04:00
Lioncash d73b79470e IRParser: Mark parameter of CheckPrintError as a const reference
Def is only ever accessed to read members, so we can signify to the
reader to not expect it to be modified.
2021-06-05 06:43:16 -04:00
Lioncash 676d9cb665 IRParser: std::move elements where advantageous
e.g. LineDefinitions are moderately beefy, they contain
two strings and a vector of strings among other things,
so we can move instances into their containing vector to avoid some
allocation churn.
2021-06-05 06:43:13 -04:00
Lioncash 993b4513d9 IRParser: Convert array of std::string over to array of std::string_view
Same behavior, but allows the arrays to be constexpr (and use less
space; 16 bytes vs 32 bytes per element).
2021-06-05 06:16:42 -04:00
Ryan Houdek 2958744777 Fixes semctl and msgctl
There were some problems in both of these implementations.

Fixes #744
Fixes #745
2021-06-04 23:40:24 -07:00
Ryan Houdek 2e8aacffe6 Fixes compat_ptr to return reference instead of copy
The expectation was that a reference would be returned rather than a copy.
Oops
2021-06-04 23:39:13 -07:00
Ryan Houdek 2101914c9d Adds drm headers to an external repository
It's highly likely that the host system won't have these headers installed.
Carry them in an external repository to ensure they are available.

Fixes #1047
2021-06-04 21:26:45 -07:00
Ryan Houdek 97c4ba018b Merge pull request #1048 from lioncash/lookup
Config: Avoid a few minor string copies where trivially possible
2021-06-04 17:13:10 -07:00
Lioncash 525d50f07e config: Pass strings by const reference where applicable
In a few cases, the strings aren't ever directly modified, so they can
be passed by reference to eliminate a few trivial copies.
2021-06-04 12:46:34 -04:00
Lioncash 5cec8ddca0 config: Move input strings in Loader constructors
Eliminates a copy, minor, but basically a "free" change.
2021-06-04 12:40:38 -04:00
Lioncash 39b2be15ab config: Make use of heterogenous map lookup
Same behavior, but allows lookups without constructing a std::string
(in most cases find() inputs use const char*, so this gets rid of some
string churn).
2021-06-04 12:36:54 -04:00
Ryan Houdek 83bf79ab42 Merge pull request #1045 from lioncash/default
IR: Make use of defaulted operator==
2021-06-03 22:01:58 -07:00
Lioncash 1f97a2baa9 IR: Make use of defaulted operator==
Same behavior, but allows both operator== and operator!= to be
automatically generated with a single declaration.
2021-06-04 00:52:25 -04:00
Ryan Houdek 0c2344166c Merge pull request #1044 from lioncash/moves
Config: Move strings where applicable
2021-06-03 18:33:00 -07:00
Lioncash 0bf30e2024 Config: Simplify qualifiers
These functions are part of the same class, so we can use the function
names directly without qualifiers.
2021-06-03 21:06:14 -04:00
Lioncash 933cdf76b4 Config: Move strings where applicable
Noticed when adding amending missing const qualifiers on interfaces. We
can make use of std::move here to avoid potential allocation churn a
little.

While we're at it, we can implement EraseSet in terms of, well, Erase()
and Set().
2021-06-03 21:03:19 -04:00
Ryan Houdek 36a77bb396 Merge pull request #1043 from lioncash/const
General: Add missing const specifiers where applicable
2021-06-03 17:40:13 -07:00
Lioncash 1936ebc59c General: Add missing const specifiers where applicable
Minor change that adds missing const specifiers to getters that don't
modify internal class state.
2021-06-03 20:24:27 -04:00
Ryan Houdek 9838309560 Merge pull request #1042 from lioncash/strong
DecodedOperand: Convert operand type into an enum class
2021-06-02 05:01:56 -07:00
Lioncash 37e2210f64 DecodedOperand: Convert operand type into an enum class
Now possible in a less messy way, since the type is now centralized in
one location.

Makes it strongly typed and prevents any potential accidental implicit
assignments to Type instead of something intended for the Data members.
2021-06-02 06:51:15 -04:00
Ryan Houdek 71c32c0135 Merge pull request #1041 from lioncash/info
DecodedOperand: Add helper functions for type testing
2021-06-02 03:10:49 -07:00
Lioncash 1f0bc49e54 DecodedOperand: Add helper functions for type testing
Significantly shortens the amount of code necessary for testing the type
of a decoded operand.

Also relocates the type field out of all the union types to have it in a
central location.
2021-06-02 05:59:12 -04:00
Lioncash 718f7ef8e6 X86InstInfo: Add operator!=
Provides logical symmetry.
2021-06-02 05:44:31 -04:00
Ryan Houdek edd1dfdfe8 Merge pull request #1040 from Sonicadvance1/more_syscalls_5_12
Implements more syscalls for supporting a higher guest kernel version
2021-06-01 00:27:50 -07:00
Ryan Houdek 4243ed19a4 Merge pull request #1037 from Sonicadvance1/fexconfig_downgrade
FEXConfig: Allow lower GL versions
2021-06-01 00:14:04 -07:00
Scott Mansell 16467c0fb7 Merge pull request #1038 from Sonicadvance1/implement_insertps
Implements SSE4.1 insertps
2021-06-01 19:04:53 +12:00
Ryan Houdek 0b7587768d Unifies 32bit and 64bit clone implementation
Syscall entry points still have different argument orders,
Moves the arguments to the clone3 argument structure and passes to generic handler.
Also implements clone3 while doing this
2021-05-27 23:00:00 -07:00
Ryan Houdek d115a57fe6 Implements support for execveat 2021-05-27 22:59:57 -07:00
Ryan Houdek 4c74478610 Implements iouring syscalls
This is a very simple initial implementation.
io_uring allows some things that are hard to capture like setting personalities.

Assume sane usage for now
2021-05-27 22:59:55 -07:00
Ryan Houdek 77a111cf19 Implements process_madvise 64-bit syscall 2021-05-27 22:59:52 -07:00
Ryan Houdek 7e551e5f88 Implements epoll_pwait2 syscall 2021-05-27 22:59:49 -07:00
Ryan Houdek 9d44dfdca6 Actually updated the hardcoded guest kernel locations
Just a couple of files and uname syscall
2021-05-27 22:59:47 -07:00
Ryan Houdek a7858f4d37 Calculates a guest kernel version for FEX instead
Takes the host kernel version and makes sure it fits in our supported kernel range
Minimum kernel version FEX reports to the guest is 5.0
Maximum kernel version FEX reports to the guest is currently 5.12
2021-05-27 22:59:44 -07:00
Ryan Houdek 6b3b10f8ae Implements pidfd_send_signal syscall
Now that we know when to forward a siginfo_t to the guest, we can allow this syscall
2021-05-27 22:59:42 -07:00
Ryan Houdek 9d5fa64f68 Implements pidfd_open syscall 2021-05-27 22:59:39 -07:00
Ryan Houdek 8b3bbd0ea1 Implements openat2 2021-05-27 22:59:37 -07:00
Ryan Houdek cc477b6964 Implements close_range syscall 2021-05-27 22:59:34 -07:00
Ryan Houdek 7a64bba8c4 Removes check for ThreadState being standard layout
Latest clang and libstdc++ makes unique_ptr not be standard layout.
Results in a compile error
2021-05-27 22:59:31 -07:00
Ryan Houdek 2d342d4662 Handle user provided siginfo_t with user signal
If the guest has sent a signal and the si_code is SI_USER then
we need to pass that siginfo_t through without touching it.
User could be sticking whatever they want in to that struct
2021-05-27 22:59:27 -07:00
Stefanos Kornilios Mitsis Poiitidis 7b41808806 Merge pull request #1039 from Sonicadvance1/pidfd_getfd
Implements pidfd_getfd syscall
2021-05-28 07:30:30 +03:00
Ryan Houdek 912d019ad8 Implements pidfd_getfd syscall
Rise of the Tomb Raider launcher uses this without checking host kernel version.
Might be part of their crash handler.
2021-05-27 18:44:12 -07:00
Ryan Houdek 1f9405b880 Implements insertps unit test 2021-05-26 21:41:16 -07:00
Ryan Houdek 211e7bf0f0 Implements SSE4.1 insertps
Cheap Golf is using this instruction unconditionally.
With this implemented the game now runs
2021-05-26 21:40:39 -07:00
Ryan Houdek 822a08f271 FEXConfig: Allow lower GL versions
Automatically fall back through older GL versions

This allows us to freely support GL 3.0, 2.1 and ES 2.0.
Should fix an issue where Pi devices don't support GL 3.0 in all configs

External imgui had to be updated to fix an issue with ES 2.0

Fixes #1036
2021-05-26 18:45:03 -07:00
Ryan Houdek 6cba775d4e Merge pull request #1032 from FEX-Emu/skmp/aotir-gen-mt
Multi threaded AOTGen
2021-05-24 23:06:13 -07:00
Ryan Houdek 8b5873061a Merge pull request #1033 from lioncash/fmtlib
Externals: Add fmtlib as an external
2021-05-20 14:58:33 -07:00
Lioncash 37a33bf127 Externals: Add fmt as an external
Begins the process of addressing issue #146.

This is separated off, so that others can make use of fmt for other
purposes (general localized non-sucky string formatting), while the
logging rework is being tackled.
2021-05-20 13:18:38 -04:00
Stefanos Kornilios Mitsis Poiitidis d70c91ddc6 AOTIR: Multi-threaded AOTGen 2021-05-19 14:19:55 +03:00
Stefanos Kornilios Mitsis Poiitidis 370f36c8f7 Merge pull request #1027 from Sonicadvance1/fix_ppoll
ppoll fixes
2021-05-18 09:20:32 +03:00
Ryan Houdek 42bb27b1fb Merge pull request #1025 from Sonicadvance1/ignore_non_canonical
Ignore non-canonical addresses in FS/GS setting
2021-05-12 22:31:09 -07:00
Ryan Houdek f522303837 gvisor: arch_prctl now passes 2021-05-12 22:17:13 -07:00
Ryan Houdek 3d09a55715 Ignore non-canonical addresses in FS/GS setting 2021-05-12 22:17:13 -07:00
Ryan Houdek c103f54774 Merge pull request #1028 from Sonicadvance1/remove_syscall_forwards
Removes syscall forward errno preprocessor implementation
2021-05-12 22:04:30 -07:00
Ryan Houdek dc7437b8c6 Merge pull request #1018 from FEX-Emu/skmp/streamable-aotir
AOTIR: .aotir files are now streamed out
2021-05-12 22:04:17 -07:00
Ryan Houdek b45503f93c gvisor: chown and sync tests now pass 2021-05-12 21:54:08 -07:00
Ryan Houdek 8cf1b0263d Removes syscall forward errno preprocessor implementation
This was causing issues with syscalls returning errors.
Remove it and move on
2021-05-12 21:53:45 -07:00
Stefanos Kornilios Mitsis Poiitidis 7ecaf24e8b Merge pull request #1030 from Sonicadvance1/disable_userfaultfd
Disable userfaultfd until supported
2021-05-12 09:45:43 +03:00
Stefanos Kornilios Mitsis Poiitidis 52f7ea5433 Merge pull request #1029 from Sonicadvance1/fix_fadvise64
Fix fadvise64
2021-05-12 09:45:21 +03:00
Stefanos Kornilios Mitsis Poiitidis 023a32ea26 Merge pull request #1026 from Sonicadvance1/fix_flag_remapping
Handle FD flag remapping correctly
2021-05-12 09:39:39 +03:00
Stefanos Kornilios Mitsis Poiitidis 36980131e0 Merge pull request #1024 from Sonicadvance1/signal_fixes
Sigaction fixes
2021-05-12 09:34:51 +03:00
Stefanos Kornilios Mitsis Poiitidis b2eb13fa1c Merge pull request #1023 from Sonicadvance1/emulate_map_32bit
Emulate MAP_32BIT in mmap
2021-05-12 09:34:18 +03:00
Stefanos Kornilios Mitsis Poiitidis 27b6497bee Merge pull request #1031 from Sonicadvance1/fix_epoll
Use epoll syscalls directly
2021-05-12 09:33:59 +03:00
Stefanos Kornilios Mitsis Poiitidis 66ee89ec8a Merge pull request #1022 from Sonicadvance1/fix_uname
Fix uname
2021-05-12 09:22:58 +03:00
Stefanos Kornilios Mitsis Poiitidis bee73199e1 Merge pull request #1021 from Sonicadvance1/fix_thread_self
Support redirecting thread-self exe softlink
2021-05-12 09:22:36 +03:00
Stefanos Kornilios Mitsis Poiitidis 0e6db843ef Merge pull request #1020 from Sonicadvance1/invalid_syscall
Return ENOSYS on too large of syscall number
2021-05-12 09:21:17 +03:00
Stefanos Kornilios Mitsis Poiitidis b45538f01e Merge pull request #1019 from Sonicadvance1/fix_emufd
Fix two EmuFD issues
2021-05-12 09:20:53 +03:00
Ryan Houdek f8cf98d378 gvisor: fadvise64 test now passes 2021-05-11 18:55:48 -07:00
Ryan Houdek 796c5ccbc9 gvisor: sigaction_test now passes 2021-05-11 18:54:08 -07:00
Ryan Houdek b157a5a0fb gvisor: bad_test now passes 2021-05-11 18:53:22 -07:00
Ryan Houdek 783ceb2d56 Use epoll syscalls directly 2021-05-11 18:51:06 -07:00
Ryan Houdek d1fc65daaa Disable userfaultfd until supported
Until we properly wrap this we can't support it
2021-05-11 18:44:31 -07:00
Ryan Houdek 94f464e1a4 Fix fadvise64
posix variant still doesn't quite match the actual syscall implementation
2021-05-11 18:39:00 -07:00
Ryan Houdek dfb15aaeb9 ppoll fixes
glibc implementation makes a copy of the timeout and the kernel is expected to update it
Can't use the glibc implementation because of this.
2021-05-11 18:33:24 -07:00
Ryan Houdek 565f0e6af3 Handle FD flag remapping correctly
FD flag remapping was broken. It would remap one flag on to another and then the next check would remap it back.

Instead keep a mask of the flags to be remapped then remap them all at the end.
Also goes through the ops and fixes a few cases where it was remapping wrong flags.
2021-05-11 18:29:21 -07:00
Ryan Houdek b03554bb7d Sigaction fixes
Check for invalid sigsetsize

Since we are using SignalDelegator we don't use errno, so just return Result and check for Result == 0
2021-05-11 18:22:23 -07:00
Ryan Houdek 7b8cb5107a Emulate MAP_32BIT in mmap
If we are on AArch64 then MAP_32BIT doesn't exist.
Emulate it by setting the address as a hint to the kernel so it scans bottom up
2021-05-11 18:18:59 -07:00
Ryan Houdek cbb20ef6c6 Fix uname
There is a domainname variable that was missed
2021-05-11 18:16:35 -07:00
Ryan Houdek 298481f9af Support redirecting thread-self exe softlink 2021-05-11 18:15:14 -07:00
Ryan Houdek 0d108cd8fd Return ENOSYS on too large of syscall number 2021-05-11 18:14:02 -07:00
Ryan Houdek 490d7e57d7 Arguments file needs one additional null argument to finish the arguments. 2021-05-11 18:12:16 -07:00
Ryan Houdek fe77c3ef06 Fix cpuinfo needing tabs on its options and PM option
Depending on option it is split by anywhere from zero to two tabs
2021-05-11 18:12:16 -07:00
Ryan Houdek eb02afe952 Merge pull request #1013 from Sonicadvance1/fix_openat_symlinks
Handles symlinks in rootfs in openat
2021-05-11 17:52:07 -07:00
Stefanos Kornilios Mitsis Poiitidis b8dc63754b AOTIR: Don't keep IR,RA,Code data around when aotirgenerating 2021-05-11 15:15:39 +03:00
Stefanos Kornilios Mitsis Poiitidis df8f1850ae AOTIR: .aotir files are now stream-written 2021-05-11 14:37:12 +03:00
Ryan Houdek e282fd2221 Merge pull request #1016 from Sonicadvance1/ioctl32_micro_optimization
Microoptimization for the DRM ioctls
2021-05-10 21:14:16 -07:00
Stefanos Kornilios Mitsis Poiitidis 3147f0d84f Merge pull request #1017 from Sonicadvance1/fix_x86_jitsymbols
Fixes JITSymbols for x86-64 JIT
2021-05-10 00:36:30 +03:00
Stefanos Kornilios Mitsis Poiitidis 4c02c9f037 Merge pull request #1015 from Sonicadvance1/fix_inotify_thread
FEXConfig: Fixes INotify watcher never coming up
2021-05-10 00:34:27 +03:00
Ryan Houdek 83b4188569 Microoptimization for the DRM ioctls
For DRM applications have a three FD deep MRU cached for faster lookups of FD to DRM handlers.
In a completely DRM ioctl bound situation like es2gears or GL application without threaded context
Then this puts us /nearly/ at the performance of calling the ioctl32 handler directly.
Sadly there is overhead that can't be overcome so this is the best that can be done from userland
2021-05-06 02:55:22 -07:00
Ryan Houdek 82b04d9886 Fixes JITSymbols for x86-64 JIT 2021-05-06 01:12:51 -07:00
Ryan Houdek 36fdd7f6e2 FEXConfig: Fixes INotify watcher never coming up
Inverted this check on accident
2021-05-05 19:44:31 -07:00
Ryan Houdek 19e45544fb Handles symlinks in rootfs in openat
This fixes an issue where rootfs has symlinks to other things in the rootfs so we need to track it through.

Relies on #1009 to be merged first.
Fixes any application that relies on libblas, mpv for example.
2021-05-04 22:13:52 -07:00
185 changed files with 8097 additions and 2737 deletions

No files matched your search

+6
View File
@@ -33,3 +33,9 @@
[submodule "External/jemalloc"]
path = External/jemalloc
url = https://github.com/FEX-Emu/jemalloc.git
[submodule "External/fmt"]
path = External/fmt
url = https://github.com/fmtlib/fmt.git
[submodule "External/drm-headers"]
path = External/drm-headers
url = https://github.com/FEX-Emu/drm-headers.git
+2
View File
@@ -116,6 +116,8 @@ include_directories(External/jemalloc/pregen/include/)
add_subdirectory(External/cpp-optparse/)
include_directories(External/cpp-optparse/)
add_subdirectory(External/fmt/)
add_subdirectory(External/imgui/)
include_directories(External/imgui/)
+2 -2
View File
@@ -267,7 +267,7 @@ function(AddObject Name Type)
add_dependencies(${Name} IR_INC)
add_dependencies(${Name} CONFIG_INC)
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
target_link_libraries(${Name} pthread vixl dl fmt::fmt 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)
@@ -306,7 +306,7 @@ endfunction()
function(AddLibrary Name Type)
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
target_link_libraries(${Name} pthread vixl dl fmt::fmt 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)
+19 -11
View File
@@ -6,10 +6,13 @@
#include <sys/stat.h>
namespace FEXCore::Paths {
std::string CachePath;
std::string EntryCache;
std::unique_ptr<std::string> CachePath;
std::unique_ptr<std::string> EntryCache;
void InitializePaths() {
CachePath = std::make_unique<std::string>();
EntryCache = std::make_unique<std::string>();
char const *HomeDir = getenv("HOME");
if (!HomeDir) {
@@ -22,29 +25,34 @@ namespace FEXCore::Paths {
char *XDGDataDir = getenv("XDG_DATA_DIR");
if (XDGDataDir) {
CachePath = XDGDataDir;
*CachePath = XDGDataDir;
}
else {
if (HomeDir) {
CachePath = HomeDir;
*CachePath = HomeDir;
}
}
CachePath += "/.fex-emu/";
EntryCache = CachePath + "/EntryCache/";
*CachePath += "/.fex-emu/";
*EntryCache = *CachePath + "/EntryCache/";
// Ensure the folder structure is created for our Data
if (!std::filesystem::exists(EntryCache) &&
!std::filesystem::create_directories(EntryCache)) {
LogMan::Msg::D("Couldn't create EntryCache directory: '%s'", EntryCache.c_str());
if (!std::filesystem::exists(*EntryCache) &&
!std::filesystem::create_directories(*EntryCache)) {
LogMan::Msg::D("Couldn't create EntryCache directory: '%s'", EntryCache->c_str());
}
}
void ShutdownPaths() {
CachePath.reset();
EntryCache.reset();
}
std::string GetCachePath() {
return CachePath;
return *CachePath;
}
std::string GetEntryCachePath() {
return EntryCache;
return *EntryCache;
}
}
+1
View File
@@ -3,6 +3,7 @@
namespace FEXCore::Paths {
void InitializePaths();
void ShutdownPaths();
std::string GetCachePath();
std::string GetEntryCachePath();
}
+1 -1
View File
@@ -83,7 +83,7 @@ namespace FEXCore::Config {
return ConfigFile;
}
std::string GetApplicationConfig(std::string &Filename, bool Global) {
std::string GetApplicationConfig(const std::string &Filename, bool Global) {
std::string ConfigFile = GetConfigDirectory(Global);
if (!Global &&
!std::filesystem::exists(ConfigFile) &&
+12 -4
View File
@@ -14,6 +14,10 @@ namespace FEXCore::Context {
IR::InstallOpcodeHandlers(Mode);
}
void ShutdownStaticTables() {
FEXCore::Paths::ShutdownPaths();
}
FEXCore::Context::Context *CreateNewContext() {
return new FEXCore::Context::Context{};
}
@@ -50,8 +54,8 @@ namespace FEXCore::Context {
CTX->Step();
}
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP) {
CTX->CompileBlock(CTX->ParentThread->CurrentFrame, GuestRIP);
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
Thread->CTX->CompileBlock(Thread->CurrentFrame, GuestRIP);
}
FEXCore::Context::ExitReason RunUntilExit(FEXCore::Context::Context *CTX) {
@@ -149,8 +153,12 @@ namespace FEXCore::Context {
CTX->AOTIRLoader = CacheReader;
}
bool WriteAOTIR(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter) {
return CTX->WriteAOTIRCache(CacheWriter);
void SetAOTIRWriter(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter) {
CTX->AOTIRWriter = CacheWriter;
}
void FinalizeAOTIRCache(FEXCore::Context::Context *CTX) {
CTX->FinalizeAOTIRCache();
}
void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
+27 -16
View File
@@ -1,4 +1,5 @@
#pragma once
#include "Common/JitSymbols.h"
#include "Interface/Core/CPUID.h"
#include "Interface/Core/Frontend.h"
@@ -9,6 +10,7 @@
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/Event.h>
#include <stdint.h>
@@ -20,7 +22,9 @@
#include <optional>
#include <ostream>
#include <set>
#include <shared_mutex>
#include <unordered_map>
#include <queue>
namespace FEXCore {
class ThunkHandler;
@@ -52,14 +56,6 @@ 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;
@@ -67,8 +63,8 @@ namespace FEXCore::Context {
/* RAData followed by IRData */
uint8_t InlineData[0];
IR::RegisterAllocationData *GetRAData();
IR::IRListView *GetIRData();
IR::RegisterAllocationData *GetRAData();
IR::IRListView *GetIRData();
};
struct AOTIRInlineIndexEntry {
@@ -85,6 +81,13 @@ namespace FEXCore::Context {
AOTIRInlineEntry *GetInlineEntry(uint64_t DataOffset);
};
struct AOTIRCaptureCacheEntry {
std::unique_ptr<std::ostream> Stream;
std::map<uint64_t, uint64_t> Index;
void AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t Start, uint64_t Length, uint64_t Hash, FEXCore::IR::IRListView *IRList, FEXCore::IR::RegisterAllocationData *RAData);
};
struct Context {
friend class FEXCore::HLE::SyscallHandler;
#ifdef JIT_ARM64
@@ -121,7 +124,7 @@ namespace FEXCore::Context {
FEX_CONFIG_OPT(DumpIR, DUMPIR);
} Config;
using IntCallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
using IntCallbackReturn = FEX_NAKED void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
IntCallbackReturn InterpreterCallbackReturn;
FEXCore::HostFeatures HostFeatures;
@@ -154,7 +157,8 @@ namespace FEXCore::Context {
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;
std::function<std::unique_ptr<std::ostream>(const std::string&)> AOTIRWriter;
std::unordered_map<std::string, AOTIRCaptureCacheEntry> AOTIRCaptureCache;
struct AddrToFileEntry {
uint64_t Start;
@@ -181,7 +185,7 @@ namespace FEXCore::Context {
bool InitCore(FEXCore::CodeLoader *Loader);
FEXCore::Context::ExitReason RunUntilExit();
int GetProgramStatus();
int GetProgramStatus() const;
bool IsPaused() const { return !Running; }
void Pause();
void Run();
@@ -192,7 +196,7 @@ namespace FEXCore::Context {
void StopThread(FEXCore::Core::InternalThreadState *Thread);
void SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event);
bool GetGdbServerStatus() { return (bool)DebugServer; }
bool GetGdbServerStatus() const { return DebugServer != nullptr; }
void StartGdbServer();
void StopGdbServer();
void HandleCallback(uint64_t RIP);
@@ -222,7 +226,7 @@ namespace FEXCore::Context {
void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
bool LoadAOTIRCache(int streamfd);
bool WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
void FinalizeAOTIRCache();
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer);
// Used for thread creation from syscalls
@@ -257,7 +261,6 @@ namespace FEXCore::Context {
void NotifyPause();
void AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr, uint64_t Start, uint64_t Length);
FEXCore::CodeLoader *LocalLoader{};
// Entry Cache
@@ -265,6 +268,14 @@ namespace FEXCore::Context {
std::mutex ExitMutex;
std::unique_ptr<GdbServer> DebugServer;
std::shared_mutex AOTIRCacheLock;
std::shared_mutex AOTIRCaptureCacheWriteoutLock;
std::atomic<bool> AOTIRCaptureCacheWriteoutFlusing;
std::queue<std::function<void()>> AOTIRCaptureCacheWriteoutQueue;
void AOTIRCaptureCacheWriteoutQueue_Flush();
void AOTIRCaptureCacheWriteoutQueue_Append(const std::function<void()> &fn);
bool StartPaused = false;
FEX_CONFIG_OPT(AppFilename, APP_FILENAME);
};
+1 -1
View File
@@ -90,7 +90,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h() {
(0 << 16) | // Reserved
(0 << 17) | // Process-context identifiers
(1 << 18) | // Prefetching from memory mapped device
(0 << 19) | // SSE4.1
(1 << 19) | // SSE4.1
(0 << 20) | // SSE4.2
(0 << 21) | // X2APIC
(1 << 22) | // MOVBE
+156 -91
View File
@@ -52,8 +52,6 @@ namespace FEXCore::CPU {
}
}
static std::mutex AOTIRCacheLock;
namespace FEXCore::Core {
struct ThreadLocalData {
FEXCore::Core::InternalThreadState* Thread;
@@ -147,6 +145,53 @@ namespace DefaultFallbackCore {
}
namespace FEXCore::Context {
void Context::AOTIRCaptureCacheWriteoutQueue_Flush() {
{
std::shared_lock lk{AOTIRCaptureCacheWriteoutLock};
if (AOTIRCaptureCacheWriteoutQueue.size() == 0) {
AOTIRCaptureCacheWriteoutFlusing.store(false);
return;
}
}
for (;;) {
AOTIRCaptureCacheWriteoutLock.lock();
std::function<void()> fn = std::move(AOTIRCaptureCacheWriteoutQueue.front());
bool MaybeEmpty = false;
AOTIRCaptureCacheWriteoutQueue.pop();
MaybeEmpty = AOTIRCaptureCacheWriteoutQueue.size() == 0;
AOTIRCaptureCacheWriteoutLock.unlock();
fn();
if (MaybeEmpty) {
std::shared_lock lk{AOTIRCaptureCacheWriteoutLock};
if (AOTIRCaptureCacheWriteoutQueue.size() == 0) {
AOTIRCaptureCacheWriteoutFlusing.store(false);
return;
}
}
}
LOGMAN_MSG_A("Must never get here");
}
void Context::AOTIRCaptureCacheWriteoutQueue_Append(const std::function<void()> &fn) {
bool Flush = false;
{
std::unique_lock lk{AOTIRCaptureCacheWriteoutLock};
AOTIRCaptureCacheWriteoutQueue.push(fn);
if (AOTIRCaptureCacheWriteoutQueue.size() > 10000) {
Flush = true;
}
}
bool test_val = false;
if (Flush && AOTIRCaptureCacheWriteoutFlusing.compare_exchange_strong(test_val, true)) {
AOTIRCaptureCacheWriteoutQueue_Flush();
}
}
Context::Context() {
#ifdef BLOCKSTATS
BlockData = std::make_unique<FEXCore::BlockSamplingData>();
@@ -177,14 +222,6 @@ namespace FEXCore::Context {
Threads.clear();
}
// AOTIRCaptureCache needs manual clear
for (auto &Mod: AOTIRCaptureCache) {
for (auto &Entry: Mod.second) {
delete Entry.second.IR;
FEXCore::Allocator::free(Entry.second.RAData);
}
}
for (auto &Mod: AOTIRCache) {
FEXCore::Allocator::munmap(Mod.second.mapping, Mod.second.size);
}
@@ -282,7 +319,7 @@ namespace FEXCore::Context {
// Tell all the threads that they should pause
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
for (auto &Thread : Threads) {
Thread->SignalReason.store(FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE);
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Pause);
if (Thread->RunningEvents.Running.load()) {
// Only attempt to stop this thread if it is running
tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
@@ -303,7 +340,7 @@ namespace FEXCore::Context {
// Spin up all the threads
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
for (auto &Thread : Threads) {
Thread->SignalReason.store(FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN);
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Return);
Thread->RunningEvents.WaitingToStart.store(true);
}
@@ -383,7 +420,7 @@ namespace FEXCore::Context {
void Context::StopThread(FEXCore::Core::InternalThreadState *Thread) {
if (Thread->RunningEvents.Running.exchange(false)) {
Thread->SignalReason.store(FEXCore::Core::SignalEvent::SIGNALEVENT_STOP);
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Stop);
tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
}
}
@@ -411,7 +448,7 @@ namespace FEXCore::Context {
}
}
int Context::GetProgramStatus() {
int Context::GetProgramStatus() const {
return ParentThread->StatusCode;
}
@@ -442,8 +479,6 @@ namespace FEXCore::Context {
}
void Context::InitializeThread(FEXCore::Core::InternalThreadState *Thread) {
InitializeThreadData(Thread);
// This will create the execution thread but it won't actually start executing
ExecutionThreadHandler *Arg = reinterpret_cast<ExecutionThreadHandler*>(FEXCore::Allocator::malloc(sizeof(ExecutionThreadHandler)));
Arg->This = this;
@@ -485,21 +520,23 @@ namespace FEXCore::Context {
// Create CPU backend
switch (Config.Core) {
case FEXCore::Config::CONFIG_INTERPRETER:
State->CPUBackend.reset(FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread));
State->CPUBackend = FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread);
break;
case FEXCore::Config::CONFIG_IRJIT:
State->PassManager->InsertRegisterAllocationPass(DoSRA);
#if (_M_X86_64 && JIT_X86_64)
State->CPUBackend.reset(FEXCore::CPU::CreateX86JITCore(this, State, CompileThread));
State->CPUBackend = FEXCore::CPU::CreateX86JITCore(this, State, CompileThread);
#elif (_M_ARM_64 && JIT_ARM64)
State->CPUBackend.reset(FEXCore::CPU::CreateArm64JITCore(this, State, CompileThread));
State->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, State, CompileThread);
#else
ERROR_AND_DIE("FEXCore has been compiled without a viable JIT core");
#endif
break;
case FEXCore::Config::CONFIG_CUSTOM: State->CPUBackend.reset(CustomCPUFactory(this, State)); break;
case FEXCore::Config::CONFIG_CUSTOM:
State->CPUBackend = CustomCPUFactory(this, State);
break;
default: ERROR_AND_DIE("Unknown core configuration");
}
}
@@ -522,6 +559,7 @@ namespace FEXCore::Context {
Thread->ThreadManager.parent_tid = ParentTID;
InitializeCompiler(Thread, false);
InitializeThreadData(Thread);
return Thread;
}
@@ -764,7 +802,6 @@ namespace FEXCore::Context {
LogMan::Msg::I("two:\n %s", out2.str().c_str());
LOGMAN_MSG_A("Parsed ir doesn't match\n");
}
delete reparsed;
}
}
// Run the passmanager over the IR from the dispatcher
@@ -824,6 +861,28 @@ namespace FEXCore::Context {
return (IR::IRListView *)&InlineData[Offset];
}
void AOTIRCaptureCacheEntry::AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t Start, uint64_t Length, uint64_t Hash, FEXCore::IR::IRListView *IRList, FEXCore::IR::RegisterAllocationData *RAData) {
auto Inserted = Index.emplace(GuestRIP, Stream->tellp());
if (Inserted.second) {
//GuestHash
Stream->write((char*)&Hash, sizeof(Hash));
//GuestLength
Stream->write((char*)&Length, sizeof(Length));
// RAData (inline)
// In file, IsShared is always set
auto Shared = RAData->IsShared;
RAData->IsShared = true;
Stream->write((char*)RAData, RAData->Size(RAData->MapCount));
RAData->IsShared = Shared;
// IRData (inline)
IRList->Serialize(*Stream);
}
}
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool, uint64_t, uint64_t> Context::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
FEXCore::IR::IRListView *IRList {};
FEXCore::Core::DebugData *DebugData {};
@@ -848,7 +907,7 @@ namespace FEXCore::Context {
}
{
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
std::shared_lock lk(AOTIRCacheLock);
auto file = AddrToFile.lower_bound(GuestRIP);
if (file != AddrToFile.begin()) {
--file;
@@ -860,7 +919,7 @@ namespace FEXCore::Context {
}
if (IRList == nullptr && Config.AOTIRLoad) {
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
std::shared_lock lk(AOTIRCacheLock);
auto file = AddrToFile.lower_bound(GuestRIP);
if (file != AddrToFile.begin()) {
--file;
@@ -939,31 +998,44 @@ namespace FEXCore::Context {
bool Context::LoadAOTIRCache(int streamfd) {
uint64_t tag;
if (!readAll(streamfd, (char*)&tag, sizeof(tag)) || tag != 0xDEADBEEFC0D30003)
if (!readAll(streamfd, (char*)&tag, sizeof(tag)) || tag != 0xDEADBEEFC0D30004)
return false;
std::string Module;
uint64_t ModSize;
uint64_t IndexSize;
lseek(streamfd, -sizeof(ModSize), SEEK_END);
if (!readAll(streamfd, (char*)&ModSize, sizeof(ModSize)))
return false;
Module.resize(ModSize);
lseek(streamfd, -sizeof(ModSize) - ModSize, SEEK_END);
if (!readAll(streamfd, (char*)&Module[0], Module.size()))
return false;
lseek(streamfd, -sizeof(ModSize) - ModSize - sizeof(IndexSize), SEEK_END);
if (!readAll(streamfd, (char*)&IndexSize, sizeof(IndexSize)))
return false;
struct stat fileinfo;
if (fstat(streamfd, &fileinfo) < 0)
return false;
size_t Size = (fileinfo.st_size + 4095) & ~4095;
size_t IndexOffset = fileinfo.st_size - IndexSize -sizeof(ModSize) - ModSize - sizeof(IndexSize);
void *FilePtr = FEXCore::Allocator::mmap(nullptr, Size, PROT_READ, MAP_SHARED, streamfd, 0);
if (FilePtr == MAP_FAILED)
return false;
auto Array = (AOTIRInlineIndex *)((char*)FilePtr + sizeof(tag) + sizeof(ModSize) + ((ModSize+31) & ~31));
auto Array = (AOTIRInlineIndex *)((char*)FilePtr + IndexOffset);
AOTIRCache.insert({Module, {Array, FilePtr, Size}});
@@ -974,83 +1046,53 @@ namespace FEXCore::Context {
}
void Context::WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
std::shared_lock lk(AOTIRCacheLock);
for( const auto &File: FilesWithCode) {
Writer(File.first, File.second);
}
}
bool Context::WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter) {
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
void Context::FinalizeAOTIRCache() {
AOTIRCaptureCacheWriteoutQueue_Flush();
bool rv = true;
std::unique_lock lk(AOTIRCacheLock);
for (auto AOTModule: AOTIRCaptureCache) {
if (AOTModule.second.size() == 0) {
for (auto &AOTModule: AOTIRCaptureCache) {
if (!AOTModule.second.Stream) {
continue;
}
auto stream = CacheWriter(AOTModule.first);
if (!*stream) {
rv = false;
}
uint64_t tag = 0xDEADBEEFC0D30003;
stream->write((char*)&tag, sizeof(tag));
auto ModSize = AOTModule.first.size();
stream->write((char*)&ModSize, sizeof(ModSize));
stream->write((char*)&AOTModule.first[0], ModSize);
auto &stream = AOTModule.second.Stream;
auto Skip = ((ModSize + 31) & ~31) - ModSize;
// pad to 32 bytes
char Zero = 0;
for (int i = 0; i < Skip; i++)
while(stream->tellp() & 31)
stream->write(&Zero, 1);
// AOTIRInlineIndex
auto FnCount = AOTModule.second.Index.size();
size_t DataBase = -stream->tellp();
auto FnCount = AOTModule.second.size();
stream->write((char*)&FnCount, sizeof(FnCount));
size_t DataBase = sizeof(FnCount) + sizeof(DataBase) + FnCount * sizeof(AOTIRInlineIndexEntry);
stream->write((char*)&DataBase, sizeof(DataBase));
size_t DataOffset = 0;
for (auto entry: AOTModule.second) {
for (auto entry: AOTModule.second.Index) {
//AOTIRInlineIndexEntry
// GuestStart
stream->write((char*)&entry.first, sizeof(entry.first));
// DataOffset
stream->write((char*)&DataOffset, sizeof(DataOffset));
DataOffset += sizeof(entry.second.crc);
DataOffset += sizeof(entry.second.len);
DataOffset += entry.second.RAData->Size(entry.second.RAData->MapCount);
DataOffset += entry.second.IR->GetInlineSize();
stream->write((char*)&entry.second, sizeof(entry.second));
}
// AOTIRInlineEntry
for (auto entry: AOTModule.second) {
//GuestHash
stream->write((char*)&entry.second.crc, sizeof(entry.second.crc));
//GuestLength
stream->write((char*)&entry.second.len, sizeof(entry.second.len));
// RAData (inline)
stream->write((char*)entry.second.RAData, entry.second.RAData->Size(entry.second.RAData->MapCount));
// IRData (inline)
entry.second.IR->Serialize(*stream);
}
// End of file header
auto IndexSize = FnCount * sizeof(AOTIRInlineIndexEntry) + sizeof(DataBase) + sizeof(FnCount);
stream->write((char*)&IndexSize, sizeof(IndexSize));
stream->write((char*)&AOTModule.first[0], ModSize);
stream->write((char*)&ModSize, sizeof(ModSize));
}
return rv;
}
void Context::CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
@@ -1061,7 +1103,7 @@ namespace FEXCore::Context {
abort();
}
}
uintptr_t Context::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
auto Thread = Frame->Thread;
@@ -1135,26 +1177,49 @@ namespace FEXCore::Context {
// Insert to caches if we generated IR
if (GeneratedIR) {
Core::LocalIREntry Entry = {StartAddr, Length, decltype(Entry.IR)(IRList), decltype(Entry.RAData)(RAData), decltype(Entry.DebugData)(DebugData)};
Thread->LocalIRCache.insert({GuestRIP, std::move(Entry)});
// Add to AOT cache if aot generation is enabled
if ((Config.AOTIRCapture() || Config.AOTIRGenerate()) && RAData) {
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
RAData->IsShared = true;
IRList->SetShared(true);
auto hash = XXH3_64bits((void*)StartAddr, Length);
std::shared_lock lk(AOTIRCacheLock);
auto file = AddrToFile.lower_bound(StartAddr);
if (file != AddrToFile.begin()) {
--file;
if (file->second.Start <= StartAddr && (file->second.Start + file->second.Len) >= (StartAddr + Length)) {
AOTIRCaptureCache[file->second.fileid].insert({GuestRIP - file->second.Start + file->second.Offset, {StartAddr - file->second.Start + file->second.Offset, Length, hash, IRList, RAData}});
auto LocalRIP = GuestRIP - file->second.Start + file->second.Offset;
auto LocalStartAddr = StartAddr - file->second.Start + file->second.Offset;
auto fileid = file->second.fileid;
AOTIRCaptureCacheWriteoutQueue_Append([this, LocalRIP, LocalStartAddr, Length, hash, IRList, RAData, fileid]() {
auto *AotFile = &AOTIRCaptureCache[fileid];
if (!AotFile->Stream) {
AotFile->Stream = AOTIRWriter(fileid);
uint64_t tag = 0xDEADBEEFC0D30004;
AotFile->Stream->write((char*)&tag, sizeof(tag));
}
AotFile->AppendAOTIRCaptureCache(LocalRIP, LocalStartAddr, Length, hash, IRList, RAData);
delete IRList;
FEXCore::Allocator::free(RAData);
});
}
}
if (Config.AOTIRGenerate()) {
// cleanup memory and early exit here -- we're not running the application
if (DecrementRefCount)
--Thread->CompileBlockReentrantRefCount;
Thread->CPUBackend->ClearCache();
return (uintptr_t)CodePtr;
}
}
// Add to thread local ir cache
Core::LocalIREntry Entry = {StartAddr, Length, decltype(Entry.IR)(IRList), decltype(Entry.RAData)(RAData), decltype(Entry.DebugData)(DebugData)};
Thread->LocalIRCache.insert({GuestRIP, std::move(Entry)});
}
if (DecrementRefCount)
@@ -1303,7 +1368,7 @@ namespace FEXCore::Context {
fileid += Config.ABILocalFlags ? "L" : "l";
fileid += Config.ABINoPF ? "p" : "P";
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
std::unique_lock lk(AOTIRCacheLock);
AddrToFile.insert({ Base, { Base, Size, Offset, fileid, filename, nullptr, false} });
@@ -1318,13 +1383,13 @@ namespace FEXCore::Context {
}
void Context::RemoveNamedRegion(uintptr_t Base, uintptr_t Size) {
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
std::unique_lock lk(AOTIRCacheLock);
// TODO: Support partial removing
AddrToFile.erase(Base);
}
void ConfigureAOTGen(FEXCore::Context::Context *CTX, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
CTX->ParentThread->FrontendDecoder->SetExternalBranches(ExternalBranches);
CTX->ParentThread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
}
}
@@ -5,6 +5,7 @@
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <bit>
#include <cmath>
#include "aarch64/assembler-aarch64.h"
@@ -103,7 +104,7 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::
ldr(x0, &l_PagePtr);
// Mask the address by the virtual address size so we can check for aliases
if (__builtin_popcountl(VirtualMemorySize) == 1) {
if (std::popcount(VirtualMemorySize) == 1) {
and_(x3, RipReg, Thread->LookupCache->GetVirtualMemorySize() - 1);
}
else {
@@ -175,16 +175,23 @@ bool Dispatcher::HandleGuestSignal(int Signal, void *info, void *ucontext, Guest
// 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;
if (HostSigInfo->si_code == SI_USER) {
// If the signal was a user signal then we need to pass this struct through unaltered
// Guest might be doing something with it
*guest_siginfo = *HostSigInfo;
}
else {
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;
@@ -254,7 +261,7 @@ 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) {
if (SignalReason == FEXCore::Core::SignalEvent::Pause) {
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
@@ -279,11 +286,11 @@ bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
// We use this to track if it is safe to clear cache
++SignalHandlerRefCounter;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
ThreadState->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
if (SignalReason == FEXCore::Core::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
@@ -304,18 +311,18 @@ bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddress);
}
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
ThreadState->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
if (SignalReason == FEXCore::Core::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);
ThreadState->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
return true;
}
@@ -344,7 +351,7 @@ void Dispatcher::RemoveCodeBuffer(uint8_t* start_to_remove) {
}
}
bool Dispatcher::IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher) {
bool Dispatcher::IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher) const {
for (auto [start, end] : CodeBuffers) {
if (Address >= start && Address < end) {
return true;
@@ -54,8 +54,8 @@ public:
void RemoveCodeBuffer(uint8_t* start);
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
bool IsAddressInDispatcher(uint64_t Address) {
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true) const;
bool IsAddressInDispatcher(uint64_t Address) const {
return Address >= Start && Address < End;
}
@@ -266,7 +266,7 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::Inte
{
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// using CallbackReturn = FEX_NAKED void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// rdi = thread
// rsi = rsp
@@ -293,7 +293,7 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::Inte
#if ENABLE_JITSYMBOLS
std::string Name = "Dispatch_" + std::to_string(::gettid());
CTX->Symbols.Register(Start, End-Start, Name);
CTX->Symbols.Register(reinterpret_cast<void*>(Start), End-Start, Name);
#endif
}
+50 -51
View File
@@ -131,7 +131,7 @@ uint8_t Decoder::ReadByte() {
return Byte;
}
uint8_t Decoder::PeekByte(uint8_t Offset) {
uint8_t Decoder::PeekByte(uint8_t Offset) const {
uint8_t Byte = InstStream[InstructionSize + Offset];
return Byte;
}
@@ -197,9 +197,9 @@ void Decoder::DecodeModRM_16(X86Tables::DecodedOperand *Operand, X86Tables::ModR
}
}
Operand->TypeSIB.Type = DecodedOperand::TYPE_SIB;
Operand->TypeSIB.Scale = 1;
Operand->TypeSIB.Offset = Literal;
Operand->Type = DecodedOperand::OpType::SIB;
Operand->Data.SIB.Scale = 1;
Operand->Data.SIB.Offset = Literal;
// Only called when ModRM.mod != 0b11
struct Encodings {
@@ -238,8 +238,8 @@ void Decoder::DecodeModRM_16(X86Tables::DecodedOperand *Operand, X86Tables::ModR
uint8_t LookupIndex = ModRM.mod << 3 | ModRM.rm;
auto it = Lookup[LookupIndex];
Operand->TypeSIB.Base = it.Base;
Operand->TypeSIB.Index = it.Index;
Operand->Data.SIB.Base = it.Base;
Operand->Data.SIB.Index = it.Index;
}
void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModRMDecoded ModRM) {
@@ -277,12 +277,12 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
}
// SIB
Operand->TypeSIB.Type = DecodedOperand::TYPE_SIB;
Operand->TypeSIB.Scale = 1 << SIB.scale;
Operand->Type = DecodedOperand::OpType::SIB;
Operand->Data.SIB.Scale = 1 << SIB.scale;
// The invalid encoding types are described at Table 1-12. "promoted nsigned is always non-zero"
Operand->TypeSIB.Index = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_X ? 1 : 0, SIB.index, false, false, false, false, 0b100);
Operand->TypeSIB.Base = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, SIB.base, false, false, false, false, ModRM.mod == 0 ? 0b101 : 16);
Operand->Data.SIB.Index = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_X ? 1 : 0, SIB.index, false, false, false, false, 0b100);
Operand->Data.SIB.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");
@@ -291,7 +291,7 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
if (Displacement == 1) {
Literal = static_cast<int8_t>(Literal);
}
Operand->TypeSIB.Offset = Literal;
Operand->Data.SIB.Offset = Literal;
}
else if (ModRM.mod == 0) {
// Explained in Table 1-14. "Operand Addressing Using ModRM and SIB Bytes"
@@ -300,13 +300,13 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
uint32_t Literal;
Literal = ReadData(4);
Operand->TypeRIPLiteral.Type = DecodedOperand::TYPE_RIP_RELATIVE;
Operand->TypeRIPLiteral.Literal.u = Literal;
Operand->Type = DecodedOperand::OpType::RIPRelative;
Operand->Data.RIPLiteral.Value.u = Literal;
}
else {
// Register-direct addressing
Operand->TypeGPR.Type = DecodedOperand::TYPE_GPR_DIRECT;
Operand->TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
Operand->Type = DecodedOperand::OpType::GPRDirect;
Operand->Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
}
}
else {
@@ -318,9 +318,9 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
}
Displacement = DisplacementSize;
Operand->TypeGPRIndirect.Type = DecodedOperand::TYPE_GPR_INDIRECT;
Operand->TypeGPRIndirect.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
Operand->TypeGPRIndirect.Displacement = Literal;
Operand->Type = DecodedOperand::OpType::GPRIndirect;
Operand->Data.GPRIndirect.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
Operand->Data.GPRIndirect.Displacement = Literal;
}
}
@@ -460,9 +460,9 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ||
HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RDX)) {
// Some instructions hardcode their destination as RAX
CurrentDest->TypeGPR.Type = DecodedOperand::TYPE_GPR;
CurrentDest->TypeGPR.HighBits = false;
CurrentDest->TypeGPR.GPR = HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ? FEXCore::X86State::REG_RAX : FEXCore::X86State::REG_RDX;
CurrentDest->Type = DecodedOperand::OpType::GPR;
CurrentDest->Data.GPR.HighBits = false;
CurrentDest->Data.GPR.GPR = HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ? FEXCore::X86State::REG_RAX : FEXCore::X86State::REG_RDX;
CurrentDest = &DecodeInst->Src[0];
}
@@ -473,11 +473,11 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
// This also means that the destination is always a GPR on these ones
// ADDITIONALLY:
// If there is a REX prefix then that allows extended GPR usage
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);
CurrentDest->Type = DecodedOperand::OpType::GPR;
DecodeInst->Dest.Data.GPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM;
CurrentDest->Data.GPR.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)
if (CurrentDest->Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
return false;
}
@@ -501,20 +501,20 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
ModRM.Hex = DecodeInst->ModRM;
// Decode the GPR source first
GPR.TypeGPR.Type = DecodedOperand::TYPE_GPR;
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);
GPR.Type = DecodedOperand::OpType::GPR;
GPR.Data.GPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM;
GPR.Data.GPR.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)
if (GPR.Data.GPR.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)
NonGPR.Type = DecodedOperand::OpType::GPR;
NonGPR.Data.GPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM;
NonGPR.Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, NonGPR8Bit, HasREX, HasXMMNonGPR, HasMMNonGPR);
if (NonGPR.Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
return false;
}
else {
@@ -540,25 +540,24 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
}
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_SRC_RAX)) {
DecodeInst->Src[CurrentSrc].TypeGPR.Type = DecodedOperand::TYPE_GPR;
DecodeInst->Src[CurrentSrc].TypeGPR.HighBits = false;
DecodeInst->Src[CurrentSrc].TypeGPR.GPR = FEXCore::X86State::REG_RAX;
DecodeInst->Src[CurrentSrc].Type = DecodedOperand::OpType::GPR;
DecodeInst->Src[CurrentSrc].Data.GPR.HighBits = false;
DecodeInst->Src[CurrentSrc].Data.GPR.GPR = FEXCore::X86State::REG_RAX;
++CurrentSrc;
}
else if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_SRC_RCX)) {
DecodeInst->Src[CurrentSrc].TypeGPR.Type = DecodedOperand::TYPE_GPR;
DecodeInst->Src[CurrentSrc].TypeGPR.HighBits = false;
DecodeInst->Src[CurrentSrc].TypeGPR.GPR = FEXCore::X86State::REG_RCX;
DecodeInst->Src[CurrentSrc].Type = DecodedOperand::OpType::GPR;
DecodeInst->Src[CurrentSrc].Data.GPR.HighBits = false;
DecodeInst->Src[CurrentSrc].Data.GPR.GPR = FEXCore::X86State::REG_RCX;
++CurrentSrc;
}
if (Bytes != 0) {
LOGMAN_THROW_A(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
DecodeInst->Src[CurrentSrc].TypeLiteral.Size = Bytes;
DecodeInst->Src[CurrentSrc].Data.Literal.Size = Bytes;
uint64_t Literal {0};
Literal = ReadData(Bytes);
uint64_t Literal = ReadData(Bytes);
if ((Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SRC_SEXT) ||
(DecodeFlags::GetSizeDstFlags(DecodeInst->Flags) == DecodeFlags::SIZE_64BIT && Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SRC_SEXT64BIT)) {
@@ -571,12 +570,12 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
else {
Literal = static_cast<int32_t>(Literal);
}
DecodeInst->Src[CurrentSrc].TypeLiteral.Size = DestSize;
DecodeInst->Src[CurrentSrc].Data.Literal.Size = DestSize;
}
Bytes = 0;
DecodeInst->Src[CurrentSrc].TypeLiteral.Type = DecodedOperand::TYPE_LITERAL;
DecodeInst->Src[CurrentSrc].TypeLiteral.Literal = Literal;
DecodeInst->Src[CurrentSrc].Type = DecodedOperand::OpType::Literal;
DecodeInst->Src[CurrentSrc].Data.Literal.Value = 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);
@@ -927,8 +926,8 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
}
if (DecodeInst->Dest.TypeNone.Type == FEXCore::X86Tables::DecodedOperand::TYPE_GPR) {
assert(DecodeInst->Dest.TypeGPR.GPR != 255);
if (DecodeInst->Dest.IsGPR()) {
assert(DecodeInst->Dest.Data.GPR.GPR != 255);
}
return true;
@@ -950,14 +949,14 @@ 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");
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
LOGMAN_THROW_A(DecodeInst->Src[0].IsLiteral(), "Had wrong operand type");
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Data.Literal.Value;
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");
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
LOGMAN_THROW_A(DecodeInst->Src[0].IsLiteral(), "Had wrong operand type");
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Data.Literal.Value;
Conditional = false;
break;
case 0xE8: // Call - Immediate target, We don't want to inline calls
+2 -2
View File
@@ -26,7 +26,7 @@ public:
Decoder(FEXCore::Context::Context *ctx);
bool DecodeInstructionsAtEntry(uint8_t const* InstStream, uint64_t PC);
std::vector<DecodedBlocks> const *GetDecodedBlocks() {
std::vector<DecodedBlocks> const *GetDecodedBlocks() const {
return &Blocks;
}
@@ -43,7 +43,7 @@ private:
void BranchTargetInMultiblockRange();
uint8_t ReadByte();
uint8_t PeekByte(uint8_t Offset);
uint8_t PeekByte(uint8_t Offset) const;
uint64_t ReadData(uint8_t Size);
void SkipBytes(uint8_t Size) { InstructionSize += Size; }
bool NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op);
+74 -90
View File
@@ -15,15 +15,19 @@ $end_info$
#include <optional>
#include "Common/NetStream.h"
#include "Common/SoftFloat.h"
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <fmt/format.h>
#include <fstream>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "GdbServer.h"
#include <FEXCore/Core/CodeLoader.h>
@@ -34,12 +38,12 @@ namespace FEXCore
void GdbServer::Break(int signal) {
std::lock_guard lk(sendMutex);
if (!CommsStream) {
return;
}
std::ostringstream ss;
ss << "S" << std::setfill('0') << std::setw(2) << std::hex << signal;
if (CommsStream)
SendPacket(*CommsStream, ss.str());
const auto str = fmt::format("S{:02x}", signal);
SendPacket(*CommsStream, str);
}
GdbServer::GdbServer(FEXCore::Context::Context *ctx) : CTX(ctx) {
@@ -65,7 +69,7 @@ GdbServer::GdbServer(FEXCore::Context::Context *ctx) : CTX(ctx) {
StartThread();
}
static int calculateChecksum(std::string &packet) {
static int calculateChecksum(const std::string &packet) {
unsigned char checksum = 0;
for (const char &c : packet) {
checksum += c;
@@ -99,11 +103,9 @@ static std::string encodeHex(unsigned char *data, size_t length) {
}
static std::string getThreadName(uint32_t ThreadID) {
std::fstream fs;
std::ostringstream ThreadFile;
ThreadFile << "/proc/" << getpid() << "/task/" << ThreadID << "/comm";
const auto ThreadFile = fmt::format("/proc/{}/task/{}/comm", getpid(), ThreadID);
std::fstream fs(ThreadFile, std::fstream::in | std::fstream::binary);
fs.open(ThreadFile.str(), std::fstream::in | std::fstream::binary);
if (fs.is_open()) {
std::string ThreadName;
fs >> ThreadName;
@@ -135,7 +137,7 @@ std::string GdbServer::ReadPacket(std::iostream &stream) {
switch(c) {
case '$': // start of packet
if (packet.size() != 0)
LogMan::Msg::E("Dropping unexpected data: \"%s\"", packet.c_str());
LogMan::Msg::EFmt("Dropping unexpected data: \"{}\"", packet);
// clear any existing data, must have been a mistake.
packet = std::string();
@@ -156,7 +158,7 @@ std::string GdbServer::ReadPacket(std::iostream &stream) {
if (calculateChecksum(packet) == expected_checksum) {
return packet;
} else {
LogMan::Msg::E("Received Invalid Packet: $%s#%02x %c%c", packet.c_str(), expected_checksum);
LogMan::Msg::EFmt("Received Invalid Packet: ${}#{:02x}", packet, expected_checksum);
}
break;
}
@@ -169,10 +171,10 @@ std::string GdbServer::ReadPacket(std::iostream &stream) {
return "";
}
static std::string escapePacket(std::string packet) {
static std::string escapePacket(const std::string& packet) {
std::ostringstream ss;
for(auto &c : packet) {
for(const auto &c : packet) {
switch (c) {
case '$':
case '#':
@@ -191,13 +193,11 @@ static std::string escapePacket(std::string packet) {
return ss.str();
}
void GdbServer::SendPacket(std::ostream &stream, std::string packet) {
auto escaped = escapePacket(packet);
std::ostringstream ss;
void GdbServer::SendPacket(std::ostream &stream, const std::string& packet) {
const auto escaped = escapePacket(packet);
const auto str = fmt::format("${}#{:02x}", escaped, calculateChecksum(escaped));
ss << '$' << escaped << '#';
ss << std::setfill('0') << std::setw(2) << std::hex << (int)calculateChecksum(escaped);
stream << ss.str() << std::flush;
stream << str << std::flush;
}
void GdbServer::SendACK(std::ostream &stream, bool NACK) {
@@ -218,7 +218,7 @@ void GdbServer::SendACK(std::ostream &stream, bool NACK) {
}
}
struct __attribute__((packed)) GDBContextDefinition {
struct FEX_PACKED GDBContextDefinition {
uint64_t gregs[16];
uint64_t rip;
uint32_t eflags;
@@ -279,7 +279,7 @@ std::string GdbServer::readRegs() {
return encodeHex((unsigned char *)&GDB, sizeof(GDBContextDefinition));
}
GdbServer::HandledPacketType GdbServer::readReg(std::string& packet) {
GdbServer::HandledPacketType GdbServer::readReg(const std::string& packet) {
size_t addr;
auto ss = std::istringstream(packet);
ss.get(); // Drop first letter
@@ -357,7 +357,7 @@ GdbServer::HandledPacketType GdbServer::readReg(std::string& packet) {
return {encodeHex((unsigned char *)(&Empty), sizeof(uint32_t)), HandledPacketType::TYPE_ACK};
}
LogMan::Msg::E("Unknown GDB register 0x%lx", addr);
LogMan::Msg::EFmt("Unknown GDB register 0x{:x}", addr);
return {"E00", HandledPacketType::TYPE_ACK};
}
@@ -462,7 +462,7 @@ std::string buildTargetXML() {
return xml.str();
}
GdbServer::HandledPacketType GdbServer::handleXfer(std::string &packet) {
GdbServer::HandledPacketType GdbServer::handleXfer(const std::string &packet) {
std::string object;
std::string rw;
std::string annex;
@@ -548,10 +548,9 @@ GdbServer::HandledPacketType GdbServer::handleXfer(std::string &packet) {
static size_t CheckMemMapping(uint64_t Address, size_t Size) {
uint64_t AddressEnd = Address + Size;
std::fstream fs;
fs.open("/proc/self/maps", std::fstream::in | std::fstream::binary);
std::fstream fs("/proc/self/maps", std::fstream::in | std::fstream::binary);
std::string Line;
while (std::getline(fs, Line)) {
if (fs.eof()) break;
uint64_t Begin, End;
@@ -568,32 +567,29 @@ static size_t CheckMemMapping(uint64_t Address, size_t Size) {
}
}
fs.close();
return 0;
}
GdbServer::HandledPacketType GdbServer::handleProgramOffsets() {
std::fstream fs;
fs.open("/proc/self/maps", std::fstream::in | std::fstream::binary);
std::fstream fs("/proc/self/maps", std::fstream::in | std::fstream::binary);
std::string Line;
std::string const &RuntimeExecutable = Filename();
while (std::getline(fs, Line)) {
uint64_t Begin, End;
char Filename[255];
if (sscanf(Line.c_str(), "%lx-%lx %*c%*c%*c%*c %*x %*x:%*x %*d%s", &Begin, &End, Filename) == 3) {
if (RuntimeExecutable == Filename) {
std::ostringstream ss;
ss << "Text=" << std::hex << Begin << ";Data=" << std::hex << Begin << ";Bss=" << std::hex << Begin;
ss << std::flush;
return {ss.str(), HandledPacketType::TYPE_ACK};
auto str = fmt::format("Text={:x};Data={:x};Bss={:x}", Begin, Begin, Begin);
return {std::move(str), HandledPacketType::TYPE_ACK};
}
}
}
fs.close();
return {"Text=0;Data=0;Bss=0", HandledPacketType::TYPE_ACK};
}
GdbServer::HandledPacketType GdbServer::handleMemory(std::string &packet) {
GdbServer::HandledPacketType GdbServer::handleMemory(const std::string &packet) {
bool write;
size_t addr;
size_t length;
@@ -634,8 +630,8 @@ GdbServer::HandledPacketType GdbServer::handleMemory(std::string &packet) {
}
GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
GdbServer::HandledPacketType GdbServer::handleQuery(const std::string &packet) {
const auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
if (match("qSupported")) {
return {"PacketSize=5000;xmlRegisters=i386;qXfer:exec-file:read+;qXfer:features:read+;", HandledPacketType::TYPE_ACK};
@@ -693,8 +689,8 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
return {"", HandledPacketType::TYPE_UNKNOWN};
}
GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
auto match = [&](std::string str) -> std::optional<std::istringstream> {
GdbServer::HandledPacketType GdbServer::handleV(const std::string& packet) {
const auto match = [&](const std::string& str) -> std::optional<std::istringstream> {
if (packet.rfind(str, 0) == 0) {
auto ss = std::istringstream(packet);
ss.seekg(str.size());
@@ -703,18 +699,11 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
return std::nullopt;
};
auto F = [](int result) {
std::ostringstream ss;
ss << "F" << std::hex << result;
return ss.str(); };
auto F_error = [&]() {
std::ostringstream ss;
ss << "F-1," << std::hex << errno;
return ss.str(); };
auto F_data = [&](int result, std::string data) {
std::ostringstream ss;
ss << "F" << std::hex << result << ";" << data;
return ss.str(); };
const auto F = [](int result) { return fmt::format("F{:x}", result); };
const auto F_error = [] { return fmt::format("F-1,{:x}", errno); };
const auto F_data = [](int result, const std::string& data) {
return fmt::format("F{:x};{}", result, data);
};
std::optional<std::istringstream> ss;
if((ss = match("vFile:open:"))) {
@@ -736,11 +725,11 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
return {F(pid == 0 ? 0 : -1), HandledPacketType::TYPE_ACK}; // Only support the common filesystem
}
if((ss = match("vFile:close:"))) {
int fd;
*ss >> std::hex >> fd;
close(fd);
return {F(0), HandledPacketType::TYPE_ACK};
}
int fd;
*ss >> std::hex >> fd;
close(fd);
return {F(0), HandledPacketType::TYPE_ACK};
}
if((ss = match("vFile:pread:"))) {
int fd, count, offset;
@@ -777,7 +766,7 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
}
if (ss->fail()) {
return {"E00", HandledPacketType::TYPE_ACK};
return {"E00", HandledPacketType::TYPE_ACK};
}
switch (action) {
@@ -787,27 +776,25 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
}
case 's': {
CTX->Step();
SendPacketPair({"OK", HandledPacketType::TYPE_ACK});
std::ostringstream ss;
ss << "T05thread:" << std::setfill('0') << std::setw(2) << std::hex << getpid() << ";core:2c;";
SendPacketPair({ss.str(), HandledPacketType::TYPE_ACK});
SendPacketPair({"OK", HandledPacketType::TYPE_ACK});
auto str = fmt::format("T05thread:{:02x};core:2c;", getpid());
SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK});
return {"OK", HandledPacketType::TYPE_ACK};
}
case 't':
// This thread isn't part of the thread pool
CTX->Stop(false /* Ignore current thread */);
return {"OK", HandledPacketType::TYPE_ACK};
return {"OK", HandledPacketType::TYPE_ACK};
default:
return {"E00", HandledPacketType::TYPE_ACK};
return {"E00", HandledPacketType::TYPE_ACK};
}
}
return {"", HandledPacketType::TYPE_ACK};
return {"", HandledPacketType::TYPE_ACK};
}
GdbServer::HandledPacketType GdbServer::handleThreadOp(std::string &packet) {
auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
GdbServer::HandledPacketType GdbServer::handleThreadOp(const std::string &packet) {
const auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
if (match("Hc")) {
// Sets thread to this ID for stepping
@@ -823,7 +810,7 @@ GdbServer::HandledPacketType GdbServer::handleThreadOp(std::string &packet) {
if (match("Hg")) {
// Sets thread for "other" operations
auto ss = std::istringstream(packet);
ss.seekg(std::string("Hg").size());
ss.seekg(std::string_view("Hg").size());
ss >> std::hex >> CurrentDebuggingThread;
// This must return quick otherwise IDA complains
@@ -834,7 +821,7 @@ GdbServer::HandledPacketType GdbServer::handleThreadOp(std::string &packet) {
return {"", HandledPacketType::TYPE_UNKNOWN};
}
GdbServer::HandledPacketType GdbServer::handleBreakpoint(std::string &packet) {
GdbServer::HandledPacketType GdbServer::handleBreakpoint(const std::string &packet) {
auto ss = std::istringstream(packet);
bool Set{};
@@ -850,17 +837,15 @@ GdbServer::HandledPacketType GdbServer::handleBreakpoint(std::string &packet) {
return {"OK", HandledPacketType::TYPE_ACK};
}
GdbServer::HandledPacketType GdbServer::ProcessPacket(std::string &packet) {
GdbServer::HandledPacketType GdbServer::ProcessPacket(const std::string &packet) {
switch (packet[0]) {
case '?': {
// Indicates the reason that the thread has stopped
// Behaviour changes if the target is in non-stop mode
// Binja doesn't support S response here
//return {"S00", HandledPacketType::TYPE_ACK};
std::ostringstream ss;
ss << "T00thread:" << std::setfill('0') << std::setw(2) << std::hex << getpid() << ";core:2c;";
return {ss.str(), HandledPacketType::TYPE_ACK};
auto str = fmt::format("T00thread:{:02x};core:2c;", getpid());
return {std::move(str), HandledPacketType::TYPE_ACK};
}
case 'g':
return {readRegs(), HandledPacketType::TYPE_ACK};
@@ -890,14 +875,14 @@ GdbServer::HandledPacketType GdbServer::ProcessPacket(std::string &packet) {
}
}
void GdbServer::SendPacketPair(HandledPacketType response) {
void GdbServer::SendPacketPair(const HandledPacketType& response) {
std::lock_guard lk(sendMutex);
if (response.TypeResponse == HandledPacketType::TYPE_ACK ||
response.TypeResponse == HandledPacketType::TYPE_ONLYACK) {
SendACK(*CommsStream, false);
}
else if (response.TypeResponse == HandledPacketType::TYPE_NACK ||
response.TypeResponse == HandledPacketType::TYPE_ONLYNACK) {
response.TypeResponse == HandledPacketType::TYPE_ONLYNACK) {
SendACK(*CommsStream, true);
}
@@ -905,8 +890,8 @@ void GdbServer::SendPacketPair(HandledPacketType response) {
SendPacket(*CommsStream, "");
}
else if (response.TypeResponse != HandledPacketType::TYPE_ONLYNACK &&
response.TypeResponse != HandledPacketType::TYPE_ONLYACK &&
response.TypeResponse != HandledPacketType::TYPE_NONE) {
response.TypeResponse != HandledPacketType::TYPE_ONLYACK &&
response.TypeResponse != HandledPacketType::TYPE_NONE) {
SendPacket(*CommsStream, response.Response);
}
}
@@ -927,7 +912,7 @@ void GdbServer::GdbServerLoop() {
response = ProcessPacket(packet);
SendPacketPair(response);
if (response.TypeResponse == HandledPacketType::TYPE_UNKNOWN) {
LogMan::Msg::D("Unknown packet %s", packet.c_str());
LogMan::Msg::DFmt("Unknown packet {}", packet);
}
break;
}
@@ -943,13 +928,12 @@ void GdbServer::GdbServerLoop() {
break;
case '\x03': { // ASCII EOT
CTX->Pause();
std::ostringstream ss;
ss << "T02thread:" << std::setfill('0') << std::setw(2) << std::hex << getpid() << ";core:2c;";
SendPacketPair({ss.str(), HandledPacketType::TYPE_ACK});
auto str = fmt::format("T02thread:{:02x};core:2c;", getpid());
SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK});
break;
}
default:
LogMan::Msg::D("GdbServer: Unexpected byte %c (%02x)", c, c);
LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", static_cast<char>(c), c);
}
}
@@ -1003,7 +987,7 @@ std::unique_ptr<std::iostream> GdbServer::OpenSocket() {
// Block until a connection arrives
LogMan::Msg::I("GdbServer, waiting for connection on localhost:8086");
LogMan::Msg::IFmt("GdbServer, waiting for connection on localhost:8086");
listen(sockfd, 1);
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
+10 -10
View File
@@ -30,7 +30,7 @@ private:
std::unique_ptr<std::iostream> OpenSocket();
void StartThread();
std::string ReadPacket(std::iostream &stream);
void SendPacket(std::ostream &stream, std::string packet);
void SendPacket(std::ostream &stream, const std::string& packet);
void SendACK(std::ostream &stream, bool NACK);
@@ -47,18 +47,18 @@ private:
ResponseType TypeResponse{};
};
void SendPacketPair(HandledPacketType packetPair);
HandledPacketType ProcessPacket(std::string &packet);
HandledPacketType handleQuery(std::string &packet);
HandledPacketType handleXfer(std::string &packet);
HandledPacketType handleMemory(std::string &packet);
HandledPacketType handleV(std::string& packet);
HandledPacketType handleThreadOp(std::string &packet);
HandledPacketType handleBreakpoint(std::string &packet);
void SendPacketPair(const HandledPacketType& packetPair);
HandledPacketType ProcessPacket(const std::string &packet);
HandledPacketType handleQuery(const std::string &packet);
HandledPacketType handleXfer(const std::string &packet);
HandledPacketType handleMemory(const std::string &packet);
HandledPacketType handleV(const std::string& packet);
HandledPacketType handleThreadOp(const std::string &packet);
HandledPacketType handleBreakpoint(const std::string &packet);
HandledPacketType handleProgramOffsets();
std::string readRegs();
HandledPacketType readReg(std::string& packet);
HandledPacketType readReg(const std::string& packet);
FEXCore::Context::Context *CTX;
std::unique_ptr<FEXCore::Threads::Thread> gdbServerThread;
@@ -115,8 +115,8 @@ void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR:
return reinterpret_cast<void*>(InterpreterExecution);
}
FEXCore::CPU::CPUBackend *CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
return new InterpreterCore(ctx, Thread, CompileThread);
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
return std::make_unique<InterpreterCore>(ctx, Thread, CompileThread);
}
}
@@ -1,5 +1,7 @@
#pragma once
#include <memory>
namespace FEXCore::Context {
struct Context;
}
@@ -11,6 +13,6 @@ namespace FEXCore::Core {
namespace FEXCore::CPU {
class CPUBackend;
FEXCore::CPU::CPUBackend *CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
}
@@ -10,15 +10,18 @@
#include "Interface/Core/DebugData.h"
#include "Interface/Core/InternalThreadState.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/BitUtils.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
#include "Interface/HLE/Thunks/Thunks.h"
#include <algorithm>
#include <atomic>
#include <cmath>
#include <limits>
@@ -411,15 +414,15 @@ static void StopThread(FEXCore::Core::InternalThreadState *Thread) {
Thread->CTX->StopThread(Thread);
LOGMAN_MSG_A("unreachable");
__builtin_unreachable();
FEX_UNREACHABLE;
}
[[noreturn]]
static void SignalReturn(FEXCore::Core::InternalThreadState *Thread) {
Thread->CTX->SignalThread(Thread, FEXCore::Core::SIGNALEVENT_RETURN);
Thread->CTX->SignalThread(Thread, FEXCore::Core::SignalEvent::Return);
LOGMAN_MSG_A("unreachable");
__builtin_unreachable();
FEX_UNREACHABLE;
}
template<IR::IROps Op>
@@ -1215,7 +1218,41 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
}
break;
}
case IR::OP_VDUPELEMENT: {
auto Op = IROp->C<IR::IROp_VDupElement>();
uint8_t Elements = OpSize / Op->Header.ElementSize;
LOGMAN_THROW_A(OpSize <= 16, "OpSize is too large for VDupElement: %d", OpSize);
if (OpSize == 16) {
__uint128_t SourceMask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
uint64_t Shift = Op->Header.ElementSize * Op->Index * 8;
if (Op->Header.ElementSize == 8)
SourceMask = ~0ULL;
__uint128_t Src = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[0]);
Src >>= Shift;
Src &= SourceMask;
for (size_t i = 0; i < Elements; ++i) {
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(GDP) + (Op->Header.ElementSize * i)),
&Src, Op->Header.ElementSize);
}
}
else {
uint64_t SourceMask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
uint64_t Shift = Op->Header.ElementSize * Op->Index * 8;
if (Op->Header.ElementSize == 8)
SourceMask = ~0ULL;
uint64_t Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
Src >>= Shift;
Src &= SourceMask;
for (size_t i = 0; i < Elements; ++i) {
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(GDP) + (Op->Header.ElementSize * i)),
&Src, Op->Header.ElementSize);
}
}
break;
}
case IR::OP_ENTRYPOINTOFFSET: {
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
GD = Entry + Op->Offset;
@@ -1573,10 +1610,10 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
uint8_t Mask = OpSize * 8 - 1;
switch (OpSize) {
case 4:
GD = static_cast<int32_t>(Src1) << (Src2 & Mask);
GD = static_cast<uint32_t>(Src1) << (Src2 & Mask);
break;
case 8:
GD = static_cast<int64_t>(Src1) << (Src2 & Mask);
GD = static_cast<uint64_t>(Src1) << (Src2 & Mask);
break;
default: LOGMAN_MSG_A("Unknown LSHL Size: %d\n", OpSize); break;
};
@@ -1866,23 +1903,23 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
case IR::OP_POPCOUNT: {
auto Op = IROp->C<IR::IROp_Popcount>();
uint64_t Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
GD = __builtin_popcountl(Src);
GD = std::popcount(Src);
break;
}
case IR::OP_FINDLSB: {
auto Op = IROp->C<IR::IROp_FindLSB>();
uint64_t Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
uint64_t Result = __builtin_ffsll(Src);
uint64_t Result = FindFirstSetBit(Src);
GD = Result - 1;
break;
}
case IR::OP_FINDMSB: {
auto Op = IROp->C<IR::IROp_FindMSB>();
switch (OpSize) {
case 1: GD = ((24 + OpSize * 8) - __builtin_clz(*GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
case 2: GD = ((16 + OpSize * 8) - __builtin_clz(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
case 4: GD = (OpSize * 8 - __builtin_clz(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
case 8: GD = (OpSize * 8 - __builtin_clzll(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
case 1: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
case 2: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
case 4: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
case 8: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
}
break;
@@ -1890,9 +1927,9 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
case IR::OP_REV: {
auto Op = IROp->C<IR::IROp_Rev>();
switch (OpSize) {
case 2: GD = __builtin_bswap16(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0])); break;
case 4: GD = __builtin_bswap32(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0])); break;
case 8: GD = __builtin_bswap64(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0])); break;
case 2: GD = BSwap16(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0])); break;
case 4: GD = BSwap32(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0])); break;
case 8: GD = BSwap64(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0])); break;
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
}
break;
@@ -1902,34 +1939,22 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
switch (OpSize) {
case 1: {
auto Src = *GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]);
if (Src)
GD = __builtin_ctz(Src);
else
GD = sizeof(Src) * 8;
GD = std::countr_zero(Src);
break;
}
case 2: {
auto Src = *GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]);
if (Src)
GD = __builtin_ctz(Src);
else
GD = sizeof(Src) * 8;
GD = std::countr_zero(Src);
break;
}
case 4: {
auto Src = *GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
if (Src)
GD = __builtin_ctz(Src);
else
GD = sizeof(Src) * 8;
GD = std::countr_zero(Src);
break;
}
case 8: {
auto Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
if (Src)
GD = __builtin_ctzll(Src);
else
GD = sizeof(Src) * 8;
GD = std::countr_zero(Src);
break;
}
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
@@ -1940,37 +1965,23 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
auto Op = IROp->C<IR::IROp_CountLeadingZeroes>();
switch (OpSize) {
case 1: {
uint32_t Src = *GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]);
Src <<= 24;
if (Src)
GD = __builtin_clz(Src);
else
GD = 8;
auto Src = *GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]);
GD = std::countl_zero(Src);
break;
}
case 2: {
uint32_t Src = *GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]);
Src <<= 16;
if (Src)
GD = __builtin_clz(Src);
else
GD = 16;
auto Src = *GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]);
GD = std::countl_zero(Src);
break;
}
case 4: {
auto Src = *GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
if (Src)
GD = __builtin_clz(Src);
else
GD = sizeof(Src) * 8;
GD = std::countl_zero(Src);
break;
}
case 8: {
auto Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
if (Src)
GD = __builtin_clzll(Src);
else
GD = sizeof(Src) * 8;
GD = std::countl_zero(Src);
break;
}
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
@@ -2543,6 +2554,16 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, &Dst, 16);
break;
}
case IR::OP_VBIC: {
auto Op = IROp->C<IR::IROp_VBic>();
__uint128_t Src1 = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[0]);
__uint128_t Src2 = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[1]);
__uint128_t Dst = Src1 & ~Src2;
memcpy(GDP, &Dst, 16);
break;
}
case IR::OP_VXOR: {
auto Op = IROp->C<IR::IROp_VXor>();
__uint128_t Src1 = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[0]);
@@ -2988,6 +3009,24 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, Op->Header.ElementSize);
break;
}
case IR::OP_VUMINV: {
auto Op = IROp->C<IR::IROp_VUMinV>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto current, auto a) { return std::min(current, a); };
switch (Op->Header.ElementSize) {
DO_VECTOR_REDUCE_1SRC_OP(1, uint8_t, Func, ~0)
DO_VECTOR_REDUCE_1SRC_OP(2, uint16_t, Func, ~0)
DO_VECTOR_REDUCE_1SRC_OP(4, uint32_t, Func, ~0U)
DO_VECTOR_REDUCE_1SRC_OP(8, uint64_t, Func, ~0ULL)
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, Op->Header.ElementSize);
break;
}
case IR::OP_VURAVG: {
auto Op = IROp->C<IR::IROp_VURAvg>();
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -3023,6 +3062,24 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VPOPCOUNT: {
auto Op = IROp->C<IR::IROp_VPopcount>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a) { return std::popcount(a); };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_OP(1, uint8_t, Func)
DO_VECTOR_1SRC_OP(2, uint16_t, Func)
DO_VECTOR_1SRC_OP(4, uint32_t, Func)
DO_VECTOR_1SRC_OP(8, uint64_t, Func)
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VFMUL: {
auto Op = IROp->C<IR::IROp_VFMul>();
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -3292,22 +3349,6 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_UTOF: {
auto Op = IROp->C<IR::IROp_Vector_UToF>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a, auto min, auto max) { return a; };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, float, uint32_t, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, double, uint64_t, Func, 0, 0)
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_STOF: {
auto Op = IROp->C<IR::IROp_Vector_SToF>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -3324,22 +3365,6 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_FTOZU: {
auto Op = IROp->C<IR::IROp_Vector_FToZU>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a, auto min, auto max) { return a; };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, uint32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, uint64_t, double, Func, 0, 0)
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_FTOZS: {
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -3347,7 +3372,7 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a, auto min, auto max) { return a; };
auto Func = [](auto a, auto min, auto max) { return std::trunc(a); };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
@@ -3356,22 +3381,6 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_FTOU: {
auto Op = IROp->C<IR::IROp_Vector_FToU>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a, auto min, auto max) { return a; };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, uint32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, uint64_t, double, Func, 0, 0)
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_FTOS: {
auto Op = IROp->C<IR::IROp_Vector_FToS>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -3379,7 +3388,7 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a, auto min, auto max) { return a; };
auto Func = [](auto a, auto min, auto max) { return std::nearbyint(a); };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
@@ -3502,6 +3511,28 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, Op->Header.Size);
break;
}
case IR::OP_VUABDL: {
auto Op = IROp->C<IR::IROp_VUABDL>();
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
void *Src2 = GetSrc<void*>(SSAData, Op->Header.Args[1]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func8 = [](auto a, auto b) { return std::abs((int16_t)a - (int16_t)b); };
auto Func16 = [](auto a, auto b) { return std::abs((int32_t)a - (int32_t)b); };
auto Func32 = [](auto a, auto b) { return std::abs((int64_t)a - (int64_t)b); };
switch (Op->Header.ElementSize) {
DO_VECTOR_2SRC_2TYPE_OP(2, uint16_t, uint8_t, Func8)
DO_VECTOR_2SRC_2TYPE_OP(4, uint32_t, uint16_t, Func16)
DO_VECTOR_2SRC_2TYPE_OP(8, uint64_t, uint32_t, Func32)
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VSXTL: {
auto Op = IROp->C<IR::IROp_VSXTL>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -3822,6 +3853,64 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VUNZIP2:
case IR::OP_VUNZIP: {
auto Op = IROp->C<IR::IROp_VUnZip>();
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
void *Src2 = GetSrc<void*>(SSAData, Op->Header.Args[1]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
unsigned Start = IROp->Op == IR::OP_VUNZIP ? 0 : 1;
Elements >>= 1;
switch (Op->Header.ElementSize) {
case 1: {
auto *Dst_d = reinterpret_cast<uint8_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint8_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint8_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i] = Src1_d[Start + (i * 2)];
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
}
break;
}
case 2: {
auto *Dst_d = reinterpret_cast<uint16_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint16_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint16_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i] = Src1_d[Start + (i * 2)];
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
}
break;
}
case 4: {
auto *Dst_d = reinterpret_cast<uint32_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint32_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint32_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i] = Src1_d[Start + (i * 2)];
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
}
break;
}
case 8: {
auto *Dst_d = reinterpret_cast<uint64_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint64_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint64_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i] = Src1_d[Start + (i * 2)];
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
}
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VINSELEMENT: {
auto Op = IROp->C<IR::IROp_VInsElement>();
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -4204,6 +4293,10 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
uint64_t Offset = Op->Index * Op->Header.ElementSize * 8;
__uint128_t Mask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
if (Op->Header.ElementSize == 8) {
Mask = ~0ULL;
}
Src2 = Src2 & Mask;
Mask <<= Offset;
Mask = ~Mask;
__uint128_t Dst = Src1 & Mask;
@@ -4240,78 +4333,57 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
}
break;
}
case IR::OP_FLOAT_FROMGPR_U: {
auto Op = IROp->C<IR::IROp_Float_FromGPR_U>();
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0404: { // Float <- int32_t
float Dst = (float)*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
case 0x0408: { // Float <- int64_t
float Dst = (float)*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
case 0x0804: { // Double <- int32_t
double Dst = (double)*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
case 0x0808: { // Double <- int64_t
double Dst = (double)*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
}
break;
}
case IR::OP_FLOAT_TOGPR_ZS: {
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZS>();
if (Op->Header.ElementSize == 8) {
int64_t Dst = (int64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
else {
int32_t Dst = (int32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
break;
}
case IR::OP_FLOAT_TOGPR_ZU: {
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZU>();
if (Op->Header.ElementSize == 8) {
uint64_t Dst = (uint64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
else {
uint32_t Dst = (uint32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0804: { // int64_t <- float
int64_t Dst = (int64_t)std::trunc(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
case 0x0808: { // int64_t <- double
int64_t Dst = (int64_t)std::trunc(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
case 0x0404: { // int32_t <- float
int32_t Dst = (int32_t)std::trunc(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
case 0x0408: { // int32_t <- double
int32_t Dst = (int32_t)std::trunc(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
}
break;
}
case IR::OP_FLOAT_TOGPR_S: {
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
if (Op->Header.ElementSize == 8) {
int64_t Dst = (int64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
else {
int32_t Dst = (int32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
break;
}
case IR::OP_FLOAT_TOGPR_U: {
auto Op = IROp->C<IR::IROp_Float_ToGPR_U>();
if (Op->Header.ElementSize == 8) {
uint64_t Dst = (uint64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
else {
uint32_t Dst = (uint32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0804: { // int64_t <- float
int64_t Dst = (int64_t)std::nearbyint(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
case 0x0808: { // int64_t <- double
int64_t Dst = (int64_t)std::nearbyint(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
case 0x0404: { // int32_t <- float
int32_t Dst = (int32_t)std::nearbyint(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
case 0x0408: { // int32_t <- double
int32_t Dst = (int32_t)std::nearbyint(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
memcpy(GDP, &Dst, IROp->Size);
break;
}
}
break;
}
@@ -4363,6 +4435,53 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_FTOI: {
auto Op = IROp->C<IR::IROp_Vector_FToI>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16]{};
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func_Nearest = [](auto a) { return std::rint(a); };
auto Func_Neg = [](auto a) { return std::floor(a); };
auto Func_Pos = [](auto a) { return std::ceil(a); };
auto Func_Trunc = [](auto a) { return std::trunc(a); };
auto Func_Host = [](auto a) { return std::rint(a); };
switch (Op->Round) {
case FEXCore::IR::Round_Nearest.Val:
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Nearest)
DO_VECTOR_1SRC_OP(8, double, Func_Nearest)
}
break;
case FEXCore::IR::Round_Negative_Infinity.Val:
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Neg)
DO_VECTOR_1SRC_OP(8, double, Func_Neg)
}
break;
case FEXCore::IR::Round_Positive_Infinity.Val:
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Pos)
DO_VECTOR_1SRC_OP(8, double, Func_Pos)
}
break;
case FEXCore::IR::Round_Towards_Zero.Val:
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Trunc)
DO_VECTOR_1SRC_OP(8, double, Func_Trunc)
}
break;
case FEXCore::IR::Round_Host.Val:
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Host)
DO_VECTOR_1SRC_OP(8, double, Func_Host)
}
break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_FCMP: {
auto Op = IROp->C<IR::IROp_FCmp>();
uint32_t ResultFlags{};
+28 -15
View File
@@ -969,34 +969,49 @@ DEF_OP(VExtractToGPR) {
}
}
DEF_OP(Float_ToGPR_ZU) {
LogMan::Msg::D("Unimplemented");
}
DEF_OP(Float_ToGPR_ZS) {
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZS>();
if (Op->Header.ElementSize == 8) {
fcvtzs(GetReg<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()).D());
aarch64::Register Dst{};
aarch64::VRegister Src{};
if (Op->SrcElementSize == 8) {
Src = GetSrc(Op->Header.Args[0].ID()).D();
}
else {
fcvtzs(GetReg<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()).S());
Src = GetSrc(Op->Header.Args[0].ID()).S();
}
}
DEF_OP(Float_ToGPR_U) {
LogMan::Msg::D("Unimplemented");
if (IROp->Size == 8) {
Dst = GetReg<RA_64>(Node);
}
else {
Dst = GetReg<RA_32>(Node);
}
fcvtzs(Dst, Src);
}
DEF_OP(Float_ToGPR_S) {
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
if (Op->Header.ElementSize == 8) {
aarch64::Register Dst{};
aarch64::VRegister Src{};
if (Op->SrcElementSize == 8) {
frinti(VTMP1.D(), GetSrc(Op->Header.Args[0].ID()).D());
fcvtzs(GetReg<RA_64>(Node), VTMP1.D());
Src = VTMP1.D();
}
else {
frinti(VTMP1.S(), GetSrc(Op->Header.Args[0].ID()).S());
fcvtzs(GetReg<RA_32>(Node), VTMP1.S());
Src = VTMP1.S();
}
if (IROp->Size == 8) {
Dst = GetReg<RA_64>(Node);
}
else {
Dst = GetReg<RA_32>(Node);
}
fcvtzs(Dst, Src);
}
DEF_OP(FCmp) {
@@ -1087,9 +1102,7 @@ void Arm64JITCore::RegisterALUHandlers() {
REGISTER_OP(SBFE, Sbfe);
REGISTER_OP(SELECT, Select);
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
REGISTER_OP(FLOAT_TOGPR_ZU, Float_ToGPR_ZU);
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
REGISTER_OP(FLOAT_TOGPR_U, Float_ToGPR_U);
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
REGISTER_OP(FCMP, FCmp);
@@ -56,10 +56,6 @@ DEF_OP(VCastFromGPR) {
}
}
DEF_OP(Float_FromGPR_U) {
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Float_FromGPR_S) {
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
@@ -99,19 +95,6 @@ DEF_OP(Float_FToF) {
}
}
DEF_OP(Vector_UToF) {
auto Op = IROp->C<IR::IROp_Vector_UToF>();
switch (Op->Header.ElementSize) {
case 4:
ucvtf(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
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);
}
}
DEF_OP(Vector_SToF) {
auto Op = IROp->C<IR::IROp_Vector_SToF>();
switch (Op->Header.ElementSize) {
@@ -125,19 +108,6 @@ DEF_OP(Vector_SToF) {
}
}
DEF_OP(Vector_FToZU) {
auto Op = IROp->C<IR::IROp_Vector_FToZU>();
switch (Op->Header.ElementSize) {
case 4:
fcvtzu(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
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);
}
}
DEF_OP(Vector_FToZS) {
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
switch (Op->Header.ElementSize) {
@@ -151,21 +121,6 @@ DEF_OP(Vector_FToZS) {
}
}
DEF_OP(Vector_FToU) {
auto Op = IROp->C<IR::IROp_Vector_FToU>();
switch (Op->Header.ElementSize) {
case 4:
frinti(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
fcvtzu(GetDst(Node).V4S(), GetDst(Node).V4S());
break;
case 8:
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);
}
}
DEF_OP(Vector_FToS) {
auto Op = IROp->C<IR::IROp_Vector_FToS>();
switch (Op->Header.ElementSize) {
@@ -198,21 +153,74 @@ DEF_OP(Vector_FToF) {
}
}
DEF_OP(Vector_FToI) {
auto Op = IROp->C<IR::IROp_Vector_FToI>();
switch (Op->Round) {
case FEXCore::IR::Round_Nearest.Val:
switch (Op->Header.ElementSize) {
case 4:
frintn(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
case 8:
frintn(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
break;
case FEXCore::IR::Round_Negative_Infinity.Val:
switch (Op->Header.ElementSize) {
case 4:
frintm(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
case 8:
frintm(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
break;
case FEXCore::IR::Round_Positive_Infinity.Val:
switch (Op->Header.ElementSize) {
case 4:
frintp(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
case 8:
frintp(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
break;
case FEXCore::IR::Round_Towards_Zero.Val:
switch (Op->Header.ElementSize) {
case 4:
frintz(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
case 8:
frintz(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
break;
case FEXCore::IR::Round_Host.Val:
switch (Op->Header.ElementSize) {
case 4:
frinti(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
break;
case 8:
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
break;
}
break;
}
}
#undef DEF_OP
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);
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
REGISTER_OP(FLOAT_FTOF, Float_FToF);
REGISTER_OP(VECTOR_UTOF, Vector_UToF);
REGISTER_OP(VECTOR_STOF, Vector_SToF);
REGISTER_OP(VECTOR_FTOZU, Vector_FToZU);
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
REGISTER_OP(VECTOR_FTOU, Vector_FToU);
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
#undef REGISTER_OP
}
}
+23 -18
View File
@@ -23,6 +23,7 @@ $end_info$
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Core/UContext.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include <sys/mman.h>
@@ -575,7 +576,7 @@ Arm64JITCore::~Arm64JITCore() {
FreeCodeBuffer(InitialCodeBuffer);
}
IR::PhysicalRegister Arm64JITCore::GetPhys(uint32_t Node) {
IR::PhysicalRegister Arm64JITCore::GetPhys(uint32_t Node) const {
auto PhyReg = RAData->GetNodeRegister(Node);
LOGMAN_THROW_A(!PhyReg.IsInvalid(), "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
@@ -584,7 +585,7 @@ IR::PhysicalRegister Arm64JITCore::GetPhys(uint32_t Node) {
}
template<>
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_32>(uint32_t Node) {
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_32>(uint32_t Node) const {
auto Reg = GetPhys(Node);
if (Reg.Class == IR::GPRFixedClass.Val) {
@@ -594,11 +595,12 @@ aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_32>(uint32_t Node) {
} else {
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
}
__builtin_unreachable();
FEX_UNREACHABLE;
}
template<>
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_64>(uint32_t Node) {
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_64>(uint32_t Node) const {
auto Reg = GetPhys(Node);
if (Reg.Class == IR::GPRFixedClass.Val) {
@@ -608,22 +610,23 @@ aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_64>(uint32_t Node) {
} else {
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
}
__builtin_unreachable();
FEX_UNREACHABLE;
}
template<>
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_32>(uint32_t Node) {
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_32>(uint32_t Node) const {
uint32_t Reg = GetPhys(Node).Reg;
return RA32Pair[Reg];
}
template<>
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_64>(uint32_t Node) {
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_64>(uint32_t Node) const {
uint32_t Reg = GetPhys(Node).Reg;
return RA64Pair[Reg];
}
aarch64::VRegister Arm64JITCore::GetSrc(uint32_t Node) {
aarch64::VRegister Arm64JITCore::GetSrc(uint32_t Node) const {
auto Reg = GetPhys(Node);
if (Reg.Class == IR::FPRFixedClass.Val) {
@@ -633,10 +636,11 @@ aarch64::VRegister Arm64JITCore::GetSrc(uint32_t Node) {
} else {
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
}
__builtin_unreachable();
FEX_UNREACHABLE;
}
aarch64::VRegister Arm64JITCore::GetDst(uint32_t Node) {
aarch64::VRegister Arm64JITCore::GetDst(uint32_t Node) const {
auto Reg = GetPhys(Node);
if (Reg.Class == IR::FPRFixedClass.Val) {
@@ -646,10 +650,11 @@ aarch64::VRegister Arm64JITCore::GetDst(uint32_t Node) {
} else {
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
}
__builtin_unreachable();
FEX_UNREACHABLE;
}
bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) {
@@ -663,7 +668,7 @@ bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_
}
}
bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) {
@@ -677,18 +682,18 @@ bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode,
}
}
FEXCore::IR::RegisterClassType Arm64JITCore::GetRegClass(uint32_t Node) {
FEXCore::IR::RegisterClassType Arm64JITCore::GetRegClass(uint32_t Node) const {
return FEXCore::IR::RegisterClassType {GetPhys(Node).Class};
}
bool Arm64JITCore::IsFPR(uint32_t Node) {
bool Arm64JITCore::IsFPR(uint32_t Node) const {
auto Class = GetRegClass(Node);
return Class == IR::FPRClass || Class == IR::FPRFixedClass;
}
bool Arm64JITCore::IsGPR(uint32_t Node) {
bool Arm64JITCore::IsGPR(uint32_t Node) const {
auto Class = GetRegClass(Node);
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
@@ -888,7 +893,7 @@ uint64_t Arm64JITCore::ExitFunctionLink(Arm64JITCore *core, FEXCore::Core::CpuSt
return HostCode;
}
FEXCore::CPU::CPUBackend *CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
return new Arm64JITCore(ctx, Thread, CompileThread);
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
return std::make_unique<Arm64JITCore>(ctx, Thread, CompileThread);
}
}
+22 -19
View File
@@ -96,35 +96,35 @@ private:
constexpr static uint8_t RA_FPR = 2;
template<uint8_t RAType>
aarch64::Register GetReg(uint32_t Node);
aarch64::Register GetReg(uint32_t Node) const;
template<>
aarch64::Register GetReg<RA_32>(uint32_t Node);
aarch64::Register GetReg<RA_32>(uint32_t Node) const;
template<>
aarch64::Register GetReg<RA_64>(uint32_t Node);
aarch64::Register GetReg<RA_64>(uint32_t Node) const;
template<uint8_t RAType>
std::pair<aarch64::Register, aarch64::Register> GetSrcPair(uint32_t Node);
std::pair<aarch64::Register, aarch64::Register> GetSrcPair(uint32_t Node) const;
template<>
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_32>(uint32_t Node);
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_32>(uint32_t Node) const;
template<>
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_64>(uint32_t Node);
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_64>(uint32_t Node) const;
aarch64::VRegister GetSrc(uint32_t Node);
aarch64::VRegister GetDst(uint32_t Node);
aarch64::VRegister GetSrc(uint32_t Node) const;
aarch64::VRegister GetDst(uint32_t Node) const;
FEXCore::IR::RegisterClassType GetRegClass(uint32_t Node);
FEXCore::IR::RegisterClassType GetRegClass(uint32_t Node) const;
IR::PhysicalRegister GetPhys(uint32_t Node);
IR::PhysicalRegister GetPhys(uint32_t Node) const;
bool IsFPR(uint32_t Node);
bool IsGPR(uint32_t Node);
bool IsFPR(uint32_t Node) const;
bool IsGPR(uint32_t Node) const;
MemOperand GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale);
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr);
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value);
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr) const;
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const;
struct LiveRange {
uint32_t Begin;
@@ -240,7 +240,6 @@ private:
DEF_OP(VExtractToGPR);
DEF_OP(Float_ToGPR_ZU);
DEF_OP(Float_ToGPR_ZS);
DEF_OP(Float_ToGPR_U);
DEF_OP(Float_ToGPR_S);
DEF_OP(FCmp);
@@ -277,16 +276,13 @@ private:
///< Conversion ops
DEF_OP(VInsGPR);
DEF_OP(VCastFromGPR);
DEF_OP(Float_FromGPR_U);
DEF_OP(Float_FromGPR_S);
DEF_OP(Float_FToF);
DEF_OP(Vector_UToF);
DEF_OP(Vector_SToF);
DEF_OP(Vector_FToZU);
DEF_OP(Vector_FToZS);
DEF_OP(Vector_FToU);
DEF_OP(Vector_FToS);
DEF_OP(Vector_FToF);
DEF_OP(Vector_FToI);
///< Flag ops
DEF_OP(GetHostFlag);
@@ -336,6 +332,7 @@ private:
DEF_OP(SplatVector4);
DEF_OP(VMov);
DEF_OP(VAnd);
DEF_OP(VBic);
DEF_OP(VOr);
DEF_OP(VXor);
DEF_OP(VAdd);
@@ -346,8 +343,10 @@ private:
DEF_OP(VSQSub);
DEF_OP(VAddP);
DEF_OP(VAddV);
DEF_OP(VUMinV);
DEF_OP(VURAvg);
DEF_OP(VAbs);
DEF_OP(VPopcount);
DEF_OP(VFAdd);
DEF_OP(VFAddP);
DEF_OP(VFSub);
@@ -367,6 +366,8 @@ private:
DEF_OP(VSMax);
DEF_OP(VZip);
DEF_OP(VZip2);
DEF_OP(VUnZip);
DEF_OP(VUnZip2);
DEF_OP(VBSL);
DEF_OP(VCMPEQ);
DEF_OP(VCMPEQZ);
@@ -389,6 +390,7 @@ private:
DEF_OP(VInsElement);
DEF_OP(VInsScalarElement);
DEF_OP(VExtractElement);
DEF_OP(VDupElement);
DEF_OP(VExtr);
DEF_OP(VSLI);
DEF_OP(VSRI);
@@ -411,6 +413,7 @@ private:
DEF_OP(VSMull);
DEF_OP(VUMull2);
DEF_OP(VSMull2);
DEF_OP(VUABDL);
DEF_OP(VTBL1);
///< Encryption ops
@@ -5,6 +5,7 @@ $end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include <FEXCore/Utils/CompilerDefs.h>
namespace FEXCore::CPU {
@@ -554,7 +555,8 @@ MemOperand Arm64JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Registe
}
}
}
__builtin_unreachable();
FEX_UNREACHABLE;
}
DEF_OP(LoadMem) {
@@ -127,6 +127,11 @@ DEF_OP(VAnd) {
and_(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
}
DEF_OP(VBic) {
auto Op = IROp->C<IR::IROp_VBic>();
bic(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
}
DEF_OP(VOr) {
auto Op = IROp->C<IR::IROp_VOr>();
orr(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
@@ -337,6 +342,21 @@ DEF_OP(VAddV) {
}
}
DEF_OP(VUMinV) {
auto Op = IROp->C<IR::IROp_VUMinV>();
uint8_t OpSize = IROp->Size;
uint8_t Elements = OpSize / Op->Header.ElementSize;
// Vector
switch (Op->Header.ElementSize) {
case 1:
case 2:
case 4:
uminv(GetDst(Node).VCast(Op->Header.ElementSize * 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;
}
}
DEF_OP(VURAvg) {
auto Op = IROp->C<IR::IROp_VURAvg>();
switch (Op->Header.ElementSize) {
@@ -380,6 +400,30 @@ DEF_OP(VAbs) {
}
}
DEF_OP(VPopcount) {
auto Op = IROp->C<IR::IROp_VPopcount>();
uint8_t OpSize = IROp->Size;
if (OpSize == 8) {
// Scalar
switch (Op->Header.ElementSize) {
case 1: {
cnt(GetDst(Node).V8B(), GetSrc(Op->Header.Args[0].ID()).V8B());
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
else {
// Vector
switch (Op->Header.ElementSize) {
case 1:
cnt(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B());
break;
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
DEF_OP(VFAdd) {
auto Op = IROp->C<IR::IROp_VFAdd>();
uint8_t OpSize = IROp->Size;
@@ -949,6 +993,92 @@ DEF_OP(VZip2) {
}
}
DEF_OP(VUnZip) {
auto Op = IROp->C<IR::IROp_VUnZip>();
uint8_t OpSize = IROp->Size;
if (OpSize == 8) {
switch (Op->Header.ElementSize) {
case 1: {
uzp1(GetDst(Node).V8B(), GetSrc(Op->Header.Args[0].ID()).V8B(), GetSrc(Op->Header.Args[1].ID()).V8B());
break;
}
case 2: {
uzp1(GetDst(Node).V4H(), GetSrc(Op->Header.Args[0].ID()).V4H(), GetSrc(Op->Header.Args[1].ID()).V4H());
break;
}
case 4: {
uzp1(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;
}
}
else {
switch (Op->Header.ElementSize) {
case 1: {
uzp1(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
break;
}
case 2: {
uzp1(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), GetSrc(Op->Header.Args[1].ID()).V8H());
break;
}
case 4: {
uzp1(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
break;
}
case 8: {
uzp1(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;
}
}
}
DEF_OP(VUnZip2) {
auto Op = IROp->C<IR::IROp_VUnZip2>();
uint8_t OpSize = IROp->Size;
if (OpSize == 8) {
switch (Op->Header.ElementSize) {
case 1: {
uzp2(GetDst(Node).V8B(), GetSrc(Op->Header.Args[0].ID()).V8B(), GetSrc(Op->Header.Args[1].ID()).V8B());
break;
}
case 2: {
uzp2(GetDst(Node).V4H(), GetSrc(Op->Header.Args[0].ID()).V4H(), GetSrc(Op->Header.Args[1].ID()).V4H());
break;
}
case 4: {
uzp2(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;
}
}
else {
switch (Op->Header.ElementSize) {
case 1: {
uzp2(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
break;
}
case 2: {
uzp2(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), GetSrc(Op->Header.Args[1].ID()).V8H());
break;
}
case 4: {
uzp2(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
break;
}
case 8: {
uzp2(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;
}
}
}
DEF_OP(VBSL) {
auto Op = IROp->C<IR::IROp_VBSL>();
if (IROp->Size == 16) {
@@ -1648,6 +1778,25 @@ DEF_OP(VExtractElement) {
}
}
DEF_OP(VDupElement) {
auto Op = IROp->C<IR::IROp_VDupElement>();
switch (Op->Header.ElementSize) {
case 1:
dup(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), Op->Index);
break;
case 2:
dup(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), Op->Index);
break;
case 4:
dup(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), Op->Index);
break;
case 8:
dup(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->Index);
break;
default: LOGMAN_MSG_A("Unhandled DupElementSize: %d", Op->Header.ElementSize);
}
}
DEF_OP(VExtr) {
auto Op = IROp->C<IR::IROp_VExtr>();
uint8_t OpSize = IROp->Size;
@@ -2135,6 +2284,25 @@ DEF_OP(VSMull2) {
}
}
DEF_OP(VUABDL) {
auto Op = IROp->C<IR::IROp_VUABDL>();
switch (Op->Header.ElementSize) {
case 2: {
uabdl(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8B(), GetSrc(Op->Header.Args[1].ID()).V8B());
break;
}
case 4: {
uabdl(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4H(), GetSrc(Op->Header.Args[1].ID()).V4H());
break;
}
case 8: {
uabdl(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;
}
}
DEF_OP(VTBL1) {
auto Op = IROp->C<IR::IROp_VTBL1>();
uint8_t OpSize = IROp->Size;
@@ -2163,6 +2331,7 @@ void Arm64JITCore::RegisterVectorHandlers() {
REGISTER_OP(SPLATVECTOR4, SplatVector4);
REGISTER_OP(VMOV, VMov);
REGISTER_OP(VAND, VAnd);
REGISTER_OP(VBIC, VBic);
REGISTER_OP(VOR, VOr);
REGISTER_OP(VXOR, VXor);
REGISTER_OP(VADD, VAdd);
@@ -2173,8 +2342,10 @@ void Arm64JITCore::RegisterVectorHandlers() {
REGISTER_OP(VSQSUB, VSQSub);
REGISTER_OP(VADDP, VAddP);
REGISTER_OP(VADDV, VAddV);
REGISTER_OP(VUMINV, VUMinV);
REGISTER_OP(VURAVG, VURAvg);
REGISTER_OP(VABS, VAbs);
REGISTER_OP(VPOPCOUNT, VPopcount);
REGISTER_OP(VFADD, VFAdd);
REGISTER_OP(VFADDP, VFAddP);
REGISTER_OP(VFSUB, VFSub);
@@ -2194,6 +2365,8 @@ void Arm64JITCore::RegisterVectorHandlers() {
REGISTER_OP(VSMAX, VSMax);
REGISTER_OP(VZIP, VZip);
REGISTER_OP(VZIP2, VZip2);
REGISTER_OP(VUNZIP, VUnZip);
REGISTER_OP(VUNZIP2, VUnZip2);
REGISTER_OP(VBSL, VBSL);
REGISTER_OP(VCMPEQ, VCMPEQ);
REGISTER_OP(VCMPEQZ, VCMPEQZ);
@@ -2216,6 +2389,7 @@ void Arm64JITCore::RegisterVectorHandlers() {
REGISTER_OP(VINSELEMENT, VInsElement);
REGISTER_OP(VINSSCALARELEMENT, VInsScalarElement);
REGISTER_OP(VEXTRACTELEMENT, VExtractElement);
REGISTER_OP(VDUPELEMENT, VDupElement);
REGISTER_OP(VEXTR, VExtr);
REGISTER_OP(VSLI, VSLI);
REGISTER_OP(VSRI, VSRI);
@@ -2239,6 +2413,7 @@ void Arm64JITCore::RegisterVectorHandlers() {
REGISTER_OP(VSMULL, VSMull);
REGISTER_OP(VUMULL2, VUMull2);
REGISTER_OP(VSMULL2, VSMull2);
REGISTER_OP(VUABDL, VUABDL);
REGISTER_OP(VTBL1, VTBL1);
#undef REGISTER_OP
}
+4 -2
View File
@@ -1,5 +1,7 @@
#pragma once
#include <memory>
namespace FEXCore::Context {
struct Context;
}
@@ -11,6 +13,6 @@ struct InternalThreadState;
namespace FEXCore::CPU {
class CPUBackend;
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);
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
}
+42 -23
View File
@@ -1108,42 +1108,63 @@ DEF_OP(VExtractToGPR) {
}
}
DEF_OP(Float_ToGPR_ZU) {
LogMan::Msg::D("Unimplemented");
}
DEF_OP(Float_ToGPR_ZS) {
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZS>();
if (Op->Header.ElementSize == 8) {
cvttsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
}
else {
cvttss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
}
}
DEF_OP(Float_ToGPR_U) {
LogMan::Msg::D("Unimplemented");
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0804: // int64_t <- float
cvttss2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
case 0x0808: // int64_t <- double
cvttsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
case 0x0404: // int32_t <- float
cvttss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
case 0x0408: // int32_t <- double
cvttsd2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
}
DEF_OP(Float_ToGPR_S) {
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
if (Op->Header.ElementSize == 8) {
cvtsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
}
else {
cvtss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0804: // int64_t <- float
cvtss2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
case 0x0808: // int64_t <- double
cvtsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
case 0x0404: // int32_t <- float
cvtss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
case 0x0408: // int32_t <- double
cvtsd2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
}
DEF_OP(FCmp) {
auto Op = IROp->C<IR::IROp_FCmp>();
if (Op->ElementSize == 4) {
ucomiss(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
if (Op->Flags & (1 << IR::FCMP_FLAG_UNORDERED)) {
if (Op->ElementSize == 4) {
ucomiss(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
}
else {
ucomisd(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
}
}
else {
ucomisd(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
if (Op->ElementSize == 4) {
comiss(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
}
else {
comisd(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
}
}
mov (rdx, 0);
@@ -1217,9 +1238,7 @@ void X86JITCore::RegisterALUHandlers() {
REGISTER_OP(SBFE, Sbfe);
REGISTER_OP(SELECT, Select);
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
REGISTER_OP(FLOAT_TOGPR_ZU, Float_ToGPR_ZU);
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
REGISTER_OP(FLOAT_TOGPR_U, Float_ToGPR_U);
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
REGISTER_OP(FCMP, FCmp);
#undef REGISTER_OP
@@ -56,10 +56,6 @@ DEF_OP(VCastFromGPR) {
}
}
DEF_OP(Float_FromGPR_U) {
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Float_FromGPR_S) {
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
@@ -99,10 +95,6 @@ DEF_OP(Float_FToF) {
}
}
DEF_OP(Vector_UToF) {
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Vector_SToF) {
auto Op = IROp->C<IR::IROp_Vector_SToF>();
switch (Op->Header.ElementSize) {
@@ -125,10 +117,6 @@ DEF_OP(Vector_SToF) {
}
}
DEF_OP(Vector_FToZU) {
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Vector_FToZS) {
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
switch (Op->Header.ElementSize) {
@@ -142,10 +130,6 @@ DEF_OP(Vector_FToZS) {
}
}
DEF_OP(Vector_FToU) {
LOGMAN_MSG_A("Unimplemented");
}
DEF_OP(Vector_FToS) {
auto Op = IROp->C<IR::IROp_Vector_FToS>();
switch (Op->Header.ElementSize) {
@@ -176,21 +160,50 @@ DEF_OP(Vector_FToF) {
}
}
DEF_OP(Vector_FToI) {
auto Op = IROp->C<IR::IROp_Vector_FToI>();
uint8_t RoundMode{};
switch (Op->Round) {
case FEXCore::IR::Round_Nearest.Val:
RoundMode = 0b0000'0'0'00;
break;
case FEXCore::IR::Round_Negative_Infinity.Val:
RoundMode = 0b0000'0'0'01;
break;
case FEXCore::IR::Round_Positive_Infinity.Val:
RoundMode = 0b0000'0'0'10;
break;
case FEXCore::IR::Round_Towards_Zero.Val:
RoundMode = 0b0000'0'0'11;
break;
case FEXCore::IR::Round_Host.Val:
RoundMode = 0b0000'0'1'00;
break;
}
switch (Op->Header.ElementSize) {
case 4:
roundps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), RoundMode);
break;
case 8:
roundpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), RoundMode);
break;
}
}
#undef DEF_OP
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);
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
REGISTER_OP(FLOAT_FTOF, Float_FToF);
REGISTER_OP(VECTOR_UTOF, Vector_UToF);
REGISTER_OP(VECTOR_STOF, Vector_SToF);
REGISTER_OP(VECTOR_FTOZU, Vector_FToZU);
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
REGISTER_OP(VECTOR_FTOU, Vector_FToU);
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
#undef REGISTER_OP
}
}
+34 -34
View File
@@ -411,7 +411,7 @@ void X86JITCore::ClearCache() {
}
}
IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) {
IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) const {
auto PhyReg = RAData->GetNodeRegister(Node);
LOGMAN_THROW_A(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
@@ -419,98 +419,98 @@ IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) {
return PhyReg;
}
bool X86JITCore::IsFPR(uint32_t Node) {
bool X86JITCore::IsFPR(uint32_t Node) const {
return RAData->GetNodeRegister(Node).Class == IR::FPRClass.Val;
}
bool X86JITCore::IsGPR(uint32_t Node) {
bool X86JITCore::IsGPR(uint32_t Node) const {
return RAData->GetNodeRegister(Node).Class == IR::GPRClass.Val;
}
template<uint8_t RAType>
Xbyak::Reg X86JITCore::GetSrc(uint32_t Node) {
Xbyak::Reg X86JITCore::GetSrc(uint32_t Node) const {
// rax, rcx, rdx, rsi, r8, r9,
// r10
// Callee Saved
// rbx, rbp, r12, r13, r14, r15
auto PhyReg = GetPhys(Node);
if (RAType == RA_64)
if constexpr (RAType == RA_64)
return RA64[PhyReg.Reg].cvt64();
else if (RAType == RA_XMM)
else if constexpr (RAType == RA_XMM)
return RAXMM[PhyReg.Reg];
else if (RAType == RA_32)
else if constexpr (RAType == RA_32)
return RA64[PhyReg.Reg].cvt32();
else if (RAType == RA_16)
else if constexpr (RAType == RA_16)
return RA64[PhyReg.Reg].cvt16();
else if (RAType == RA_8)
else if constexpr (RAType == RA_8)
return RA64[PhyReg.Reg].cvt8();
}
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_64>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_64>(uint32_t Node) const;
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_32>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_32>(uint32_t Node) const;
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_16>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_16>(uint32_t Node) const;
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_8>(uint32_t Node);
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_8>(uint32_t Node) const;
Xbyak::Xmm X86JITCore::GetSrc(uint32_t Node) {
Xbyak::Xmm X86JITCore::GetSrc(uint32_t Node) const {
auto PhyReg = GetPhys(Node);
return RAXMM_x[PhyReg.Reg];
}
template<uint8_t RAType>
Xbyak::Reg X86JITCore::GetDst(uint32_t Node) {
Xbyak::Reg X86JITCore::GetDst(uint32_t Node) const {
auto PhyReg = GetPhys(Node);
if (RAType == RA_64)
if constexpr (RAType == RA_64)
return RA64[PhyReg.Reg].cvt64();
else if (RAType == RA_XMM)
else if constexpr (RAType == RA_XMM)
return RAXMM[PhyReg.Reg];
else if (RAType == RA_32)
else if constexpr (RAType == RA_32)
return RA64[PhyReg.Reg].cvt32();
else if (RAType == RA_16)
else if constexpr (RAType == RA_16)
return RA64[PhyReg.Reg].cvt16();
else if (RAType == RA_8)
else if constexpr (RAType == RA_8)
return RA64[PhyReg.Reg].cvt8();
}
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_64>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_64>(uint32_t Node) const;
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_32>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_32>(uint32_t Node) const;
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_16>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_16>(uint32_t Node) const;
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_8>(uint32_t Node);
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_8>(uint32_t Node) const;
template<uint8_t RAType>
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair(uint32_t Node) {
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair(uint32_t Node) const {
auto PhyReg = GetPhys(Node);
if (RAType == RA_64)
if constexpr (RAType == RA_64)
return RA64Pair[PhyReg.Reg];
else if (RAType == RA_32)
else if constexpr (RAType == RA_32)
return {RA64Pair[PhyReg.Reg].first.cvt32(), RA64Pair[PhyReg.Reg].second.cvt32()};
}
template
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_64>(uint32_t Node);
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_64>(uint32_t Node) const;
template
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_32>(uint32_t Node);
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_32>(uint32_t Node) const;
Xbyak::Xmm X86JITCore::GetDst(uint32_t Node) {
Xbyak::Xmm X86JITCore::GetDst(uint32_t Node) const {
auto PhyReg = GetPhys(Node);
return RAXMM_x[PhyReg.Reg];
}
bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) {
@@ -524,7 +524,7 @@ bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t*
}
}
bool X86JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
bool X86JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) {
@@ -764,7 +764,7 @@ uint64_t X86JITCore::ExitFunctionLink(X86JITCore *core, FEXCore::Core::CpuStateF
return HostCode;
}
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);
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
return std::make_unique<X86JITCore>(ctx, Thread, AllocateNewCodeBuffer(CompileThread ? X86JITCore::MAX_CODE_SIZE : X86JITCore::INITIAL_CODE_SIZE), CompileThread);
}
}
+19 -16
View File
@@ -112,26 +112,26 @@ private:
constexpr static uint8_t RA_64 = 3;
constexpr static uint8_t RA_XMM = 4;
IR::PhysicalRegister GetPhys(uint32_t Node);
IR::PhysicalRegister GetPhys(uint32_t Node) const;
bool IsFPR(uint32_t Node);
bool IsGPR(uint32_t Node);
bool IsFPR(uint32_t Node) const;
bool IsGPR(uint32_t Node) const;
template<uint8_t RAType>
Xbyak::Reg GetSrc(uint32_t Node);
Xbyak::Reg GetSrc(uint32_t Node) const;
template<uint8_t RAType>
std::pair<Xbyak::Reg, Xbyak::Reg> GetSrcPair(uint32_t Node);
std::pair<Xbyak::Reg, Xbyak::Reg> GetSrcPair(uint32_t Node) const;
template<uint8_t RAType>
Xbyak::Reg GetDst(uint32_t Node);
Xbyak::Reg GetDst(uint32_t Node) const;
Xbyak::Xmm GetSrc(uint32_t Node);
Xbyak::Xmm GetDst(uint32_t Node);
Xbyak::Xmm GetSrc(uint32_t Node) const;
Xbyak::Xmm GetDst(uint32_t Node) const;
Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale);
Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) const;
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr);
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value);
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr) const;
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const;
IR::RegisterAllocationPass *RAPass;
FEXCore::IR::RegisterAllocationData *RAData;
@@ -241,9 +241,7 @@ private:
DEF_OP(Sbfe);
DEF_OP(Select);
DEF_OP(VExtractToGPR);
DEF_OP(Float_ToGPR_ZU);
DEF_OP(Float_ToGPR_ZS);
DEF_OP(Float_ToGPR_U);
DEF_OP(Float_ToGPR_S);
DEF_OP(FCmp);
DEF_OP(F80Cmp);
@@ -281,16 +279,14 @@ private:
///< Conversion ops
DEF_OP(VInsGPR);
DEF_OP(VCastFromGPR);
DEF_OP(Float_FromGPR_U);
DEF_OP(Float_FromGPR_S);
DEF_OP(Float_FToF);
DEF_OP(Vector_UToF);
DEF_OP(Vector_SToF);
DEF_OP(Vector_FToZU);
DEF_OP(Vector_FToZS);
DEF_OP(Vector_FToU);
DEF_OP(Vector_FToS);
DEF_OP(Vector_FToF);
DEF_OP(Vector_FToI);
///< Flag ops
DEF_OP(GetHostFlag);
@@ -333,6 +329,7 @@ private:
DEF_OP(SplatVector);
DEF_OP(VMov);
DEF_OP(VAnd);
DEF_OP(VBic);
DEF_OP(VOr);
DEF_OP(VXor);
DEF_OP(VAdd);
@@ -343,8 +340,10 @@ private:
DEF_OP(VSQSub);
DEF_OP(VAddP);
DEF_OP(VAddV);
DEF_OP(VUMinV);
DEF_OP(VURAvg);
DEF_OP(VAbs);
DEF_OP(VPopcount);
DEF_OP(VFAdd);
DEF_OP(VFAddP);
DEF_OP(VFSub);
@@ -364,6 +363,8 @@ private:
DEF_OP(VSMax);
DEF_OP(VZip);
DEF_OP(VZip2);
DEF_OP(VUnZip);
DEF_OP(VUnZip2);
DEF_OP(VBSL);
DEF_OP(VCMPEQ);
DEF_OP(VCMPEQZ);
@@ -386,6 +387,7 @@ private:
DEF_OP(VInsElement);
DEF_OP(VInsScalarElement);
DEF_OP(VExtractElement);
DEF_OP(VDupElement);
DEF_OP(VExtr);
DEF_OP(VSLI);
DEF_OP(VSRI);
@@ -408,6 +410,7 @@ private:
DEF_OP(VSMull);
DEF_OP(VUMull2);
DEF_OP(VSMull2);
DEF_OP(VUABDL);
DEF_OP(VTBL1);
///< Encryption ops
@@ -425,7 +425,7 @@ DEF_OP(StoreFlag) {
mov(byte [STATE + (offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag)], al);
}
Xbyak::RegExp X86JITCore::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) const {
if (Offset.IsInvalid()) {
return Base;
} else {
+15 -13
View File
@@ -12,6 +12,10 @@ 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);
}
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(Fence) {
@@ -119,24 +123,22 @@ DEF_OP(SetRoundingMode) {
DEF_OP(Print) {
auto Op = IROp->C<IR::IROp_Print>();
for (auto &Reg : RA64)
push(Reg);
PushRegs();
if (IsGPR(Op->Header.Args[0].ID())) {
mov (rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
auto NumPush = RA64.size();
if (NumPush & 1)
sub(rsp, 8); // Align
mov(rax, reinterpret_cast<uintptr_t>(PrintValue));
}
else {
pextrq(rdi, GetSrc(Op->Header.Args[0].ID()), 0);
pextrq(rsi, GetSrc(Op->Header.Args[0].ID()), 1);
mov (rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
mov(rax, reinterpret_cast<uintptr_t>(PrintValue));
mov(rax, reinterpret_cast<uintptr_t>(PrintVectorValue));
}
call(rax);
if (NumPush & 1)
add(rsp, 8); // Align
for (uint32_t i = RA64.size(); i > 0; --i)
pop(RA64[i - 1]);
PopRegs();
}
#undef DEF_OP
@@ -139,6 +139,14 @@ DEF_OP(VAnd) {
vpand(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
}
DEF_OP(VBic) {
auto Op = IROp->C<IR::IROp_VBic>();
// This doesn't map directly to ARM
vpcmpeqd(xmm15, xmm15, xmm15);
vpxor(xmm15, GetSrc(Op->Header.Args[1].ID()), xmm15);
vpand(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), xmm15);
}
DEF_OP(VOr) {
auto Op = IROp->C<IR::IROp_VOr>();
vpor(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
@@ -355,6 +363,23 @@ DEF_OP(VAddV) {
movaps(Dest, xmm15);
}
DEF_OP(VUMinV) {
auto Op = IROp->C<IR::IROp_VUMinV>();
auto Src = GetSrc(Op->Header.Args[0].ID());
auto Dest = GetDst(Node);
switch (Op->Header.ElementSize) {
case 2: {
phminposuw(Dest, Src);
// Extract the upper bits which are zero, overwriting position
pextrw(eax, Dest, 2);
pinsrw(Dest, eax, 1);
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
DEF_OP(VURAvg) {
auto Op = IROp->C<IR::IROp_VURAvg>();
switch (Op->Header.ElementSize) {
@@ -393,6 +418,32 @@ DEF_OP(VAbs) {
}
}
DEF_OP(VPopcount) {
auto Op = IROp->C<IR::IROp_VPopcount>();
uint8_t OpSize = IROp->Size;
// This only supports 8bit popcount on 8byte to 16byte registers
auto Src = GetSrc(Op->Header.Args[0].ID());
auto Dest = GetDst(Node);
vpxor(xmm15, xmm15, xmm15);
uint8_t Elements = OpSize / Op->Header.ElementSize;
// This is disgustingly bad on x86-64 but we only need it for compatibility
switch (Op->Header.ElementSize) {
case 1: {
for (size_t i = 0; i < Elements; ++i) {
pextrb(eax, Src, i);
popcnt(eax, eax);
pinsrb(xmm15, eax, i);
}
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
movaps(Dest, xmm15);
}
DEF_OP(VFAdd) {
auto Op = IROp->C<IR::IROp_VFAdd>();
uint8_t OpSize = IROp->Size;
@@ -933,6 +984,112 @@ DEF_OP(VZip2) {
}
}
DEF_OP(VUnZip) {
auto Op = IROp->C<IR::IROp_VUnZip>();
uint8_t OpSize = IROp->Size;
if (OpSize == 8) {
LOGMAN_MSG_A("Unsupported registersize on VunZip");
}
else {
switch (Op->Header.ElementSize) {
case 1: {
// Shuffle low bits
mov(rax, 0x0E'0C'0A'08'06'04'02'00); // Lower
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
vmovq(xmm15, rax);
pinsrq(xmm15, rcx, 1);
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
// movlhps back to combine
vmovlhps(GetDst(Node), xmm14, xmm13);
break;
}
case 2: {
// Shuffle low bits
mov(rax, 0x0D'0C'09'08'05'04'01'00); // Lower
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
vmovq(xmm15, rax);
pinsrq(xmm15, rcx, 1);
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
// movlhps back to combine
vmovlhps(GetDst(Node), xmm14, xmm13);
break;
}
case 4: {
vshufps(GetDst(Node),
GetSrc(Op->Header.Args[0].ID()),
GetSrc(Op->Header.Args[1].ID()),
0b10'00'10'00);
break;
}
case 8: {
vshufpd(GetDst(Node),
GetSrc(Op->Header.Args[0].ID()),
GetSrc(Op->Header.Args[1].ID()),
0b0'0);
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
DEF_OP(VUnZip2) {
auto Op = IROp->C<IR::IROp_VUnZip2>();
uint8_t OpSize = IROp->Size;
if (OpSize == 8) {
LOGMAN_MSG_A("Unsupported registersize on VunZip");
}
else {
switch (Op->Header.ElementSize) {
case 1: {
// Shuffle low bits
mov(rax, 0x0F'0D'0B'09'07'05'03'01); // Lower
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
vmovq(xmm15, rax);
pinsrq(xmm15, rcx, 1);
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
// movlhps back to combine
vmovlhps(GetDst(Node), xmm14, xmm13);
break;
}
case 2: {
// Shuffle low bits
mov(rax, 0x0F'0E'0B'0A'07'06'03'02); // Lower
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
vmovq(xmm15, rax);
pinsrq(xmm15, rcx, 1);
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
// movlhps back to combine
vmovlhps(GetDst(Node), xmm14, xmm13);
break;
}
case 4: {
vshufps(GetDst(Node),
GetSrc(Op->Header.Args[0].ID()),
GetSrc(Op->Header.Args[1].ID()),
0b11'01'11'01);
break;
}
case 8: {
vshufpd(GetDst(Node),
GetSrc(Op->Header.Args[0].ID()),
GetSrc(Op->Header.Args[1].ID()),
0b1'1);
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
}
DEF_OP(VBSL) {
auto Op = IROp->C<IR::IROp_VBSL>();
vpand(xmm0, GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
@@ -1407,6 +1564,61 @@ DEF_OP(VExtractElement) {
}
}
DEF_OP(VDupElement) {
auto Op = IROp->C<IR::IROp_VDupElement>();
switch (Op->Header.ElementSize) {
case 1: {
// First extract the index
pextrb(eax, GetSrc(Op->Header.Args[0].ID()), Op->Index);
// Insert it in to the first element of the destination
pinsrb(GetDst(Node), eax, 0);
pinsrb(GetDst(Node), eax, 1);
// Shuffle low elements
vpshuflw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
// Insert element in to the first upper 64bit element
pinsrb(GetDst(Node), eax, 8);
pinsrb(GetDst(Node), eax, 9);
// Shuffle high elements
vpshufhw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
break;
}
case 2: {
// First extract the index
pextrw(eax, GetSrc(Op->Header.Args[0].ID()), Op->Index);
// Insert it in to the first element of the destination
pinsrw(GetDst(Node), eax, 0);
// Shuffle low elements
vpshuflw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
// Insert element in to the first upper 64bit element
pinsrw(GetDst(Node), eax, 4);
// Shuffle high elements
vpshufhw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
break;
}
case 4: {
vpshufd(GetDst(Node),
GetSrc(Op->Header.Args[0].ID()),
(Op->Index << 0) |
(Op->Index << 2) |
(Op->Index << 4) |
(Op->Index << 6));
break;
}
case 8: {
vshufpd(GetDst(Node),
GetSrc(Op->Header.Args[0].ID()),
GetSrc(Op->Header.Args[0].ID()),
(Op->Index << 0) |
(Op->Index << 1));
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
DEF_OP(VExtr) {
auto Op = IROp->C<IR::IROp_VExtr>();
uint8_t OpSize = IROp->Size;
@@ -1460,14 +1672,36 @@ DEF_OP(VUShrI) {
DEF_OP(VSShrI) {
auto Op = IROp->C<IR::IROp_VSShrI>();
movapd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
auto Dest = GetDst(Node);
movapd(Dest, GetSrc(Op->Header.Args[0].ID()));
switch (Op->Header.ElementSize) {
case 1: {
// This isn't a native instruction on x86
uint8_t OpSize = IROp->Size;
uint8_t Elements = OpSize / Op->Header.ElementSize;
for (int i = 0; i < Elements; ++i) {
pextrb(eax, Dest, i);
movsx(eax, al);
sar(al, Op->BitShift);
pinsrb(Dest, eax, i);
}
break;
}
case 2: {
psraw(GetDst(Node), Op->BitShift);
psraw(Dest, Op->BitShift);
break;
}
case 4: {
psrad(GetDst(Node), Op->BitShift);
psrad(Dest, Op->BitShift);
break;
}
case 8: {
// This isn't a native instruction on x86
for (int i = 0; i < 2; ++i) {
pextrq(rax, Dest, i);
sar(rax, Op->BitShift);
pinsrq(Dest, rax, i);
}
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
@@ -1872,6 +2106,27 @@ DEF_OP(VSMull2) {
}
}
DEF_OP(VUABDL) {
auto Op = IROp->C<IR::IROp_VUABDL>();
switch (Op->Header.ElementSize) {
case 2: {
pmovzxbw(xmm14, GetSrc(Op->Header.Args[0].ID()));
pmovzxbw(xmm15, GetSrc(Op->Header.Args[1].ID()));
vpsubw(GetDst(Node), xmm14, xmm15);
vpabsw(GetDst(Node), GetDst(Node));
break;
}
case 4: {
pmovzxwd(xmm14, GetSrc(Op->Header.Args[0].ID()));
pmovzxwd(xmm15, GetSrc(Op->Header.Args[1].ID()));
vpsubd(GetDst(Node), xmm14, xmm15);
vpabsd(GetDst(Node), GetDst(Node));
break;
}
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
}
DEF_OP(VTBL1) {
auto Op = IROp->C<IR::IROp_VTBL1>();
uint8_t OpSize = IROp->Size;
@@ -1901,6 +2156,7 @@ void X86JITCore::RegisterVectorHandlers() {
REGISTER_OP(SPLATVECTOR4, SplatVector);
REGISTER_OP(VMOV, VMov);
REGISTER_OP(VAND, VAnd);
REGISTER_OP(VBIC, VBic);
REGISTER_OP(VOR, VOr);
REGISTER_OP(VXOR, VXor);
REGISTER_OP(VADD, VAdd);
@@ -1911,8 +2167,10 @@ void X86JITCore::RegisterVectorHandlers() {
REGISTER_OP(VSQSUB, VSQSub);
REGISTER_OP(VADDP, VAddP);
REGISTER_OP(VADDV, VAddV);
REGISTER_OP(VUMINV, VUMinV);
REGISTER_OP(VURAVG, VURAvg);
REGISTER_OP(VABS, VAbs);
REGISTER_OP(VPOPCOUNT, VPopcount);
REGISTER_OP(VFADD, VFAdd);
REGISTER_OP(VFADDP, VFAddP);
REGISTER_OP(VFSUB, VFSub);
@@ -1932,6 +2190,8 @@ void X86JITCore::RegisterVectorHandlers() {
REGISTER_OP(VSMAX, VSMax);
REGISTER_OP(VZIP, VZip);
REGISTER_OP(VZIP2, VZip2);
REGISTER_OP(VUNZIP, VUnZip);
REGISTER_OP(VUNZIP2, VUnZip2);
REGISTER_OP(VBSL, VBSL);
REGISTER_OP(VCMPEQ, VCMPEQ);
REGISTER_OP(VCMPEQZ, VCMPEQZ);
@@ -1954,6 +2214,7 @@ void X86JITCore::RegisterVectorHandlers() {
REGISTER_OP(VINSELEMENT, VInsElement);
REGISTER_OP(VINSSCALARELEMENT, VInsScalarElement);
REGISTER_OP(VEXTRACTELEMENT, VExtractElement);
REGISTER_OP(VDUPELEMENT, VDupElement);
REGISTER_OP(VEXTR, VExtr);
REGISTER_OP(VSLI, VSLI);
REGISTER_OP(VSRI, VSRI);
@@ -1977,6 +2238,7 @@ void X86JITCore::RegisterVectorHandlers() {
REGISTER_OP(VSMULL, VSMull);
REGISTER_OP(VUMULL2, VUMull2);
REGISTER_OP(VSMULL2, VSMull2);
REGISTER_OP(VUABDL, VUABDL);
REGISTER_OP(VTBL1, VTBL1);
#undef REGISTER_OP
}
+2 -2
View File
@@ -98,8 +98,8 @@ public:
void HintUsedRange(uint64_t Address, uint64_t Size);
uintptr_t GetL1Pointer() { return L1Pointer; }
uintptr_t GetPagePointer() { return PagePointer; }
uintptr_t GetL1Pointer() const { return L1Pointer; }
uintptr_t GetPagePointer() const { return PagePointer; }
uintptr_t GetVirtualMemorySize() const { return VirtualMemSize; }
constexpr static size_t L1_ENTRIES = 1 * 1024 * 1024; // Must be a power of 2
File diff suppressed because it is too large. Load diff
+30 -27
View File
@@ -105,7 +105,7 @@ public:
void ResetWorkingList();
void ResetDecodeFailure() { DecodeFailure = false; }
bool HadDecodeFailure() { return DecodeFailure; }
bool HadDecodeFailure() const { return DecodeFailure; }
void BeginFunction(uint64_t RIP, std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
void Finalize();
@@ -260,12 +260,6 @@ public:
template<size_t ElementSize>
void PSUBQOp(OpcodeArgs);
template<size_t ElementSize>
void PMINUOp(OpcodeArgs);
template<size_t ElementSize>
void PMAXUOp(OpcodeArgs);
void PMINSWOp(OpcodeArgs);
void PMAXSWOp(OpcodeArgs);
template<size_t ElementSize>
void MOVMSKOp(OpcodeArgs);
void MOVMSKOpOne(OpcodeArgs);
template<size_t ElementSize>
@@ -275,10 +269,6 @@ public:
void PSHUFBOp(OpcodeArgs);
template<size_t ElementSize, bool HalfSize, bool Low>
void PSHUFDOp(OpcodeArgs);
template<size_t ElementSize>
void PCMPEQOp(OpcodeArgs);
template<size_t ElementSize>
void PCMPGTOp(OpcodeArgs);
void MOVDOp(OpcodeArgs);
template<size_t ElementSize, bool Scalar, uint32_t SrcIndex>
void PSRLDOp(OpcodeArgs);
@@ -297,21 +287,21 @@ public:
template<size_t ElementSize>
void PAVGOp(OpcodeArgs);
void MOVDDUPOp(OpcodeArgs);
template<size_t DstElementSize, bool Signed>
template<size_t DstElementSize>
void CVTGPR_To_FPR(OpcodeArgs);
template<size_t SrcElementSize, bool Signed, bool HostRoundingMode>
template<size_t SrcElementSize, bool HostRoundingMode>
void CVTFPR_To_GPR(OpcodeArgs);
template<size_t SrcElementSize, bool Signed, bool Widen>
template<size_t SrcElementSize, bool Widen>
void Vector_CVT_Int_To_Float(OpcodeArgs);
template<size_t DstElementSize, size_t SrcElementSize>
void Scalar_CVT_Float_To_Float(OpcodeArgs);
template<size_t DstElementSize, size_t SrcElementSize>
void Vector_CVT_Float_To_Float(OpcodeArgs);
template<size_t SrcElementSize, bool Signed, bool Narrow, bool HostRoundingMode>
template<size_t SrcElementSize, bool Narrow, bool HostRoundingMode>
void Vector_CVT_Float_To_Int(OpcodeArgs);
template<size_t SrcElementSize, bool Signed, bool Widen>
void MMX_To_XMM_Vector_CVT_Int_To_Float(OpcodeArgs);
template<size_t SrcElementSize, bool Signed, bool Narrow, bool HostRoundingMode>
template<size_t SrcElementSize, bool Narrow, bool HostRoundingMode>
void XMM_To_MMX_Vector_CVT_Float_To_Int(OpcodeArgs);
void MASKMOVOp(OpcodeArgs);
void MOVBetweenGPR_FPR(OpcodeArgs);
@@ -325,17 +315,13 @@ public:
void ANDNOp(OpcodeArgs);
template<size_t ElementSize>
void PINSROp(OpcodeArgs);
void InsertPSOp(OpcodeArgs);
template<size_t ElementSize>
void PExtrOp(OpcodeArgs);
template<size_t ElementSize, bool Signed>
void PMULOp(OpcodeArgs);
template<size_t ElementSize>
void PSIGN(OpcodeArgs);
template<size_t ElementSize>
void PABS(OpcodeArgs);
// X87 Ops
template<size_t width>
void FLD(OpcodeArgs);
@@ -470,6 +456,23 @@ public:
void AESDecLastOp(OpcodeArgs);
void AESKeyGenAssist(OpcodeArgs);
template<size_t ElementSize, size_t DstElementSize, bool Signed>
void ExtendVectorElements(OpcodeArgs);
template<size_t ElementSize, bool Scalar>
void VectorRound(OpcodeArgs);
template<size_t ElementSize>
void VectorBlend(OpcodeArgs);
template<size_t ElementSize>
void VectorVariableBlend(OpcodeArgs);
void PTestOp(OpcodeArgs);
void PHMINPOSUWOp(OpcodeArgs);
template<size_t ElementSize>
void DPPOp(OpcodeArgs);
void MPSADBWOp(OpcodeArgs);
void UnimplementedOp(OpcodeArgs);
#undef OpcodeArgs
@@ -494,8 +497,8 @@ private:
void StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, FEXCore::X86Tables::DecodedOperand const& Operand, OrderedNode *const Src, int8_t Align);
void StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, OrderedNode *const Src, int8_t Align);
uint8_t GetDstSize(FEXCore::X86Tables::DecodedOp Op);
uint8_t GetSrcSize(FEXCore::X86Tables::DecodedOp Op);
uint8_t GetDstSize(FEXCore::X86Tables::DecodedOp Op) const;
uint8_t GetSrcSize(FEXCore::X86Tables::DecodedOp Op) const;
template<unsigned BitOffset>
void SetRFLAG(OrderedNode *Value);
@@ -525,12 +528,12 @@ private:
OrderedNode * GetX87Top();
void SetX87Top(OrderedNode *Value);
bool DestIsLockedMem(FEXCore::X86Tables::DecodedOp Op) {
return Op->Dest.TypeNone.Type !=FEXCore::X86Tables::DecodedOperand::TYPE_GPR && (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK);
bool DestIsLockedMem(FEXCore::X86Tables::DecodedOp Op) const {
return DestIsMem(Op) && (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK) != 0;
}
bool DestIsMem(FEXCore::X86Tables::DecodedOp Op) {
return Op->Dest.TypeNone.Type !=FEXCore::X86Tables::DecodedOperand::TYPE_GPR;
bool DestIsMem(FEXCore::X86Tables::DecodedOp Op) const {
return !Op->Dest.IsGPR();
}
void CreateJumpBlocks(std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
@@ -41,10 +41,10 @@ void InitializeH0F38Tables() {
{OPD(PF_38_NONE, 0x0B), 1, X86InstInfo{"PMULHRSW", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
{OPD(PF_38_66, 0x0B), 1, X86InstInfo{"PMULHRSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x10), 1, X86InstInfo{"PBLENDVB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x14), 1, X86InstInfo{"BLENDVPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x15), 1, X86InstInfo{"BLENDVPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x17), 1, X86InstInfo{"PTEST", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x10), 1, X86InstInfo{"PBLENDVB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x14), 1, X86InstInfo{"BLENDVPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x15), 1, X86InstInfo{"BLENDVPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x17), 1, X86InstInfo{"PTEST", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_NONE, 0x1C), 1, X86InstInfo{"PABSB", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
{OPD(PF_38_66, 0x1C), 1, X86InstInfo{"PABSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_NONE, 0x1D), 1, X86InstInfo{"PABSW", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
@@ -52,34 +52,34 @@ void InitializeH0F38Tables() {
{OPD(PF_38_NONE, 0x1E), 1, X86InstInfo{"PABSD", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
{OPD(PF_38_66, 0x1E), 1, X86InstInfo{"PABSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x20), 1, X86InstInfo{"PMOVSXBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x21), 1, X86InstInfo{"PMOVSXBD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x22), 1, X86InstInfo{"PMOVSXBQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x23), 1, X86InstInfo{"PMOVSXWD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x24), 1, X86InstInfo{"PMOVSXWQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{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, 0x20), 1, X86InstInfo{"PMOVSXBW", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x21), 1, X86InstInfo{"PMOVSXBD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x22), 1, X86InstInfo{"PMOVSXBQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_16BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x23), 1, X86InstInfo{"PMOVSXWD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x24), 1, X86InstInfo{"PMOVSXWQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x25), 1, X86InstInfo{"PMOVSXDQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x28), 1, X86InstInfo{"PMULDQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x29), 1, X86InstInfo{"PCMPEQQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 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, 0x2B), 1, X86InstInfo{"PACKUSDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x30), 1, X86InstInfo{"PMOVZXBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x31), 1, X86InstInfo{"PMOVZXBD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x32), 1, X86InstInfo{"PMOVZXBQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x33), 1, X86InstInfo{"PMOVZXWD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x34), 1, X86InstInfo{"PMOVZXWQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x35), 1, X86InstInfo{"PMOVZXDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x38), 1, X86InstInfo{"PMINSB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x39), 1, X86InstInfo{"PMINSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x3A), 1, X86InstInfo{"PMINUW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x3B), 1, X86InstInfo{"PMINUD", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x3C), 1, X86InstInfo{"PMAXSB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x3D), 1, X86InstInfo{"PMAXSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x3E), 1, X86InstInfo{"PMAXUW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x3F), 1, X86InstInfo{"PMAXUD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x30), 1, X86InstInfo{"PMOVZXBW", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x31), 1, X86InstInfo{"PMOVZXBD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x32), 1, X86InstInfo{"PMOVZXBQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_16BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x33), 1, X86InstInfo{"PMOVZXWD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x34), 1, X86InstInfo{"PMOVZXWQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x35), 1, X86InstInfo{"PMOVZXDQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x38), 1, X86InstInfo{"PMINSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x39), 1, X86InstInfo{"PMINSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x3A), 1, X86InstInfo{"PMINUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x3B), 1, X86InstInfo{"PMINUD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x3C), 1, X86InstInfo{"PMAXSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x3D), 1, X86InstInfo{"PMAXSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x3E), 1, X86InstInfo{"PMAXUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x3F), 1, X86InstInfo{"PMAXUD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x40), 1, X86InstInfo{"PMULLD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x41), 1, X86InstInfo{"PHMINPOSUW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x40), 1, X86InstInfo{"PMULLD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x41), 1, X86InstInfo{"PHMINPOSUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0xDB), 1, X86InstInfo{"AESIMC", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0xDC), 1, X86InstInfo{"AESENC", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
@@ -16,26 +16,26 @@ void InitializeH0F3ATables(Context::OperatingMode Mode) {
const U16U8InfoStruct H0F3ATable[] = {
{OPD(0, PF_3A_NONE, 0x0F), 1, X86InstInfo{"PALIGNR", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 1, nullptr}},
{OPD(0, PF_3A_66, 0x08), 1, X86InstInfo{"ROUNDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x09), 1, X86InstInfo{"ROUNDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x0A), 1, X86InstInfo{"ROUNDSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x0B), 1, X86InstInfo{"ROUNDSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x0C), 1, X86InstInfo{"BLENDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x0D), 1, X86InstInfo{"BLENDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x0E), 1, X86InstInfo{"PBLENDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x08), 1, X86InstInfo{"ROUNDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x09), 1, X86InstInfo{"ROUNDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x0A), 1, X86InstInfo{"ROUNDSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x0B), 1, X86InstInfo{"ROUNDSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x0C), 1, X86InstInfo{"BLENDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x0D), 1, X86InstInfo{"BLENDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x0E), 1, X86InstInfo{"PBLENDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x0F), 1, X86InstInfo{"PALIGNR", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x14), 1, X86InstInfo{"PEXTRB", TYPE_INST, GenFlagsSizes(SIZE_8BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x15), 1, X86InstInfo{"PEXTRW", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x16), 1, X86InstInfo{"PEXTRD", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x17), 1, X86InstInfo{"EXTRACTPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x17), 1, X86InstInfo{"EXTRACTPS", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x20), 1, X86InstInfo{"PINSRB", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_8BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_SRC_GPR, 1, nullptr}},
{OPD(0, PF_3A_66, 0x21), 1, X86InstInfo{"INSERTPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x20), 1, X86InstInfo{"PINSRB", TYPE_INST, GenFlagsDstSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_SRC_GPR, 1, nullptr}},
{OPD(0, PF_3A_66, 0x21), 1, X86InstInfo{"INSERTPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x22), 1, X86InstInfo{"PINSRD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_SRC_GPR, 1, nullptr}},
{OPD(0, PF_3A_66, 0x40), 1, X86InstInfo{"DPPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x41), 1, X86InstInfo{"DPPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x42), 1, X86InstInfo{"MPSADBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x40), 1, X86InstInfo{"DPPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x41), 1, X86InstInfo{"DPPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x42), 1, X86InstInfo{"MPSADBW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(0, PF_3A_66, 0x44), 1, X86InstInfo{"PCLMULQDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(0, PF_3A_66, 0x60), 1, X86InstInfo{"PCMPESTRM", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
+168 -99
View File
@@ -60,6 +60,12 @@
"constexpr static uint8_t ROUND_MODE_TOWARDS_ZERO = 3",
"constexpr static uint8_t ROUND_MODE_FLUSH_TO_ZERO = 1 << 2",
"static constexpr FEXCore::IR::RoundType Round_Nearest {ROUND_MODE_NEAREST}",
"static constexpr FEXCore::IR::RoundType Round_Negative_Infinity {ROUND_MODE_NEGATIVE_INFINITY}",
"static constexpr FEXCore::IR::RoundType Round_Positive_Infinity {ROUND_MODE_POSITIVE_INFINITY}",
"static constexpr FEXCore::IR::RoundType Round_Towards_Zero {ROUND_MODE_TOWARDS_ZERO} /* Truncate */",
"static constexpr FEXCore::IR::RoundType Round_Host {ROUND_MODE_TOWARDS_ZERO + 1}",
"constexpr static FEXCore::IR::MemOffsetType MEM_OFFSET_SXTX {0};",
"constexpr static FEXCore::IR::MemOffsetType MEM_OFFSET_UXTW {1};",
"constexpr static FEXCore::IR::MemOffsetType MEM_OFFSET_SXTW {2};"
@@ -1477,24 +1483,6 @@
]
},
"Float_ToGPR_U": {
"Desc": ["Moves the scalar element to a GPR with conversion",
"Converts the 32bit or 64bit float to an unsigned integer",
"Rounding mode determined by host flag's rounding mode"
],
"OpClass": "ALU",
"HasDest": true,
"DestClass": "GPR",
"DestSize": "ElementSize",
"SSAArgs": "1",
"SSANames": [
"Scalar"
],
"Args": [
"uint8_t", "ElementSize"
]
},
"Float_ToGPR_S": {
"Desc": ["Moves the scalar element to a GPR with conversion",
"Converts the 32bit or 64bit float to an signed integer",
@@ -1503,30 +1491,16 @@
"OpClass": "ALU",
"HasDest": true,
"DestClass": "GPR",
"DestSize": "ElementSize",
"DestSize": "DestElementSize",
"SSAArgs": "1",
"SSANames": [
"Scalar"
],
"Args": [
"uint8_t", "ElementSize"
]
},
"Float_ToGPR_ZU": {
"Desc": ["Moves the scalar element to a GPR with conversion",
"Converts the 32bit or 64bit float to an unsigned integer rounding towards zero (Truncating)"
],
"OpClass": "ALU",
"HasDest": true,
"DestClass": "GPR",
"DestSize": "ElementSize",
"SSAArgs": "1",
"SSANames": [
"Scalar"
"HelperArgs": [
"uint8_t", "DestElementSize"
],
"Args": [
"uint8_t", "ElementSize"
"uint8_t", "SrcElementSize"
]
},
@@ -1537,13 +1511,16 @@
"OpClass": "ALU",
"HasDest": true,
"DestClass": "GPR",
"DestSize": "ElementSize",
"DestSize": "DestElementSize",
"SSAArgs": "1",
"SSANames": [
"Scalar"
],
"HelperArgs": [
"uint8_t", "DestElementSize"
],
"Args": [
"uint8_t", "ElementSize"
"uint8_t", "SrcElementSize"
]
},
@@ -1664,6 +1641,23 @@
]
},
"VBic": {
"OpClass": "Vector",
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "2",
"SSANames": [
"Vector1",
"Vector2"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"VOr": {
"OpClass": "Vector",
"HasDest": true,
@@ -1809,8 +1803,8 @@
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "2",
"SSANames": [
"Vector1",
"Vector2"
"VectorLower",
"VectorUpper"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
@@ -1837,6 +1831,25 @@
]
},
"VUMinV": {
"OpClass": "Vector",
"Desc": ["Does a horizontal vector unsigned minimum of elements across the source vector",
"Result is a zero extended scalar"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"VURAvg": {
"OpClass": "Vector",
"Desc": ["Does an unsigned rounded average", "dst_elem = (src1_elem + src2_elem + 1) >> 1"],
@@ -1873,6 +1886,24 @@
]
},
"VPopcount": {
"OpClass": "Vector",
"Desc": ["Does a popcount for each element of the register"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"VFAdd": {
"OpClass": "Vector",
"HasDest": true,
@@ -1899,8 +1930,8 @@
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "2",
"SSANames": [
"Vector1",
"Vector2"
"VectorLow",
"VectorHigh"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
@@ -2192,6 +2223,40 @@
]
},
"VUnZip": {
"OpClass": "Vector",
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "2",
"SSANames": [
"Lower",
"Upper"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"VUnZip2": {
"OpClass": "Vector",
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "2",
"SSANames": [
"Lower",
"Upper"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"VBSL": {
"Desc": ["Does a vector bitwise select.",
"If the bit in the field is 1 then the corresponding bit is pulled from VectorTrue",
@@ -2583,6 +2648,26 @@
]
},
"VDupElement": {
"Desc": ["Duplicates one element from the source register across the whole register"],
"OpClass": "Vector",
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
],
"Args": [
"uint8_t", "Index"
]
},
"VExtr": {
"Desc": ["Concats two vector registers together and extracts a full width register from the element index",
"Index is an element index. So it is offset by ElementSize argument",
@@ -2935,27 +3020,6 @@
]
},
"Float_FromGPR_U": {
"OpClass": "Conv",
"Desc": ["Scalar op: Converts unsigned GPR to Scalar float",
"Zeroes the upper bits of the vector register"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "DstElementSize",
"NumElements": "1",
"SSAArgs": "1",
"SSANames": [
"GPR"
],
"HelperArgs": [
"uint8_t", "DstElementSize"
],
"Args": [
"uint8_t", "SrcElementSize"
]
},
"Float_FromGPR_S": {
"OpClass": "Conv",
"Desc": ["Scalar op: Converts signed GPR to Scalar float",
@@ -3032,25 +3096,6 @@
]
},
"Vector_FToU": {
"OpClass": "Conv",
"Desc": ["Vector op: Converts float to unsigned integer",
"Rounding mode determined by host rounding mode"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"Vector_FToS": {
"OpClass": "Conv",
"Desc": ["Vector op: Converts float to signed integer, rounding towards zero",
@@ -3070,23 +3115,6 @@
]
},
"Vector_FToZU": {
"OpClass": "Conv",
"Desc": "Vector op: Converts float to unsigned integer, rounding towards zero",
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"Vector_FToZS": {
"OpClass": "Conv",
"Desc": "Vector op: Converts float to signed integer, rounding towards zero",
@@ -3124,6 +3152,28 @@
]
},
"Vector_FToI": {
"OpClass": "Conv",
"Desc": ["Vector op: Rounds float to integral",
"Rounding mode determined by argument"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
],
"Args":[
"FEXCore::IR::RoundType", "Round"
]
},
"VUMul": {
"OpClass": "Vector",
"HasDest": true,
@@ -3231,6 +3281,25 @@
]
},
"VUABDL": {
"OpClass": "Vector",
"Desc": ["Unsigned Absolute Difference Long"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / (ElementSize << 1)",
"SSAArgs": "2",
"SSANames": [
"Vector1",
"Vector2"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"VTBL1": {
"Desc": ["Does a vector table lookup from one register in to the destination",
"Lookup is byte sized per byte element.",
+13 -2
View File
@@ -37,7 +37,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, CondClassType Arg) {
std::array<std::string, 22> CondNames = {
static constexpr std::array<std::string_view, 22> CondNames = {
"EQ",
"NEQ",
"UGE",
@@ -66,7 +66,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, MemOffsetType Arg) {
std::array<std::string, 3> Names = {
static constexpr std::array<std::string_view, 3> Names = {
"SXTX",
"UXTW",
"SXTW",
@@ -154,6 +154,17 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
}
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, FEXCore::IR::RoundType Arg) {
switch (Arg) {
case FEXCore::IR::Round_Nearest: *out << "Nearest"; break;
case FEXCore::IR::Round_Negative_Infinity: *out << "-Inf"; break;
case FEXCore::IR::Round_Positive_Infinity: *out << "+Inf"; break;
case FEXCore::IR::Round_Towards_Zero: *out << "Towards Zero"; break;
case FEXCore::IR::Round_Host: *out << "Host"; break;
default: *out << "<Unknown Round Type>"; break;
}
}
void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData) {
auto HeaderOp = IR->GetHeader();
+106 -108
View File
@@ -66,7 +66,8 @@ std::string DecodeErrorToString(DecodeFailure Failure) {
case DecodeFailure::DECODE_INVALID_CONDFLAG: return "Invalid Conditional name";
case DecodeFailure::DECODE_INVALID_MEMOFFSETTYPE: return "Invalid Memory Offset Type";
case DecodeFailure::DECODE_INVALID_FENCETYPE: return "Invalid Fence Type";
};
}
return "Unknown Error";
}
std::unordered_map<std::string_view, FEXCore::IR::IROps> NameToOpMap;
@@ -74,22 +75,22 @@ std::unordered_map<std::string_view, FEXCore::IR::IROps> NameToOpMap;
class IRParser: public FEXCore::IR::IREmitter {
public:
template<typename Type>
std::pair<DecodeFailure, Type> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, Type> DecodeValue(const std::string &Arg) {
return {DecodeFailure::DECODE_UNKNOWN_TYPE, {}};
}
template<>
std::pair<DecodeFailure, uint8_t> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, uint8_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint8_t Result = strtoul(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
}
template<>
std::pair<DecodeFailure, bool> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, bool> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint8_t Result = strtoul(&Arg.at(1), nullptr, 0);
@@ -98,7 +99,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, uint16_t> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, uint16_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint16_t Result = strtoul(&Arg.at(1), nullptr, 0);
@@ -107,7 +108,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, uint32_t> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, uint32_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint32_t Result = strtoul(&Arg.at(1), nullptr, 0);
@@ -116,7 +117,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, uint64_t> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, uint64_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint64_t Result = strtoull(&Arg.at(1), nullptr, 0);
@@ -125,7 +126,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, int64_t> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, int64_t> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
int64_t Result = (int64_t)strtoull(&Arg.at(1), nullptr, 0);
@@ -134,7 +135,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, IR::SHA256Sum> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, IR::SHA256Sum> DecodeValue(const std::string &Arg) {
IR::SHA256Sum Result;
if (Arg.at(0) != 's' || Arg.at(1) != 'h' || Arg.at(2) != 'a' || Arg.at(3) != '2' || Arg.at(4) != '5' || Arg.at(5) != '6' || Arg.at(6) != ':')
@@ -165,7 +166,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, FEXCore::IR::RegisterClassType> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, FEXCore::IR::RegisterClassType> DecodeValue(const std::string &Arg) {
if (Arg == "GPR") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::GPRClass};
}
@@ -183,7 +184,7 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, FEXCore::IR::TypeDefinition> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, FEXCore::IR::TypeDefinition> DecodeValue(const std::string &Arg) {
uint8_t Size{}, Elements{1};
int NumArgs = sscanf(Arg.c_str(), "i%hhdv%hhd", &Size, &Elements);
@@ -195,8 +196,8 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, FEXCore::IR::CondClassType> DecodeValue(std::string &Arg) {
std::array<std::string, 22> CondNames = {
std::pair<DecodeFailure, FEXCore::IR::CondClassType> DecodeValue(const std::string &Arg) {
static constexpr std::array<std::string_view, 22> CondNames = {
"EQ",
"NEQ",
"UGE",
@@ -230,8 +231,8 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, FEXCore::IR::MemOffsetType> DecodeValue(std::string &Arg) {
std::array<std::string, 3> Names = {
std::pair<DecodeFailure, FEXCore::IR::MemOffsetType> DecodeValue(const std::string &Arg) {
static constexpr std::array<std::string_view, 3> Names = {
"SXTX",
"UXTW",
"SXTW",
@@ -246,8 +247,8 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, FEXCore::IR::FenceType> DecodeValue(std::string &Arg) {
std::array<std::string, 3> Names = {
std::pair<DecodeFailure, FEXCore::IR::FenceType> DecodeValue(const std::string &Arg) {
static constexpr std::array<std::string_view, 3> Names = {
"Loads",
"Stores",
"LoadStores",
@@ -262,23 +263,22 @@ class IRParser: public FEXCore::IR::IREmitter {
}
template<>
std::pair<DecodeFailure, OrderedNode*> DecodeValue(std::string &Arg) {
std::pair<DecodeFailure, OrderedNode*> DecodeValue(const std::string &Arg) {
if (Arg.at(0) != '%') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
// Strip off the type qualifier from the ssa value
size_t ArgEnd = std::string::npos;
std::string SSAName = trim(Arg);
ArgEnd = SSAName.find_first_of(" ");
const size_t ArgEnd = SSAName.find_first_of(' ');
if (ArgEnd != std::string::npos) {
SSAName = SSAName.substr(0, ArgEnd);
}
SSAName = SSAName.substr(0, ArgEnd);
}
// Forward declarations may make this not succed
// Forward declarations may make this not succed
auto Op = SSANameMapper.find(SSAName);
if (Op == SSANameMapper.end()) {
if (Op == SSANameMapper.end()) {
return {DecodeFailure::DECODE_UNKNOWN_SSA, nullptr};
}
}
return {DecodeFailure::DECODE_OKAY, Op->second};
}
@@ -302,21 +302,21 @@ class IRParser: public FEXCore::IR::IREmitter {
IRParser(std::istream *text) {
InitializeStaticTables();
std::string TmpLine;
while (!text->eof()) {
std::getline(*text, TmpLine);
if (text->eof()) {
break;
}
if (text->eof()) {
break;
}
if (text->fail()) {
LogMan::Msg::E("Failed to getline on line: %ld", Lines.size());
LogMan::Msg::EFmt("Failed to getline on line: {}", Lines.size());
return;
}
Lines.emplace_back(TmpLine);
}
ResetWorkingList();
ResetWorkingList();
Loaded = Parse();
}
@@ -327,11 +327,11 @@ class IRParser: public FEXCore::IR::IREmitter {
bool Parse() {
auto CheckPrintError = [&](LineDefinition &Def, DecodeFailure Failure) -> bool {
const auto CheckPrintError = [&](const LineDefinition &Def, DecodeFailure Failure) -> bool {
if (Failure != DecodeFailure::DECODE_OKAY) {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("Value Couldn't be decoded due to %s", DecodeErrorToString(Failure).c_str());
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("Value Couldn't be decoded due to {}", DecodeErrorToString(Failure));
return false;
}
@@ -339,13 +339,13 @@ class IRParser: public FEXCore::IR::IREmitter {
};
// String parse every line for our definitions
for (size_t i = 0; i < Lines.size(); ++i) {
std::string Line = Lines[i];
for (size_t i = 0; i < Lines.size(); ++i) {
std::string Line = Lines[i];
LineDefinition Def{};
CurrentDef = &Def;
CurrentDef = &Def;
Def.LineNumber = i;
Line = trim(Line);
Line = trim(Line);
// Skip empty lines
if (Line.empty()) {
@@ -359,35 +359,37 @@ class IRParser: public FEXCore::IR::IREmitter {
}
size_t CurrentPos{};
// Let's see if this node is assigning something first
if (Line[0] == '%') {
// Let's see if this node is assigning something first
if (Line[0] == '%') {
size_t DefinitionEnd = std::string::npos;
if ((DefinitionEnd = Line.find_first_of("=", CurrentPos)) != std::string::npos) {
if ((DefinitionEnd = Line.find_first_of('=', CurrentPos)) != std::string::npos) {
Def.Definition = Line.substr(0, DefinitionEnd);
Def.Definition = trim(Def.Definition);
Def.HasDefinition = true;
CurrentPos = DefinitionEnd + 1; // +1 to ensure we go past then assignment
}
else {
LogMan::Msg::E("Error on Line: %d", i);
LogMan::Msg::E("%s", Lines[i].c_str());
LogMan::Msg::E("SSA declaration without assignment");
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("SSA declaration without assignment");
return false;
}
}
}
// Check if we are pulling in some IR from the IR Printer
// Prints (%ssa%d) at the start of lines without a definition
if (Line[0] == '(') {
size_t DefinitionEnd = std::string::npos;
if ((DefinitionEnd = Line.find_first_of(")", CurrentPos)) != std::string::npos) {
if ((DefinitionEnd = Line.find_first_of(')', CurrentPos)) != std::string::npos) {
size_t SSAEnd = std::string::npos;
if ((SSAEnd = Line.find_last_of(" ", DefinitionEnd)) != std::string::npos) {
if ((SSAEnd = Line.find_last_of(' ', DefinitionEnd)) != std::string::npos) {
std::string Type = Line.substr(SSAEnd + 1, DefinitionEnd - SSAEnd - 1);
Type = trim(Type);
auto DefinitionSize = DecodeValue<FEXCore::IR::TypeDefinition>(Type);
if (!CheckPrintError(Def, DefinitionSize.first)) return false;
if (!CheckPrintError(Def, DefinitionSize.first)) {
return false;
}
Def.Size = DefinitionSize.second;
}
@@ -396,9 +398,9 @@ class IRParser: public FEXCore::IR::IREmitter {
CurrentPos = DefinitionEnd + 1;
}
else {
LogMan::Msg::E("Error on Line: %d", i);
LogMan::Msg::E("%s", Lines[i].c_str());
LogMan::Msg::E("SSA value with numbered SSA provided but no closing parentheses");
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("SSA value with numbered SSA provided but no closing parentheses");
return false;
}
}
@@ -406,7 +408,7 @@ class IRParser: public FEXCore::IR::IREmitter {
if (Def.HasDefinition) {
// Let's check if we have a size declared with this variable
size_t NameEnd = std::string::npos;
if ((NameEnd = Def.Definition.find_first_of(" ")) != std::string::npos) {
if ((NameEnd = Def.Definition.find_first_of(' ')) != std::string::npos) {
std::string Type = Def.Definition.substr(NameEnd + 1);
Type = trim(Type);
Def.Definition = trim(Def.Definition.substr(0, NameEnd));
@@ -417,9 +419,9 @@ class IRParser: public FEXCore::IR::IREmitter {
}
if (Def.Definition == "%Invalid") {
LogMan::Msg::E("Error on Line: %d", i);
LogMan::Msg::E("%s", Lines[i].c_str());
LogMan::Msg::E("Definition tried to define reserved %Invalid ssa node");
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("Definition tried to define reserved %Invalid ssa node");
return false;
}
}
@@ -436,9 +438,9 @@ class IRParser: public FEXCore::IR::IREmitter {
}
else {
if (RemainingLine.empty()) {
LogMan::Msg::E("Error on Line: %d", i);
LogMan::Msg::E("%s", Lines[i].c_str());
LogMan::Msg::E("Line without an IROp?");
LogMan::Msg::EFmt("Error on Line: {}", i);
LogMan::Msg::EFmt("{}", Lines[i]);
LogMan::Msg::EFmt("Line without an IROp?");
return false;
}
@@ -455,12 +457,10 @@ class IRParser: public FEXCore::IR::IREmitter {
}
else {
while (!RemainingLine.empty()) {
size_t ArgEnd = std::string::npos;
ArgEnd = RemainingLine.find_first_of(",");
const size_t ArgEnd = RemainingLine.find(',');
std::string Arg = trim(RemainingLine.substr(0, ArgEnd));
std::string Arg = RemainingLine.substr(0, ArgEnd);
Arg = trim(Arg);
Def.Args.emplace_back(Arg);
Def.Args.emplace_back(std::move(Arg));
RemainingLine.erase(0, ArgEnd+1); // +1 to ensure we go past the ','
if (ArgEnd == std::string::npos)
@@ -469,17 +469,17 @@ class IRParser: public FEXCore::IR::IREmitter {
}
}
Defs.emplace_back(Def);
}
CurrentDef = &Defs.emplace_back(std::move(Def));
}
// Ensure all of the ops are real ops
for(size_t i = 0; i < Defs.size(); ++i) {
auto &Def = Defs[i];
auto Op = NameToOpMap.find(Def.IROp);
if (Op == NameToOpMap.end()) {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("IROp '%s' doesn't exist", Def.IROp.c_str());
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("IROp '{}' doesn't exist", Def.IROp);
return false;
}
Def.OpEnum = Op->second;
@@ -489,11 +489,11 @@ class IRParser: public FEXCore::IR::IREmitter {
IRPair<IROp_IRHeader> IRHeader;
{
auto &Def = Defs[0];
CurrentDef = &Def;
CurrentDef = &Def;
if (Def.OpEnum != FEXCore::IR::IROps::OP_IRHEADER) {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("First op needs to be IRHeader. Was '%s'", Def.IROp.c_str());
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("First op needs to be IRHeader. Was '{}'", Def.IROp);
return false;
}
@@ -507,14 +507,14 @@ class IRParser: public FEXCore::IR::IREmitter {
SetWriteCursor(nullptr); // isolate the header from everything following
// Initialize SSANameMapper with Invalid value
SSANameMapper["%Invalid"] = Invalid();
SSANameMapper.insert_or_assign("%Invalid", Invalid());
// Spin through the blocks and generate basic block ops
for(size_t i = 0; i < Defs.size(); ++i) {
auto &Def = Defs[i];
if (Def.OpEnum == FEXCore::IR::IROps::OP_CODEBLOCK) {
auto CodeBlock = _CodeBlock(InvalidNode, InvalidNode);
SSANameMapper[Def.Definition] = CodeBlock.Node;
SSANameMapper.insert_or_assign(Def.Definition, CodeBlock.Node);
Def.Node = CodeBlock.Node;
if (i == 1) {
@@ -532,23 +532,22 @@ class IRParser: public FEXCore::IR::IREmitter {
FEXCore::IR::IROp_CodeBlock *CurrentBlockOp{};
for(size_t i = 1; i < Defs.size(); ++i) {
auto &Def = Defs[i];
CurrentDef = &Def;
CurrentDef = &Def;
switch (Def.OpEnum) {
// Special handled
case FEXCore::IR::IROps::OP_IRHEADER:
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("IRHEADER used in the middle of the block!");
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("IRHEADER used in the middle of the block!");
return false; // only one OP_IRHEADER allowed per block
case FEXCore::IR::IROps::OP_CODEBLOCK: {
SetWriteCursor(nullptr); // isolate from previous block
if (CurrentBlock != nullptr) {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("CodeBlock being used inside of already existing codeblock!");
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("CodeBlock being used inside of already existing codeblock!");
return false;
}
@@ -560,15 +559,16 @@ class IRParser: public FEXCore::IR::IREmitter {
case FEXCore::IR::IROps::OP_BEGINBLOCK: {
if (CurrentBlock == nullptr) {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("EndBlock being used outside of a block!");
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("EndBlock being used outside of a block!");
return false;
}
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
if (!CheckPrintError(Def, Adjust.first)) return false;
if (!CheckPrintError(Def, Adjust.first)) {
return false;
}
Def.Node = _BeginBlock(Adjust.second);
CurrentBlockOp->Begin = Def.Node->Wrapped(DualListData.ListBegin());
@@ -577,15 +577,16 @@ class IRParser: public FEXCore::IR::IREmitter {
case FEXCore::IR::IROps::OP_ENDBLOCK: {
if (CurrentBlock == nullptr) {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("EndBlock being used outside of a block!");
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("EndBlock being used outside of a block!");
return false;
}
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
if (!CheckPrintError(Def, Adjust.first)) return false;
if (!CheckPrintError(Def, Adjust.first)) {
return false;
}
Def.Node = _EndBlock(Adjust.second);
CurrentBlockOp->Last = Def.Node->Wrapped(DualListData.ListBegin());
@@ -597,20 +598,18 @@ class IRParser: public FEXCore::IR::IREmitter {
}
case FEXCore::IR::IROps::OP_DUMMY: {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("Dummy op must not be used");
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("Dummy op must not be used");
break;
}
#define IROP_PARSER_SWITCH_HELPERS
#include <FEXCore/IR/IRDefines.inc>
default: {
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
LogMan::Msg::E("Unhandled Op enum '%s' in parser", Def.IROp.c_str());
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
LogMan::Msg::EFmt("Unhandled Op enum '{}' in parser", Def.IROp);
return false;
break;
}
}
@@ -624,7 +623,7 @@ class IRParser: public FEXCore::IR::IREmitter {
IROp->Size = Def.Size.Bytes();
IROp->ElementSize = 0;
}
SSANameMapper[Def.Definition] = Def.Node;
SSANameMapper.insert_or_assign(Def.Definition, Def.Node);
}
}
@@ -632,11 +631,11 @@ class IRParser: public FEXCore::IR::IREmitter {
}
void InitializeStaticTables() {
if (NameToOpMap.size() == 0) {
if (NameToOpMap.empty()) {
for (FEXCore::IR::IROps Op = FEXCore::IR::IROps::OP_DUMMY;
Op <= FEXCore::IR::IROps::OP_LAST;
Op = static_cast<FEXCore::IR::IROps>(static_cast<uint32_t>(Op) + 1)) {
NameToOpMap[FEXCore::IR::GetName(Op)] = Op;
NameToOpMap.insert_or_assign(FEXCore::IR::GetName(Op), Op);
}
}
}
@@ -644,13 +643,12 @@ class IRParser: public FEXCore::IR::IREmitter {
} // anon namespace
IREmitter* Parse(std::istream *in) {
auto parser = new IRParser(in);
std::unique_ptr<IREmitter> Parse(std::istream *in) {
auto parser = std::make_unique<IRParser>(in);
if (parser->Loaded) {
return parser;
} else {
delete parser;
return nullptr;
}
}
+367 -312
View File
@@ -19,15 +19,6 @@ $end_info$
namespace FEXCore::IR {
class ConstProp final : public FEXCore::IR::Pass {
std::unordered_map<uint64_t, OrderedNode*> ConstPool;
std::map<OrderedNode*, uint64_t> AddressgenConsts;
public:
bool Run(IREmitter *IREmit) override;
bool InlineConstants;
ConstProp(bool DoInlineConstants) : InlineConstants(DoInlineConstants) { }
};
template<typename T>
uint64_t getMask(T Op) {
uint64_t NumBits = Op->Header.Size * 8;
@@ -69,8 +60,7 @@ static bool IsImmMemory(uint64_t imm, uint8_t AccessSize) {
}
}
std::tuple<MemOffsetType, uint8_t, OrderedNode*, OrderedNode*> MemExtendedAddressing(IREmitter *IREmit, uint8_t AccessSize, IROp_Header* AddressHeader) {
static std::tuple<MemOffsetType, uint8_t, OrderedNode*, OrderedNode*> MemExtendedAddressing(IREmitter *IREmit, uint8_t AccessSize, IROp_Header* AddressHeader) {
auto Src0Header = IREmit->GetOpHeader(AddressHeader->Args[0]);
if (Src0Header->Size == 8) {
//Try to optimize: Base + MUL(Offset, Scale)
@@ -124,7 +114,7 @@ std::tuple<MemOffsetType, uint8_t, OrderedNode*, OrderedNode*> MemExtendedAddres
return { MEM_OFFSET_SXTX, 1, IREmit->UnwrapNode(AddressHeader->Args[0]), IREmit->UnwrapNode(AddressHeader->Args[1]) };
}
OrderedNodeWrapper RemoveUselessMasking(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t mask) {
static OrderedNodeWrapper RemoveUselessMasking(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t mask) {
#if 1 // HOTFIX: We need to clear up the meaning of opsize and dest size. See #594
return src;
#else
@@ -151,7 +141,7 @@ OrderedNodeWrapper RemoveUselessMasking(IREmitter *IREmit, OrderedNodeWrapper sr
#endif
}
bool IsBfeAlreadyDone(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t Width) {
static bool IsBfeAlreadyDone(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t Width) {
auto IROp = IREmit->GetOpHeader(src);
if (IROp->Op == OP_BFE) {
auto Op = IROp->C<IR::IROp_Bfe>();
@@ -162,31 +152,55 @@ bool IsBfeAlreadyDone(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t Width)
return false;
}
bool ConstProp::Run(IREmitter *IREmit) {
class ConstProp final : public FEXCore::IR::Pass {
public:
explicit ConstProp(bool DoInlineConstants) : InlineConstants(DoInlineConstants) { }
bool Run(IREmitter *IREmit) override;
bool InlineConstants;
private:
bool HandleConstantPools(IREmitter *IREmit, const IRListView& CurrentIR);
void CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& CurrentIR);
void FCMPOptimization(IREmitter *IREmit, const IRListView& CurrentIR);
void LoadMemStoreMemImmediatePooling(IREmitter *IREmit, const IRListView& CurrentIR);
bool ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& CurrentIR,
OrderedNode* CodeNode, IROp_Header* IROp);
bool ConstantPropagation(IREmitter *IREmit, const IRListView& CurrentIR,
OrderedNode* CodeNode, IROp_Header* IROp);
bool ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR);
std::unordered_map<uint64_t, OrderedNode*> ConstPool;
std::map<OrderedNode*, uint64_t> AddressgenConsts;
};
bool ConstProp::HandleConstantPools(IREmitter *IREmit, const IRListView& CurrentIR) {
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
auto OriginalWriteCursor = IREmit->GetWriteCursor();
{
// constants are pooled per block
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_CONSTANT) {
auto Op = IROp->C<IR::IROp_Constant>();
if (ConstPool.count(Op->Constant)) {
IREmit->ReplaceAllUsesWith(CodeNode, ConstPool[Op->Constant]);
Changed = true;
} else {
ConstPool[Op->Constant] = CodeNode;
}
// constants are pooled per block
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_CONSTANT) {
auto Op = IROp->C<IR::IROp_Constant>();
if (ConstPool.count(Op->Constant)) {
IREmit->ReplaceAllUsesWith(CodeNode, ConstPool[Op->Constant]);
Changed = true;
} else {
ConstPool[Op->Constant] = CodeNode;
}
}
ConstPool.clear();
}
ConstPool.clear();
}
return Changed;
}
// Code motion around selects
// Moves unary ops that depend on a select before the select, if both inputs are constants
// assumes that unary ops without side effects on constants will be constprop'd
void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& CurrentIR) {
// Code motion around selects
// Moves unary ops that depend on a select before the select, if both inputs are constants
// assumes that unary ops without side effects on constants will be constprop'd
@@ -243,9 +257,9 @@ bool ConstProp::Run(IREmitter *IREmit) {
}
}
}
}
// FCMP optimization
void ConstProp::FCMPOptimization(IREmitter *IREmit, const IRListView& CurrentIR) {
// Make all FCMPs set no flags
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
if (IROp->Op == OP_FCMP) {
@@ -266,10 +280,11 @@ bool ConstProp::Run(IREmitter *IREmit) {
}
}
}
}
// LoadMem / StoreMem imm pooling
// If imms are close by, use address gen to generate the values instead of using a new imm
// LoadMem / StoreMem imm pooling
// If imms are close by, use address gen to generate the values instead of using a new imm
void ConstProp::LoadMemStoreMemImmediatePooling(IREmitter *IREmit, const IRListView& CurrentIR) {
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
if (IROp->Op == OP_LOADMEM || IROp->Op == OP_STOREMEM) {
@@ -293,152 +308,163 @@ bool ConstProp::Run(IREmitter *IREmit) {
}
AddressgenConsts.clear();
}
}
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
// zext / masking elimination
switch (IROp->Op) {
// Generic handling
case OP_OR:
case OP_XOR:
case OP_NOT:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_UMUL:
case OP_DIV:
case OP_UDIV:
case OP_LSHR:
case OP_ASHR:
case OP_LSHL:
case OP_ROR: {
for (int i = 0; i < IROp->NumArgs; i++) {
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp));
if (newArg.ID() != IROp->Args[i].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
Changed = true;
}
}
break;
}
bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& CurrentIR,
OrderedNode* CodeNode, IROp_Header* IROp) {
bool Changed = false;
case OP_AND: {
// if AND's arguments are imms, they are masking
for (int i = 0; i < IROp->NumArgs; i++) {
auto mask = getMask(IROp);
uint64_t imm = 0;
if (IREmit->IsValueConstant(IROp->Args[i^1], &imm))
mask = imm;
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], imm);
if (newArg.ID() != IROp->Args[i].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
Changed = true;
}
}
break;
}
case OP_BFE: {
auto Op = IROp->C<IR::IROp_Bfe>();
// Is this value already BFE'd?
if (IsBfeAlreadyDone(IREmit, IROp->Args[0], Op->Width)) {
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
//printf("Removed BFE once \n");
break;
}
// Is this value already ZEXT'd?
if (Op->lsb == 0) {
//LoadMem, LoadMemTSO & LoadContext ZExt
auto source = IROp->Args[0];
auto sourceHeader = IREmit->GetOpHeader(source);
if (Op->Width >= (sourceHeader->Size*8) &&
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)
) {
//printf("Eliminated needless zext bfe\n");
// Load mem / load ctx zexts, no need to vmem
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
break;
}
}
// BFE does implicit masking, remove any masks leading to this, if possible
uint64_t imm = 1ULL << (Op->Width-1);
imm = (imm-1) *2 + 1;
imm <<= Op->lsb;
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
if (newArg.ID() != IROp->Args[0].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
switch (IROp->Op) {
// Generic handling
case OP_OR:
case OP_XOR:
case OP_NOT:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_UMUL:
case OP_DIV:
case OP_UDIV:
case OP_LSHR:
case OP_ASHR:
case OP_LSHL:
case OP_ROR: {
for (int i = 0; i < IROp->NumArgs; i++) {
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp));
if (newArg.ID() != IROp->Args[i].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
Changed = true;
}
break;
}
break;
}
case OP_SBFE: {
auto Op = IROp->C<IR::IROp_Sbfe>();
case OP_AND: {
// if AND's arguments are imms, they are masking
for (int i = 0; i < IROp->NumArgs; i++) {
auto mask = getMask(IROp);
uint64_t imm = 0;
if (IREmit->IsValueConstant(IROp->Args[i^1], &imm))
mask = imm;
// BFE does implicit masking
uint64_t imm = 1ULL << (Op->Width-1);
imm = (imm-1) *2 + 1;
imm <<= Op->lsb;
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], imm);
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
if (newArg.ID() != IROp->Args[0].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
if (newArg.ID() != IROp->Args[i].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
Changed = true;
}
}
break;
}
case OP_BFE: {
auto Op = IROp->C<IR::IROp_Bfe>();
// Is this value already BFE'd?
if (IsBfeAlreadyDone(IREmit, IROp->Args[0], Op->Width)) {
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
//printf("Removed BFE once \n");
break;
}
case OP_VFADD:
case OP_VFSUB:
case OP_VFMUL:
case OP_VFDIV:
case OP_FCMP: {
auto flopSize = IROp->Size;
for (int i = 0; i < IROp->NumArgs; i++) {
auto argHeader = IREmit->GetOpHeader(IROp->Args[i]);
if (argHeader->Op == OP_VMOV) {
auto source = argHeader->Args[0];
auto sourceHeader = IREmit->GetOpHeader(source);
if (sourceHeader->Size >= flopSize) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(source));
//printf("VMOV bypassed\n");
}
}
}
break;
}
case OP_VMOV: {
// elim from load mem
// Is this value already ZEXT'd?
if (Op->lsb == 0) {
//LoadMem, LoadMemTSO & LoadContext ZExt
auto source = IROp->Args[0];
auto sourceHeader = IREmit->GetOpHeader(source);
if (IROp->Size >= sourceHeader->Size &&
if (Op->Width >= (sourceHeader->Size*8) &&
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)
) {
//printf("Eliminated needless zext VMOV\n");
) {
//printf("Eliminated needless zext bfe\n");
// Load mem / load ctx zexts, no need to vmem
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
} else if (IROp->Size == sourceHeader->Size) {
// VMOV of same size
//printf("printf vmov of same size?!\n");
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
break;
}
break;
}
default: break;
// BFE does implicit masking, remove any masks leading to this, if possible
uint64_t imm = 1ULL << (Op->Width-1);
imm = (imm-1) *2 + 1;
imm <<= Op->lsb;
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
if (newArg.ID() != IROp->Args[0].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
Changed = true;
}
break;
}
// constprop + some more per instruction logic
case OP_SBFE: {
auto Op = IROp->C<IR::IROp_Sbfe>();
// BFE does implicit masking
uint64_t imm = 1ULL << (Op->Width-1);
imm = (imm-1) *2 + 1;
imm <<= Op->lsb;
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
if (newArg.ID() != IROp->Args[0].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
Changed = true;
}
break;
}
case OP_VFADD:
case OP_VFSUB:
case OP_VFMUL:
case OP_VFDIV:
case OP_FCMP: {
auto flopSize = IROp->Size;
for (int i = 0; i < IROp->NumArgs; i++) {
auto argHeader = IREmit->GetOpHeader(IROp->Args[i]);
if (argHeader->Op == OP_VMOV) {
auto source = argHeader->Args[0];
auto sourceHeader = IREmit->GetOpHeader(source);
if (sourceHeader->Size >= flopSize) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(source));
//printf("VMOV bypassed\n");
}
}
}
break;
}
case OP_VMOV: {
// elim from load mem
auto source = IROp->Args[0];
auto sourceHeader = IREmit->GetOpHeader(source);
if (IROp->Size >= sourceHeader->Size &&
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)
) {
//printf("Eliminated needless zext VMOV\n");
// Load mem / load ctx zexts, no need to vmem
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
} else if (IROp->Size == sourceHeader->Size) {
// VMOV of same size
//printf("printf vmov of same size?!\n");
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
}
break;
}
default:
break;
}
return Changed;
}
// constprop + some more per instruction logic
bool ConstProp::ConstantPropagation(IREmitter *IREmit, const IRListView& CurrentIR,
OrderedNode* CodeNode, IROp_Header* IROp) {
bool Changed = false;
switch (IROp->Op) {
/*
case OP_UMUL:
@@ -490,7 +516,6 @@ bool ConstProp::Run(IREmitter *IREmit) {
auto AddressHeader = IREmit->GetOpHeader(Op->Header.Args[0]);
if (AddressHeader->Op == OP_ADD && AddressHeader->Size == 8) {
auto [OffsetType, OffsetScale, Arg0, Arg1] = MemExtendedAddressing(IREmit, Op->Size, AddressHeader);
Op->OffsetType = OffsetType;
@@ -530,7 +555,6 @@ bool ConstProp::Run(IREmitter *IREmit) {
uint64_t NewConstant = (Constant1 + Constant2) & getMask(Op) ;
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
continue;
}
break;
}
@@ -711,9 +735,9 @@ bool ConstProp::Run(IREmitter *IREmit) {
uint64_t NewConstant = (Constant1 * Constant2) & getMask(Op);
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
Changed = true;
} else if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2) && __builtin_popcountl(Constant2) == 1) {
} else if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2) && std::popcount(Constant2) == 1) {
if (IROp->Size == 4 || IROp->Size == 8) {
uint64_t amt = __builtin_ctzl(Constant2);
uint64_t amt = std::countr_zero(Constant2);
IREmit->SetWriteCursor(CodeNode);
auto shift = IREmit->_Lshl(CurrentIR.GetNode(Op->Header.Args[0]), IREmit->_Constant(amt));
shift.first->Header.Size = IROp->Size; // force Lshl to be the same size as the original Mul
@@ -753,187 +777,218 @@ bool ConstProp::Run(IREmitter *IREmit) {
default:
break;
}
}
// constant inlining
if (InlineConstants) {
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
switch(IROp->Op) {
case OP_LSHR:
case OP_ASHR:
case OP_ROR:
case OP_LSHL:
{
auto Op = IROp->C<IR::IROp_Lshr>();
return Changed;
}
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR) {
bool Changed = false;
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
switch(IROp->Op) {
case OP_LSHR:
case OP_ASHR:
case OP_ROR:
case OP_LSHL:
{
auto Op = IROp->C<IR::IROp_Lshr>();
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
// this shouldn't be here, but rather on the emitter themselves or the constprop transformation?
if (IROp->Size <=4)
Constant2 &= 31;
else
Constant2 &= 63;
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
break;
}
case OP_ADD:
case OP_SUB:
{
auto Op = IROp->C<IR::IROp_Add>();
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
// this shouldn't be here, but rather on the emitter themselves or the constprop transformation?
if (IROp->Size <=4)
Constant2 &= 31;
else
Constant2 &= 63;
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
break;
}
break;
}
case OP_SELECT:
{
auto Op = IROp->C<IR::IROp_Select>();
uint64_t Constant1{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant1)) {
if (IsImmAddSub(Constant1)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
Changed = true;
}
}
case OP_ADD:
case OP_SUB:
uint64_t Constant2{};
uint64_t Constant3{};
if (IREmit->IsValueConstant(Op->Header.Args[2], &Constant2) &&
IREmit->IsValueConstant(Op->Header.Args[3], &Constant3) &&
Constant2 == 1 &&
Constant3 == 0)
{
auto Op = IROp->C<IR::IROp_Add>();
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
}
case OP_SELECT:
{
auto Op = IROp->C<IR::IROp_Select>();
break;
}
uint64_t Constant1{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant1)) {
if (IsImmAddSub(Constant1)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
case OP_CONDJUMP:
{
auto Op = IROp->C<IR::IROp_CondJump>();
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
Changed = true;
}
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
}
uint64_t Constant2{};
uint64_t Constant3{};
if (IREmit->IsValueConstant(Op->Header.Args[2], &Constant2) &&
IREmit->IsValueConstant(Op->Header.Args[3], &Constant3) &&
Constant2 == 1 &&
Constant3 == 0)
{
case OP_EXITFUNCTION:
{
auto Op = IROp->C<IR::IROp_ExitFunction>();
uint64_t Constant{};
if (IREmit->IsValueConstant(Op->NewRIP, &Constant)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant));
Changed = true;
} else {
auto NewRIP = IREmit->GetOpHeader(Op->NewRIP);
if (NewRIP->Op == OP_ENTRYPOINTOFFSET) {
auto EO = NewRIP->C<IR::IROp_EntrypointOffset>();
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(EO->Offset, EO->Header.Size));
Changed = true;
}
}
break;
}
case OP_OR:
case OP_XOR:
case OP_AND:
{
auto Op = IROp->CW<IR::IROp_Or>();
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmLogical(Constant2, IROp->Size * 8)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
}
case OP_LOADMEM:
{
auto Op = IROp->CW<IR::IROp_LoadMem>();
uint64_t Constant2{};
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmMemory(Constant2, Op->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
}
case OP_STOREMEM:
{
auto Op = IROp->CW<IR::IROp_StoreMem>();
uint64_t Constant2{};
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[2], &Constant2)) {
if (IsImmMemory(Constant2, Op->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
}
break;
}
case OP_CONDJUMP:
{
auto Op = IROp->C<IR::IROp_CondJump>();
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
}
case OP_EXITFUNCTION:
{
auto Op = IROp->C<IR::IROp_ExitFunction>();
uint64_t Constant{};
if (IREmit->IsValueConstant(Op->NewRIP, &Constant)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant));
Changed = true;
} else {
auto NewRIP = IREmit->GetOpHeader(Op->NewRIP);
if (NewRIP->Op == OP_ENTRYPOINTOFFSET) {
auto EO = NewRIP->C<IR::IROp_EntrypointOffset>();
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(EO->Offset, EO->Header.Size));
Changed = true;
}
}
break;
}
case OP_OR:
case OP_XOR:
case OP_AND:
{
auto Op = IROp->CW<IR::IROp_Or>();
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmLogical(Constant2, IROp->Size * 8)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
}
case OP_LOADMEM:
{
auto Op = IROp->CW<IR::IROp_LoadMem>();
uint64_t Constant2{};
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmMemory(Constant2, Op->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
}
case OP_STOREMEM:
{
auto Op = IROp->CW<IR::IROp_StoreMem>();
uint64_t Constant2{};
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[2], &Constant2)) {
if (IsImmMemory(Constant2, Op->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
Changed = true;
}
}
break;
}
default: break;
break;
}
default:
break;
}
}
IREmit->SetWriteCursor(OriginalWriteCursor);
return Changed;
}
bool ConstProp::Run(IREmitter *IREmit) {
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
auto OriginalWriteCursor = IREmit->GetWriteCursor();
if (HandleConstantPools(IREmit, CurrentIR)) {
Changed = true;
}
CodeMotionAroundSelects(IREmit, CurrentIR);
FCMPOptimization(IREmit, CurrentIR);
LoadMemStoreMemImmediatePooling(IREmit, CurrentIR);
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
if (ZextAndMaskingElimination(IREmit, CurrentIR, CodeNode, IROp)) {
Changed = true;
}
if (ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp)) {
Changed = true;
}
}
if (InlineConstants && ConstantInlining(IREmit, CurrentIR)) {
Changed = true;
}
IREmit->SetWriteCursor(OriginalWriteCursor);
return Changed;
}
+45 -4
View File
@@ -3,6 +3,7 @@
#include <sys/mman.h>
#include <jemalloc/jemalloc.h>
#include <memory>
#include <malloc.h>
extern "C" {
extern void *__libc_malloc(size_t size);
@@ -16,6 +17,15 @@ extern "C" {
extern mmap_hook_type __mmap_hook;
extern munmap_hook_type __munmap_hook;
static FEXCore::Allocator::MALLOC_Hook global_malloc {::__libc_malloc};
static FEXCore::Allocator::REALLOC_Hook global_realloc {::__libc_realloc};
static FEXCore::Allocator::FREE_Hook global_free {::__libc_free};
// Override the global functions
FEX_DEFAULT_VISIBILITY void *malloc(size_t size) { return global_malloc(size); }
FEX_DEFAULT_VISIBILITY void *realloc(void *ptr, size_t size) { return global_realloc(ptr, size); }
FEX_DEFAULT_VISIBILITY void free(void *ptr) { return global_free(ptr); }
}
namespace FEXCore::Allocator {
@@ -25,6 +35,10 @@ namespace FEXCore::Allocator {
REALLOC_Hook realloc {::__libc_realloc};
FREE_Hook free {::__libc_free};
using GLIBC_MALLOC_Hook = void*(*)(size_t, const void *caller);
using GLIBC_REALLOC_Hook = void*(*)(void*, size_t, const void *caller);
using GLIBC_FREE_Hook = void(*)(void*, const void *caller);
std::unique_ptr<Alloc::HostAllocator> Alloc64{};
void *FEX_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
@@ -57,6 +71,8 @@ namespace FEXCore::Allocator {
return ::je_free(ptr);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
void SetupHooks() {
Alloc64.reset(Alloc::OSAllocator::Create64BitAllocator());
__mmap_hook = FEX_mmap;
@@ -66,12 +82,37 @@ namespace FEXCore::Allocator {
FEXCore::Allocator::malloc = ::je_malloc;
FEXCore::Allocator::realloc = ::je_realloc;
FEXCore::Allocator::free = ::je_free;
global_malloc = ::je_malloc;
global_realloc = ::je_realloc;
global_free = ::je_free;
__malloc_hook = FEXCore::Allocator::FEX_malloc_hook;
__realloc_hook = FEXCore::Allocator::FEX_realloc_hook;
__free_hook = FEXCore::Allocator::FEX_free_hook;
}
void ClearHooks() {
__mmap_hook = ::mmap;
__munmap_hook = ::munmap;
FEXCore::Allocator::mmap = ::mmap;
FEXCore::Allocator::munmap = ::munmap;
FEXCore::Allocator::malloc = ::__libc_malloc;
FEXCore::Allocator::realloc = ::__libc_realloc;
FEXCore::Allocator::free = ::__libc_free;
global_malloc = ::__libc_malloc;
global_realloc = ::__libc_realloc;
global_free = ::__libc_free;
// Reset's glibc hooks
__malloc_hook = 0;
__realloc_hook = 0;
__free_hook = 0;
}
#pragma GCC diagnostic pop
}
extern "C" {
// Override the global functions
void *malloc(size_t size) { return FEXCore::Allocator::malloc(size); }
void *realloc(void *ptr, size_t size) { return FEXCore::Allocator::realloc(ptr, size); }
void free(void *ptr) { return FEXCore::Allocator::free(ptr); }
}
+19 -1
View File
@@ -37,7 +37,17 @@ void UnInstallHandlers() { Handlers.clear(); }
Handler(Buffer);
}
__builtin_trap();
FEX_TRAP_EXECUTION;
}
void MFmt(const char *fmt, const fmt::format_args& args) {
auto msg = fmt::vformat(fmt, args);
for (auto& Handler : Handlers) {
Handler(msg.c_str());
}
FEX_TRAP_EXECUTION;
}
} // namespace Throw
@@ -67,5 +77,13 @@ void M(DebugLevels Level, const char *fmt, va_list args) {
}
}
void MFmtImpl(DebugLevels level, const char* fmt, const fmt::format_args& args) {
const auto msg = fmt::vformat(fmt, args);
for (auto& Handler : Handlers) {
Handler(level, msg.c_str());
}
}
} // namespace Msg
} // namespace LogMan
+31 -29
View File
@@ -1,5 +1,7 @@
#pragma once
#include <FEXCore/Core/Context.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
#include <list>
@@ -54,15 +56,15 @@ namespace Type {
#undef P
}
__attribute__((visibility("default"))) std::string GetDataDirectory();
__attribute__((visibility("default"))) std::string GetConfigDirectory(bool Global);
__attribute__((visibility("default"))) std::string GetConfigFileLocation();
__attribute__((visibility("default"))) std::string GetApplicationConfig(std::string &Filename, bool Global);
FEX_DEFAULT_VISIBILITY std::string GetDataDirectory();
FEX_DEFAULT_VISIBILITY std::string GetConfigDirectory(bool Global);
FEX_DEFAULT_VISIBILITY std::string GetConfigFileLocation();
FEX_DEFAULT_VISIBILITY std::string GetApplicationConfig(const std::string &Filename, bool Global);
using LayerValue = std::list<std::string>;
using LayerOptions = std::unordered_map<ConfigOption, LayerValue>;
class __attribute__((visibility("default"))) Layer {
class FEX_DEFAULT_VISIBILITY Layer {
public:
explicit Layer(const LayerType _Type);
virtual ~Layer();
@@ -94,57 +96,57 @@ namespace Type {
}
void Set(ConfigOption Option, std::string Data) {
OptionMap[Option].emplace_back(Data);
OptionMap[Option].emplace_back(std::move(Data));
}
void EraseSet(ConfigOption Option, std::string Data) {
OptionMap.erase(Option);
OptionMap[Option].emplace_back(Data);
Erase(Option);
Set(Option, std::move(Data));
}
void Erase(ConfigOption Option) {
OptionMap.erase(Option);
}
const LayerType GetLayerType() const { return Type; }
const LayerOptions &GetOptionMap() { return OptionMap; }
LayerType GetLayerType() const { return Type; }
const LayerOptions &GetOptionMap() const { return OptionMap; }
protected:
const LayerType Type;
LayerOptions OptionMap;
};
__attribute__((visibility("default"))) void Initialize();
__attribute__((visibility("default"))) void Shutdown();
FEX_DEFAULT_VISIBILITY void Initialize();
FEX_DEFAULT_VISIBILITY void Shutdown();
__attribute__((visibility("default"))) void Load();
__attribute__((visibility("default"))) void ReloadMetaLayer();
FEX_DEFAULT_VISIBILITY void Load();
FEX_DEFAULT_VISIBILITY void ReloadMetaLayer();
__attribute__((visibility("default"))) void AddLayer(std::unique_ptr<FEXCore::Config::Layer> _Layer);
FEX_DEFAULT_VISIBILITY void AddLayer(std::unique_ptr<FEXCore::Config::Layer> _Layer);
__attribute__((visibility("default"))) bool Exists(ConfigOption Option);
__attribute__((visibility("default"))) std::optional<LayerValue*> All(ConfigOption Option);
__attribute__((visibility("default"))) std::optional<std::string*> Get(ConfigOption Option);
FEX_DEFAULT_VISIBILITY bool Exists(ConfigOption Option);
FEX_DEFAULT_VISIBILITY std::optional<LayerValue*> All(ConfigOption Option);
FEX_DEFAULT_VISIBILITY std::optional<std::string*> Get(ConfigOption Option);
__attribute__((visibility("default"))) void Set(ConfigOption Option, std::string Data);
__attribute__((visibility("default"))) void Erase(ConfigOption Option);
__attribute__((visibility("default"))) void EraseSet(ConfigOption Option, std::string Data);
FEX_DEFAULT_VISIBILITY void Set(ConfigOption Option, std::string Data);
FEX_DEFAULT_VISIBILITY void Erase(ConfigOption Option);
FEX_DEFAULT_VISIBILITY void EraseSet(ConfigOption Option, std::string Data);
template<typename T>
class __attribute__((visibility("default"))) Value {
class FEX_DEFAULT_VISIBILITY Value {
public:
template <typename TT = T,
typename std::enable_if<!std::is_same<TT, std::string>::value, int>::type = 0>
Value(FEXCore::Config::ConfigOption _Option, T Default)
: Option {_Option} {
ValueData = FEXCore::Config::Value<T>::GetIfExists(Option, Default);
ValueData = GetIfExists(Option, Default);
}
template <typename TT = T,
typename std::enable_if<std::is_same<TT, std::string>::value, int>::type = 0>
Value(FEXCore::Config::ConfigOption _Option, T Default)
: Option {_Option} {
ValueData = FEXCore::Config::Value<T>::GetIfExists(Option, Default);
ValueData = GetIfExists(Option, Default);
GetListIfExists(Option, &AppendList);
}
@@ -156,7 +158,7 @@ namespace Type {
ERROR_AND_DIE("FEXCore::Config::Value has no value");
}
ValueData = FEXCore::Config::Value<T>::Get(Option);
ValueData = Get(Option);
}
template <typename TT = T,
@@ -167,13 +169,13 @@ namespace Type {
ERROR_AND_DIE("FEXCore::Config::Value has no value");
}
ValueData = FEXCore::Config::Value<T>::GetIfExists(Option);
ValueData = GetIfExists(Option);
GetListIfExists(Option, &AppendList);
}
operator T() { return ValueData; }
T operator()() { return ValueData; }
Value<T>(T Value) { ValueData = Value; }
operator T() const { return ValueData; }
T operator()() const { return ValueData; }
Value<T>(T Value) { ValueData = std::move(Value); }
std::list<T> &All() { return AppendList; }
private:
+6 -3
View File
@@ -6,7 +6,10 @@ $end_info$
*/
#pragma once
#include <stdint.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <cstdint>
#include <string>
namespace FEXCore {
@@ -85,8 +88,8 @@ class LLVMCore;
virtual void ClearCache() {}
virtual void CopyNecessaryDataForCompileThread(CPUBackend *Original) {}
using AsmDispatch = __attribute__((naked)) void(*)(FEXCore::Core::CpuStateFrame *Frame);
using JITCallback = __attribute__((naked)) void(*)(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP);
using AsmDispatch = FEX_NAKED void(*)(FEXCore::Core::CpuStateFrame *Frame);
using JITCallback = FEX_NAKED void(*)(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP);
JITCallback CallbackPtr{};
protected:
+5 -1
View File
@@ -16,6 +16,10 @@ class IREmitter;
*/
class CodeLoader {
public:
using MapperFn = std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)>;
using UnmapperFn = std::function<int(void *addr, size_t length)>;
virtual ~CodeLoader() = default;
/**
* @brief CPU Core uses this to choose what the stack size should be for this code
@@ -35,7 +39,7 @@ public:
/**
* @brief Maps and copies the executable, also sets up stack
*/
virtual bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) { return false; }
virtual bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) { return false; }
virtual std::vector<std::string> const *GetApplicationArguments() { return nullptr; }
virtual void GetExecveArguments(std::vector<char const*> *Args) {}
+44 -41
View File
@@ -5,6 +5,7 @@
#include <FEXCore/Core/SignalDelegator.h>
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <istream>
#include <ostream>
@@ -45,12 +46,13 @@ namespace FEXCore::Context {
MODE_32BIT,
MODE_64BIT,
};
using CustomCPUFactoryType = std::function<FEXCore::CPU::CPUBackend* (FEXCore::Context::Context*, FEXCore::Core::InternalThreadState *Thread)>;
using CustomCPUFactoryType = std::function<std::unique_ptr<FEXCore::CPU::CPUBackend> (FEXCore::Context::Context*, FEXCore::Core::InternalThreadState *Thread)>;
/**
* @brief This initializes internal FEXCore state that is shared between contexts and requires overhead to setup
*/
__attribute__((visibility("default"))) void InitializeStaticTables(OperatingMode Mode = MODE_64BIT);
FEX_DEFAULT_VISIBILITY void InitializeStaticTables(OperatingMode Mode = MODE_64BIT);
FEX_DEFAULT_VISIBILITY void ShutdownStaticTables();
/**
* @brief [[threadsafe]] Create a new FEXCore context object
@@ -59,7 +61,7 @@ namespace FEXCore::Context {
*
* @return a new context object
*/
__attribute__((visibility("default"))) FEXCore::Context::Context *CreateNewContext();
FEX_DEFAULT_VISIBILITY FEXCore::Context::Context *CreateNewContext();
/**
* @brief Post creation context initialization
@@ -69,14 +71,14 @@ namespace FEXCore::Context {
*
* @return true if we managed to initialize correctly
*/
__attribute__((visibility("default"))) bool InitializeContext(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY bool InitializeContext(FEXCore::Context::Context *CTX);
/**
* @brief Destroy the context object
*
* @param CTX
*/
__attribute__((visibility("default"))) void DestroyContext(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void DestroyContext(FEXCore::Context::Context *CTX);
/**
* @brief Allows setting up in memory code and other things prior to launchign code execution
@@ -86,17 +88,17 @@ namespace FEXCore::Context {
*
* @return true if we loaded code
*/
__attribute__((visibility("default"))) bool InitCore(FEXCore::Context::Context *CTX, FEXCore::CodeLoader *Loader);
FEX_DEFAULT_VISIBILITY bool InitCore(FEXCore::Context::Context *CTX, FEXCore::CodeLoader *Loader);
__attribute__((visibility("default"))) void SetExitHandler(FEXCore::Context::Context *CTX, std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> handler);
__attribute__((visibility("default"))) std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> GetExitHandler(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void SetExitHandler(FEXCore::Context::Context *CTX, std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> handler);
FEX_DEFAULT_VISIBILITY std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> GetExitHandler(FEXCore::Context::Context *CTX);
/**
* @brief Pauses execution on the CPU core
*
* Blocks until all threads have paused.
*/
__attribute__((visibility("default"))) void Pause(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void Pause(FEXCore::Context::Context *CTX);
/**
* @brief Starts (or continues) the CPU core
@@ -105,7 +107,7 @@ namespace FEXCore::Context {
* Use RunUntilExit() for synchonous executions
*
*/
__attribute__((visibility("default"))) void Run(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void Run(FEXCore::Context::Context *CTX);
/**
* @brief Runs the CPU core until it exits
@@ -117,9 +119,9 @@ namespace FEXCore::Context {
*
* @return The ExitReason for the parentthread.
*/
__attribute__((visibility("default"))) ExitReason RunUntilExit(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY ExitReason RunUntilExit(FEXCore::Context::Context *CTX);
__attribute__((visibility("default"))) void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP);
FEX_DEFAULT_VISIBILITY void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
/**
* @brief Gets the program exit status
@@ -129,21 +131,21 @@ namespace FEXCore::Context {
*
* @return The program exit status
*/
__attribute__((visibility("default"))) int GetProgramStatus(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY int GetProgramStatus(FEXCore::Context::Context *CTX);
/**
* @brief Tells the core to shutdown
*
* Blocks until shutdown
*/
__attribute__((visibility("default"))) void Stop(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void Stop(FEXCore::Context::Context *CTX);
/**
* @brief Executes one instruction
*
* Returns once execution is complete.
*/
__attribute__((visibility("default"))) void Step(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void Step(FEXCore::Context::Context *CTX);
/**
* @brief [[threadsafe]] Returns the ExitReason of the parent thread. Typically used for async result status
@@ -152,7 +154,7 @@ namespace FEXCore::Context {
*
* @return The ExitReason for the parentthread
*/
__attribute__((visibility("default"))) ExitReason GetExitReason(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY ExitReason GetExitReason(FEXCore::Context::Context *CTX);
/**
* @brief [[theadsafe]] Checks if the Context is either done working or paused(in the case of single stepping)
@@ -163,7 +165,7 @@ namespace FEXCore::Context {
*
* @return true if the core is done or paused
*/
__attribute__((visibility("default"))) bool IsDone(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY bool IsDone(FEXCore::Context::Context *CTX);
/**
* @brief Gets a copy the CPUState of the parent thread
@@ -171,7 +173,7 @@ namespace FEXCore::Context {
* @param CTX The context that we created
* @param State The state object to populate
*/
__attribute__((visibility("default"))) void GetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
FEX_DEFAULT_VISIBILITY void GetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
/**
* @brief Copies the CPUState provided to the parent thread
@@ -179,7 +181,7 @@ namespace FEXCore::Context {
* @param CTX The context that we created
* @param State The satate object to copy from
*/
__attribute__((visibility("default"))) void SetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
FEX_DEFAULT_VISIBILITY void SetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
/**
* @brief Allows the frontend to pass in a custom CPUBackend creation factory
@@ -189,7 +191,7 @@ namespace FEXCore::Context {
* @param CTX The context that we created
* @param Factory The factory that the context will call if the DefaultCore config ise set to CUSTOM
*/
__attribute__((visibility("default"))) void SetCustomCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory);
FEX_DEFAULT_VISIBILITY void SetCustomCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory);
/**
* @brief Sets up memory regions on the guest for mirroring within the guest's VM space
@@ -200,7 +202,7 @@ namespace FEXCore::Context {
*
* @return true when successfully mapped. false if there was an error adding
*/
__attribute__((visibility("default"))) bool AddVirtualMemoryMapping(FEXCore::Context::Context *CTX, uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size);
FEX_DEFAULT_VISIBILITY bool AddVirtualMemoryMapping(FEXCore::Context::Context *CTX, uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size);
/**
* @brief Allows the frontend to set a custom syscall handler
@@ -210,29 +212,30 @@ namespace FEXCore::Context {
* @param Syscall Which syscall ID to install a visitor to
* @param Visitor The Visitor to install
*/
__attribute__((visibility("default"))) void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, uint64_t Syscall, FEXCore::HLE::SyscallVisitor *Visitor);
FEX_DEFAULT_VISIBILITY void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, uint64_t Syscall, FEXCore::HLE::SyscallVisitor *Visitor);
__attribute__((visibility("default"))) void HandleCallback(FEXCore::Context::Context *CTX, uint64_t RIP);
FEX_DEFAULT_VISIBILITY void HandleCallback(FEXCore::Context::Context *CTX, uint64_t RIP);
__attribute__((visibility("default"))) void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
__attribute__((visibility("default"))) void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
FEX_DEFAULT_VISIBILITY void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
FEX_DEFAULT_VISIBILITY void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
__attribute__((visibility("default"))) FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
__attribute__((visibility("default"))) void InitializeThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
__attribute__((visibility("default"))) void RunThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
__attribute__((visibility("default"))) void StopThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
__attribute__((visibility("default"))) void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
__attribute__((visibility("default"))) void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
__attribute__((visibility("default"))) void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation);
__attribute__((visibility("default"))) void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler);
__attribute__((visibility("default"))) FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf);
FEX_DEFAULT_VISIBILITY FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
FEX_DEFAULT_VISIBILITY void InitializeThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void RunThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void StopThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation);
FEX_DEFAULT_VISIBILITY void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler);
FEX_DEFAULT_VISIBILITY FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf);
__attribute__((visibility("default"))) void AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name);
__attribute__((visibility("default"))) void RemoveNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length);
__attribute__((visibility("default"))) void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader);
__attribute__((visibility("default"))) bool WriteAOTIR(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
__attribute__((visibility("default"))) void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer);
__attribute__((visibility("default"))) void FlushCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length);
FEX_DEFAULT_VISIBILITY void AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name);
FEX_DEFAULT_VISIBILITY void RemoveNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length);
FEX_DEFAULT_VISIBILITY void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader);
FEX_DEFAULT_VISIBILITY void SetAOTIRWriter(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
FEX_DEFAULT_VISIBILITY void FinalizeAOTIRCache(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer);
FEX_DEFAULT_VISIBILITY void FlushCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length);
__attribute__((visibility("default"))) void ConfigureAOTGen(FEXCore::Context::Context *CTX, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
FEX_DEFAULT_VISIBILITY void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
}
+6 -3
View File
@@ -1,12 +1,15 @@
#pragma once
#include <FEXCore/HLE/Linux/ThreadManagement.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <atomic>
#include <cstddef>
#include <stdint.h>
#include <string_view>
namespace FEXCore::Core {
struct __attribute__((packed)) CPUState {
struct FEX_PACKED CPUState {
uint64_t rip; ///< Current core's RIP. May not be entirely accurate while JIT is active
uint64_t gregs[16];
uint64_t : 64;
@@ -51,6 +54,6 @@ namespace FEXCore::Core {
constexpr uint64_t PAGE_SIZE = 4096;
__attribute__((visibility("default"))) std::string_view const& GetFlagName(unsigned Flag);
__attribute__((visibility("default"))) std::string_view const& GetGRegName(unsigned Reg);
FEX_DEFAULT_VISIBILITY std::string_view const& GetFlagName(unsigned Flag);
FEX_DEFAULT_VISIBILITY std::string_view const& GetGRegName(unsigned Reg);
}
+7 -2
View File
@@ -1,4 +1,7 @@
#pragma once
#include <FEXCore/Utils/CompilerDefs.h>
#include <cstdint>
#include <functional>
#include <signal.h>
@@ -7,11 +10,11 @@ namespace FEXCore {
namespace Core {
struct InternalThreadState;
}
struct __attribute__((packed)) GuestSAMask {
struct FEX_PACKED GuestSAMask {
uint64_t Val;
};
struct __attribute__((packed)) GuestSigAction {
struct FEX_PACKED GuestSigAction {
union {
void (*handler)(int);
void (*sigaction)(int, siginfo_t *, void*);
@@ -27,6 +30,8 @@ namespace Core {
class SignalDelegator {
public:
virtual ~SignalDelegator() = default;
/**
* @brief Registers an emulated thread's object to a TLS object
*
+10 -7
View File
@@ -1,4 +1,7 @@
#pragma once
#include <FEXCore/Utils/CompilerDefs.h>
#include <cstddef>
#include <cstdint>
@@ -13,7 +16,7 @@ namespace FEXCore {
constexpr uint64_t UC_STRICT_RESTORE_SS = (1ULL << 2);
///< Describes the signal stack
struct __attribute__((packed)) stack_t {
struct FEX_PACKED stack_t {
void *ss_sp;
int32_t ss_flags;
uint32_t : 32;
@@ -21,7 +24,7 @@ namespace FEXCore {
};
static_assert(sizeof(FEXCore::x86_64::stack_t) == 24, "This needs to be the right size");
struct __attribute__((packed)) _libc_fpstate {
struct FEX_PACKED _libc_fpstate {
// This is in FXSAVE format
uint16_t fcw;
uint16_t fsw;
@@ -65,19 +68,19 @@ namespace FEXCore {
};
static_assert(FEX_REG_CR2 == 22, "Oops");
struct __attribute__((packed)) mcontext_t {
struct FEX_PACKED mcontext_t {
uint64_t gregs[23];
FEXCore::x86_64::_libc_fpstate *fpregs;
uint64_t __reserved[8];
};
static_assert(sizeof(FEXCore::x86_64::mcontext_t) == 256, "This needs to be the right size");
struct __attribute__((packed)) sigset_t {
struct FEX_PACKED sigset_t {
uint64_t val[16];
};
static_assert(sizeof(FEXCore::x86_64::sigset_t) == 128, "This needs to be the right size");
struct __attribute__((packed)) ucontext_t {
struct FEX_PACKED ucontext_t {
uint64_t uc_flags;
FEXCore::x86_64::ucontext_t *uc_link;
FEXCore::x86_64::stack_t uc_stack;
@@ -92,12 +95,12 @@ namespace FEXCore {
}
namespace x86 {
struct __attribute__((packed)) siginfo_t {
struct FEX_PACKED siginfo_t {
uint32_t pad[32];
};
static_assert(sizeof(FEXCore::x86::siginfo_t) == 128, "This needs to be the right size");
struct __attribute__((packed)) ucontext_t {
struct FEX_PACKED ucontext_t {
uint32_t pad[91];
};
static_assert(sizeof(FEXCore::x86::ucontext_t) == 364, "This needs to be the right size");
@@ -54,11 +54,11 @@ namespace FEXCore::Core {
std::vector<DebugDataSubblock> Subblocks;
};
enum SignalEvent {
SIGNALEVENT_NONE, // If the guest uses our signal we need to know it was errant on our end
SIGNALEVENT_PAUSE,
SIGNALEVENT_STOP,
SIGNALEVENT_RETURN,
enum class SignalEvent {
Nothing, // If the guest uses our signal we need to know it was errant on our end
Pause,
Stop,
Return,
};
struct LocalIREntry {
@@ -78,7 +78,7 @@ namespace FEXCore::Core {
} RunningEvents;
FEXCore::Context::Context *CTX;
std::atomic<SignalEvent> SignalReason {SignalEvent::SIGNALEVENT_NONE};
std::atomic<SignalEvent> SignalReason{SignalEvent::Nothing};
std::unique_ptr<FEXCore::Threads::Thread> ExecutionThread;
Event StartRunning;
@@ -107,7 +107,7 @@ namespace FEXCore::Core {
alignas(16) FEXCore::Core::CpuStateFrame BaseFrameState{};
};
static_assert(std::is_standard_layout<InternalThreadState>::value, "This needs to be standard layout");
// static_assert(std::is_standard_layout<InternalThreadState>::value, "This needs to be standard layout");
}
+80 -58
View File
@@ -1,6 +1,7 @@
#pragma once
#include <FEXCore/Core/Context.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <cstdint>
#include <cstring>
@@ -99,54 +100,72 @@ inline void PopOpAddrIf(uint32_t *Flags, uint32_t Flag) {
}
union DecodedOperand {
enum {
TYPE_NONE,
TYPE_GPR,
TYPE_GPR_DIRECT,
TYPE_GPR_INDIRECT,
TYPE_RIP_RELATIVE,
TYPE_LITERAL,
TYPE_SIB,
struct DecodedOperand {
enum class OpType : uint8_t {
Nothing,
GPR,
GPRDirect,
GPRIndirect,
RIPRelative,
Literal,
SIB,
};
struct {
uint8_t Type;
} TypeNone;
bool IsNone() const {
return Type == OpType::Nothing;
}
bool IsGPR() const {
return Type == OpType::GPR;
}
bool IsGPRDirect() const {
return Type == OpType::GPRDirect;
}
bool IsGPRIndirect() const {
return Type == OpType::GPRIndirect;
}
bool IsRIPRelative() const {
return Type == OpType::RIPRelative;
}
bool IsLiteral() const {
return Type == OpType::Literal;
}
bool IsSIB() const {
return Type == OpType::SIB;
}
struct {
uint8_t Type;
bool HighBits;
uint8_t GPR;
} TypeGPR;
union TypeUnion {
struct {
bool HighBits;
uint8_t GPR;
} GPR;
struct {
uint8_t Type;
uint8_t GPR;
int32_t Displacement;
} TypeGPRIndirect;
struct {
uint8_t GPR;
int32_t Displacement;
} GPRIndirect;
struct {
uint8_t Type;
union {
int32_t s;
uint32_t u;
struct {
union {
int32_t s;
uint32_t u;
} Value;
} RIPLiteral;
struct {
uint8_t Size;
uint64_t Value;
} Literal;
} TypeRIPLiteral;
struct {
uint8_t Type;
uint8_t Size;
uint64_t Literal;
} TypeLiteral;
struct {
uint8_t Index; // ~0 invalid
uint8_t Base; // ~0 invalid
uint32_t Scale : 8;
int32_t Offset;
} SIB;
};
struct {
uint8_t Type;
uint8_t Index; // ~0 invalid
uint8_t Base; // ~0 invalid
uint32_t Scale : 8;
int32_t Offset;
} TypeSIB;
OpType Type;
TypeUnion Data;
};
struct DecodedInst {
@@ -418,6 +437,9 @@ struct X86InstInfo {
// We don't care if the opcode dispatcher differs
return true;
}
bool operator!=(const X86InstInfo &b) const {
return !operator==(b);
}
};
static_assert(std::is_trivial<X86InstInfo>::value, "X86InstInfo needs to be trivial");
@@ -455,29 +477,29 @@ constexpr size_t MAX_XOP_GROUP_TABLE_SIZE = (1 << 6);
constexpr size_t MAX_EVEX_TABLE_SIZE = 256;
extern __attribute__((visibility("default"))) X86InstInfo BaseOps[MAX_PRIMARY_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo SecondBaseOps[MAX_SECOND_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo RepModOps[MAX_REP_MOD_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo RepNEModOps[MAX_REPNE_MOD_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo OpSizeModOps[MAX_OPSIZE_MOD_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo PrimaryInstGroupOps[MAX_INST_GROUP_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo SecondInstGroupOps[MAX_INST_SECOND_GROUP_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo SecondModRMTableOps[MAX_SECOND_MODRM_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo X87Ops[MAX_X87_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo DDDNowOps[MAX_3DNOW_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo H0F38TableOps[MAX_0F_38_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo H0F3ATableOps[MAX_0F_3A_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo BaseOps[MAX_PRIMARY_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo SecondBaseOps[MAX_SECOND_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo RepModOps[MAX_REP_MOD_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo RepNEModOps[MAX_REPNE_MOD_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo OpSizeModOps[MAX_OPSIZE_MOD_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo PrimaryInstGroupOps[MAX_INST_GROUP_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo SecondInstGroupOps[MAX_INST_SECOND_GROUP_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo SecondModRMTableOps[MAX_SECOND_MODRM_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo X87Ops[MAX_X87_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo DDDNowOps[MAX_3DNOW_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo H0F38TableOps[MAX_0F_38_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo H0F3ATableOps[MAX_0F_3A_TABLE_SIZE];
// VEX
extern __attribute__((visibility("default"))) X86InstInfo VEXTableOps[MAX_VEX_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo VEXTableGroupOps[MAX_VEX_GROUP_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo VEXTableOps[MAX_VEX_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo VEXTableGroupOps[MAX_VEX_GROUP_TABLE_SIZE];
// XOP
extern __attribute__((visibility("default"))) X86InstInfo XOPTableOps[MAX_XOP_TABLE_SIZE];
extern __attribute__((visibility("default"))) X86InstInfo XOPTableGroupOps[MAX_XOP_GROUP_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo XOPTableOps[MAX_XOP_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo XOPTableGroupOps[MAX_XOP_GROUP_TABLE_SIZE];
// EVEX
extern __attribute__((visibility("default"))) X86InstInfo EVEXTableOps[MAX_EVEX_TABLE_SIZE];
extern FEX_DEFAULT_VISIBILITY X86InstInfo EVEXTableOps[MAX_EVEX_TABLE_SIZE];
__attribute__((visibility("default"))) void InitializeInfoTables(Context::OperatingMode Mode);
FEX_DEFAULT_VISIBILITY void InitializeInfoTables(Context::OperatingMode Mode);
}
@@ -7,12 +7,12 @@ namespace FEXCore::HLE {
// Tracking relationships between thread IDs and such
class ThreadManagement {
public:
uint64_t GetUID() { return UID; }
uint64_t GetGID() { return GID; }
uint64_t GetEUID() { return EUID; }
uint64_t GetEGID() { return EGID; }
uint64_t GetTID() { return TID; }
uint64_t GetPID() { return PID; }
uint64_t GetUID() const { return UID; }
uint64_t GetGID() const { return GID; }
uint64_t GetEUID() const { return EUID; }
uint64_t GetEGID() const { return EGID; }
uint64_t GetTID() const { return TID; }
uint64_t GetPID() const { return PID; }
uint64_t UID{1000};
uint64_t GID{1000};
+37 -29
View File
@@ -1,8 +1,12 @@
#pragma once
#include <FEXCore/Utils/CompilerDefs.h>
#include <array>
#include <cassert>
#include <cstdint>
#include <string.h>
#include <cstring>
#include <memory>
#include <sstream>
#include <tuple>
@@ -70,11 +74,10 @@ struct NodeWrapperBase final {
Type const *GetNode(uintptr_t Base) const { return reinterpret_cast<Type*>(Base + NodeOffset); }
void SetOffset(uintptr_t Base, uintptr_t Value) { NodeOffset = Value - Base; }
constexpr bool operator==(NodeWrapperBase<Type> const &rhs) const { return NodeOffset == rhs.NodeOffset; }
constexpr bool operator!=(NodeWrapperBase<Type> const &rhs) const { return !operator==(rhs); }
friend constexpr bool operator==(const NodeWrapperBase<Type>&, const NodeWrapperBase<Type>&) = default;
};
static_assert(std::is_trivial<NodeWrapperBase<OrderedNode>>::value);
static_assert(std::is_trivial_v<NodeWrapperBase<OrderedNode>>);
static_assert(sizeof(NodeWrapperBase<OrderedNode>) == sizeof(uint32_t));
@@ -252,76 +255,81 @@ class OrderedNode final {
void SetUses(uint32_t Uses) { NumUses = Uses; }
};
static_assert(std::is_trivial<OrderedNode>::value);
static_assert(std::is_trivially_copyable<OrderedNode>::value);
static_assert(std::is_trivial_v<OrderedNode>);
static_assert(std::is_trivially_copyable_v<OrderedNode>);
static_assert(offsetof(OrderedNode, Header) == 0);
static_assert(sizeof(OrderedNode) == (sizeof(OrderedNodeHeader) + sizeof(uint32_t)));
struct RegisterClassType final {
uint32_t Val;
operator uint32_t() {
constexpr operator uint32_t() const {
return Val;
}
constexpr bool operator==(RegisterClassType const &rhs) const { return Val == rhs.Val; }
constexpr bool operator!=(RegisterClassType const &rhs) const { return !operator==(rhs); }
friend constexpr bool operator==(const RegisterClassType&, const RegisterClassType&) = default;
};
struct CondClassType final {
uint8_t Val;
operator uint8_t() {
constexpr operator uint8_t() const {
return Val;
}
friend constexpr bool operator==(const CondClassType&, const CondClassType&) = default;
};
struct MemOffsetType final {
uint8_t Val;
operator uint8_t() {
constexpr operator uint8_t() const {
return Val;
}
int operator ==(const MemOffsetType other) {
return Val == other.Val;
}
int operator !=(const MemOffsetType other) {
return Val != other.Val;
}
friend constexpr bool operator==(const MemOffsetType&, const MemOffsetType&) = default;
};
struct TypeDefinition final {
uint16_t Val;
operator uint16_t() const {
constexpr operator uint16_t() const {
return Val;
}
static TypeDefinition Create(uint8_t Bytes) {
static constexpr TypeDefinition Create(uint8_t Bytes) {
TypeDefinition Type{};
Type.Val = Bytes << 8;
return Type;
}
static TypeDefinition Create(uint8_t Bytes, uint8_t Elements) {
static constexpr TypeDefinition Create(uint8_t Bytes, uint8_t Elements) {
TypeDefinition Type{};
Type.Val = (Bytes << 8) | (Elements & 255);
return Type;
}
uint8_t Bytes() const {
constexpr uint8_t Bytes() const {
return Val >> 8;
}
uint8_t Elements() const {
constexpr uint8_t Elements() const {
return Val & 255;
}
friend constexpr bool operator==(const TypeDefinition&, const TypeDefinition&) = default;
};
static_assert(std::is_trivial<TypeDefinition>::value);
static_assert(std::is_trivial_v<TypeDefinition>);
struct FenceType final {
uint8_t Val;
operator uint8_t() const {
constexpr operator uint8_t() const {
return Val;
}
constexpr bool operator==(FenceType const &rhs) const { return Val == rhs.Val; }
constexpr bool operator!=(FenceType const &rhs) const { return !operator==(rhs); }
friend constexpr bool operator==(const FenceType&, const FenceType&) = default;
};
struct RoundType final {
uint8_t Val;
constexpr operator uint8_t() const {
return Val;
}
friend constexpr bool operator==(const RoundType&, const RoundType&) = default;
};
struct SHA256Sum final {
@@ -383,7 +391,7 @@ public:
return { RealNode, RealNode->Op(IRList) };
}
uint32_t ID() {
uint32_t ID() const {
return Node.ID();
}
@@ -463,8 +471,8 @@ public:
class IRListView;
class IREmitter;
__attribute__((visibility("default"))) void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData);
__attribute__((visibility("default"))) IREmitter* Parse(std::istream *in);
FEX_DEFAULT_VISIBILITY void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData);
FEX_DEFAULT_VISIBILITY std::unique_ptr<IREmitter> Parse(std::istream *in);
template<typename Type>
inline uint32_t NodeWrapperBase<Type>::ID() const { return NodeOffset / sizeof(IR::OrderedNode); }
+24 -3
View File
@@ -111,9 +111,15 @@ friend class FEXCore::IR::PassManager;
IRPair<IROp_VExtractElement> _VExtractElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
return _VExtractElement(ssa0, Index, RegisterSize, ElementSize);
}
IRPair<IROp_VDupElement> _VDupElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
return _VDupElement(ssa0, Index, RegisterSize, ElementSize);
}
IRPair<IROp_VAnd> _VAnd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VAnd(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VBic> _VBic(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VBic(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VOr> _VOr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VOr(ssa0, ssa1, RegisterSize, ElementSize);
}
@@ -144,12 +150,21 @@ friend class FEXCore::IR::PassManager;
IRPair<IROp_VAddV> _VAddV(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
return _VAddV(ssa0, RegisterSize, ElementSize);
}
IRPair<IROp_VUMinV> _VUMinV(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
return _VUMinV(ssa0, RegisterSize, ElementSize);
}
IRPair<IROp_VURAvg> _VURAvg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VURAvg(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VAbs> _VAbs(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
return _VAbs(ssa0, RegisterSize, ElementSize);
}
IRPair<IROp_VPopcount> _VPopcount(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
return _VPopcount(ssa0, RegisterSize, ElementSize);
}
IRPair<IROp_VFMul> _VFMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VFMul(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VUMin> _VUMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VUMin(ssa0, ssa1, RegisterSize, ElementSize);
}
@@ -168,6 +183,12 @@ friend class FEXCore::IR::PassManager;
IRPair<IROp_VZip2> _VZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VZip2(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VUnZip> _VUnZip(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VUnZip(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VUnZip2> _VUnZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VUnZip2(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VCMPEQ> _VCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VCMPEQ(ssa0, ssa1, RegisterSize, ElementSize);
}
@@ -294,9 +315,6 @@ friend class FEXCore::IR::PassManager;
IRPair<IROp_Vector_FToF> _Vector_FToF(uint8_t RegisterSize, uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
return _Vector_FToF(ssa0, SrcElementSize, RegisterSize, DstElementSize);
}
IRPair<IROp_Float_FromGPR_U> _Float_FromGPR_U(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
return _Float_FromGPR_U(ssa0, SrcElementSize, DstElementSize);
}
IRPair<IROp_Float_FromGPR_S> _Float_FromGPR_S(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
return _Float_FromGPR_S(ssa0, SrcElementSize, DstElementSize);
}
@@ -321,6 +339,9 @@ friend class FEXCore::IR::PassManager;
IRPair<IROp_VSMull2> _VSMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VSMull2(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VUABDL> _VUABDL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
return _VUABDL(ssa0, ssa1, RegisterSize, ElementSize);
}
IRPair<IROp_VSXTL> _VSXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
return _VSXTL(ssa0, RegisterSize, ElementSize);
}
+2 -2
View File
@@ -35,12 +35,12 @@ class DualIntrusiveAllocator final {
FEXCore::Allocator::free(reinterpret_cast<void*>(Data));
}
bool DataCheckSize(size_t Size) {
bool DataCheckSize(size_t Size) const {
size_t NewOffset = DataCurrentOffset + Size;
return NewOffset <= MemorySize;
}
bool ListCheckSize(size_t Size) {
bool ListCheckSize(size_t Size) const {
size_t NewOffset = ListCurrentOffset + Size;
return NewOffset <= MemorySize;
}
@@ -21,7 +21,7 @@ union PhysicalRegister {
return PhysicalRegister(InvalidClass, InvalidReg);
}
bool IsInvalid() {
bool IsInvalid() const {
return *this == Invalid();
}
};
+8 -5
View File
@@ -1,5 +1,7 @@
#pragma once
#include <FEXCore/Utils/CompilerDefs.h>
#include <cstdint>
#include <functional>
@@ -11,11 +13,12 @@ namespace FEXCore::Allocator {
using REALLOC_Hook = void*(*)(void*, size_t);
using FREE_Hook = void(*)(void*);
__attribute__((visibility("default"))) extern MMAP_Hook mmap;
__attribute__((visibility("default"))) extern MUNMAP_Hook munmap;
__attribute__((visibility("default"))) extern MALLOC_Hook malloc;
__attribute__((visibility("default"))) extern REALLOC_Hook realloc;
__attribute__((visibility("default"))) extern FREE_Hook free;
FEX_DEFAULT_VISIBILITY extern MMAP_Hook mmap;
FEX_DEFAULT_VISIBILITY extern MUNMAP_Hook munmap;
FEX_DEFAULT_VISIBILITY extern MALLOC_Hook malloc;
FEX_DEFAULT_VISIBILITY extern REALLOC_Hook realloc;
FEX_DEFAULT_VISIBILITY extern FREE_Hook free;
void SetupHooks();
void ClearHooks();
}
+64
View File
@@ -0,0 +1,64 @@
#pragma once
// Header for various utilities that operate on bits and bytes.
#include <bit>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <type_traits>
namespace FEXCore {
// Determines the number of bits inside of a given type.
template <typename T>
[[nodiscard]] constexpr size_t BitSize() noexcept {
return sizeof(T) * CHAR_BIT;
}
// Swaps the bytes of a 16-bit unsigned value.
[[nodiscard]] inline uint16_t BSwap16(uint16_t value) noexcept {
#ifdef __GNUC__
return __builtin_bswap16(value);
#else
return (value >> 8) | (value << 8);
#endif
}
// Swaps the bytes of a 32-bit unsigned value.
[[nodiscard]] inline uint32_t BSwap32(uint32_t value) noexcept {
#ifdef __GNUC__
return __builtin_bswap32(value);
#else
return ((value & 0xFF000000U) >> 24) | ((value & 0x00FF0000U) >> 8) |
((value & 0x0000FF00U) << 8) | ((value & 0x000000FFU) << 24);
#endif
}
// Swaps the bytes of a 64-bit unsigned value.
[[nodiscard]] inline uint64_t BSwap64(uint64_t value) noexcept {
#ifdef __GNUC__
return __builtin_bswap64(value);
#else
return ((value & 0xFF00000000000000ULL) >> 56) | ((value & 0x00FF000000000000ULL) >> 40) |
((value & 0x0000FF0000000000ULL) >> 24) | ((value & 0x000000FF00000000ULL) >> 8) |
((value & 0x00000000FF000000ULL) << 8) | ((value & 0x0000000000FF0000ULL) << 24) |
((value & 0x000000000000FF00ULL) << 40) | ((value & 0x00000000000000FFULL) << 56);
#endif
}
// Finds the first least-significant set bit within a given value.
// Note that all returned indices are 1-based, not 0-based.
template <typename T>
[[nodiscard]] constexpr int FindFirstSetBit(T value) noexcept {
static_assert(std::is_unsigned_v<T>, "Type must be unsigned.");
if (value == 0) {
return 0;
}
const int trailing_zeroes = std::countr_zero(value);
return trailing_zeroes + 1;
}
} // namespace FEXCore
+29
View File
@@ -0,0 +1,29 @@
#pragma once
// Contains general abstractions related to compilers used to build FEX.
// Specifies the minimum alignment for a variable or structure field, measured in bytes.
#define FEX_ALIGNED(alignment) __attribute__((aligned(alignment)))
// Allows annotating declarations with extra information.
#define FEX_ANNOTATE(annotation_str) __attribute__((annotate(annotation_str)))
// Makes the attributed entity have the default DSO visibility level.
// Compiler options can affect the visibility of symbols. This attribute
// overrides said changes. This gives entities external linkage.
#define FEX_DEFAULT_VISIBILITY __attribute__((visibility("default")))
// Indicates that the specified function doesn't need a function prologue/epilogue.
// emitted for it by the compiler.
#define FEX_NAKED __attribute__((naked))
// Specifies that a structure member or structure itself should have the smallest possible alignment.
#define FEX_PACKED __attribute__((packed))
// Causes execution to exit abnormally.
#define FEX_TRAP_EXECUTION __builtin_trap()
// Dictates to the compiler that the path this is on should not be reachable
// from normal execution control flow. If normal execution does reach this,
// then program behavior is undefined.
#define FEX_UNREACHABLE __builtin_unreachable()
+6 -3
View File
@@ -1,10 +1,13 @@
#pragma once
#include "ELFContainer.h"
#include <vector>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/ELFContainer.h>
#include <unordered_map>
#include <vector>
namespace ELFLoader {
class __attribute__((visibility("default"))) ELFSymbolDatabase final {
class FEX_DEFAULT_VISIBILITY ELFSymbolDatabase final {
public:
ELFSymbolDatabase(::ELFLoader::ELFContainer *file);
~ELFSymbolDatabase();
+105 -8
View File
@@ -1,7 +1,12 @@
#pragma once
#include <FEXCore/Utils/CompilerDefs.h>
#include <functional>
#include <cstdarg>
#include <sstream>
#include <stdarg.h>
#include <fmt/format.h>
namespace LogMan {
enum DebugLevels {
@@ -18,8 +23,8 @@ constexpr DebugLevels MSG_LEVEL = INFO;
namespace Throw {
using ThrowHandler = void(*)(char const *Message);
__attribute__((visibility("default"))) void InstallHandler(ThrowHandler Handler);
__attribute__((visibility("default"))) void UnInstallHandlers();
FEX_DEFAULT_VISIBILITY void InstallHandler(ThrowHandler Handler);
FEX_DEFAULT_VISIBILITY void UnInstallHandlers();
[[noreturn]] void M(const char *fmt, va_list args);
@@ -38,14 +43,32 @@ static inline void A(bool, const char*, ...) {}
#define LOGMAN_THROW_A(pred, ...) do {} while (0)
#endif
// Fmt interface
[[noreturn]] void MFmt(const char *fmt, const fmt::format_args& args);
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
template <typename... Args>
static inline void AFmt(bool Value, const char *fmt, const Args&... args) {
if (MSG_LEVEL < ASSERT || Value) {
return;
}
MFmt(fmt, fmt::make_format_args(args...));
}
#define LOGMAN_THROW_A_FMT(pred, ...) do { LogMan::Throw::AFmt(pred, __VA_ARGS__); } while (0)
#else
static inline void AFmt(bool, const char*, ...) {}
#define LOGMAN_THROW_A_FMT(pred, ...) do {} while (0)
#endif
} // namespace Throw
namespace Msg {
using MsgHandler = void(*)(DebugLevels Level, char const *Message);
__attribute__((visibility("default"))) void InstallHandler(MsgHandler Handler);
__attribute__((visibility("default"))) void UnInstallHandlers();
FEX_DEFAULT_VISIBILITY void InstallHandler(MsgHandler Handler);
FEX_DEFAULT_VISIBILITY void UnInstallHandlers();
__attribute__((visibility("default"))) void M(DebugLevels Level, const char *fmt, va_list args);
FEX_DEFAULT_VISIBILITY void M(DebugLevels Level, const char *fmt, va_list args);
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
static inline void A(const char *fmt, ...) {
@@ -55,7 +78,7 @@ static inline void A(const char *fmt, ...) {
M(ASSERT, fmt, args);
va_end(args);
}
__builtin_trap();
FEX_TRAP_EXECUTION;
}
#define LOGMAN_MSG_A(...) do { LogMan::Msg::A(__VA_ARGS__); } while (0)
#else
@@ -114,7 +137,81 @@ static inline void ERR(const char *fmt, ...) {
#define ERROR_AND_DIE(...) \
do { \
LogMan::Msg::E(__VA_ARGS__); \
__builtin_trap(); \
FEX_TRAP_EXECUTION; \
} while(0)
// Fmt-capable interface.
FEX_DEFAULT_VISIBILITY void MFmtImpl(DebugLevels level, const char* fmt, const fmt::format_args& args);
template <typename... Args>
static inline void MFmt(DebugLevels level, const char* fmt, const Args&... args) {
MFmtImpl(level, fmt, fmt::make_format_args(args...));
}
template <typename... Args>
static inline void EFmt(const char* fmt, const Args&... args) {
if (MSG_LEVEL < ERROR) {
return;
}
MFmtImpl(ERROR, fmt, fmt::make_format_args(args...));
}
template <typename... Args>
static inline void DFmt(const char* fmt, const Args&... args) {
if (MSG_LEVEL < DEBUG) {
return;
}
MFmtImpl(DEBUG, fmt, fmt::make_format_args(args...));
}
template <typename... Args>
static inline void IFmt(const char* fmt, const Args&... args) {
if (MSG_LEVEL < INFO) {
return;
}
MFmtImpl(INFO, fmt, fmt::make_format_args(args...));
}
template <typename... Args>
static inline void OutFmt(const char* fmt, const Args&... args) {
MFmtImpl(STDOUT, fmt, fmt::make_format_args(args...));
}
template <typename... Args>
static inline void ErrFmt(const char* fmt, const Args&... args) {
MFmtImpl(STDERR, fmt, fmt::make_format_args(args...));
}
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
template <typename... Args>
static inline void AFmt(const char *fmt, const Args&... args) {
if (MSG_LEVEL < ASSERT) {
return;
}
MFmtImpl(ASSERT, fmt, fmt::make_format_args(args...));
FEX_TRAP_EXECUTION;
}
#define LOGMAN_MSG_A_FMT(...) do { LogMan::Msg::AFmt(__VA_ARGS__); } while (0)
#else
template <typename... Args>
static inline void AFmt(const char*, const Args&...) {}
#define LOGMAN_MSG_A_FMT(...) do {} while(0)
#endif
#define WARN_ONCE_FMT(...) \
do { \
static bool Warned{}; \
if (!Warned) { \
LogMan::Msg::DFmt(__VA_ARGS__); \
Warned = true; \
} \
} while (0);
#define ERROR_AND_DIE_FMT(...) \
do { \
LogMan::Msg::EFmt(__VA_ARGS__); \
FEX_TRAP_EXECUTION; \
} while(0)
} // namespace Msg
Vendored Submodule
+1
Submodule External/drm-headers added at 2b23749e35.
Vendored Submodule
+1
Submodule External/fmt added at 7bdf0628b1.
+1 -1
+9 -9
View File
@@ -17,7 +17,7 @@
#include <json-maker.h>
namespace FEX::Config {
bool LoadConfigFile(std::vector<char> &Data, std::string Config) {
static bool LoadConfigFile(std::vector<char> &Data, const std::string &Config) {
std::fstream ConfigFile;
ConfigFile.open(Config, std::ios::in);
@@ -72,7 +72,7 @@ namespace FEX::Config {
return &*alloc->json_objects->emplace(alloc->json_objects->end());
}
void LoadJSonConfig(std::string &Config, std::function<void(const char *Name, const char *ConfigSring)> Func) {
static void LoadJSonConfig(const std::string &Config, std::function<void(const char *Name, const char *ConfigSring)> Func) {
std::vector<char> Data;
if (!LoadConfigFile(Data, Config)) {
return;
@@ -123,7 +123,7 @@ namespace FEX::Config {
#include <FEXCore/Config/ConfigValues.inl>
}};
static const std::map<std::string, FEXCore::Config::ConfigOption> ConfigLookup = {{
static const std::map<std::string, FEXCore::Config::ConfigOption, std::less<>> ConfigLookup = {{
#define OPT_BASE(type, group, enum, json, default) {#json, FEXCore::Config::ConfigOption::CONFIG_##enum},
#include <FEXCore/Config/ConfigValues.inl>
}};
@@ -132,7 +132,7 @@ namespace FEX::Config {
#include <FEXCore/Config/ConfigValues.inl>
}};
void SaveLayerToJSON(std::string Filename, FEXCore::Config::Layer *const Layer) {
void SaveLayerToJSON(const std::string& Filename, FEXCore::Config::Layer *const Layer) {
char Buffer[4096];
char *Dest{};
Dest = json_objOpen(Buffer, nullptr);
@@ -166,13 +166,13 @@ namespace FEX::Config {
}
MainLoader::MainLoader()
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN) {
Config = FEXCore::Config::GetConfigFileLocation();
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN)
, Config{FEXCore::Config::GetConfigFileLocation()} {
}
MainLoader::MainLoader(std::string ConfigFile)
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN) {
Config = ConfigFile;
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN)
, Config{std::move(ConfigFile)} {
}
void MainLoader::Load() {
@@ -181,7 +181,7 @@ namespace FEX::Config {
});
}
AppLoader::AppLoader(std::string Filename, bool Global)
AppLoader::AppLoader(const std::string& Filename, bool Global)
: FEX::Config::OptionMapper(Global ? FEXCore::Config::LayerType::LAYER_GLOBAL_APP : FEXCore::Config::LayerType::LAYER_LOCAL_APP) {
Config = FEXCore::Config::GetApplicationConfig(Filename, Global);
+2 -2
View File
@@ -40,7 +40,7 @@ namespace FEX::Config {
class AppLoader final : public FEX::Config::OptionMapper {
public:
explicit AppLoader(std::string Filename, bool Global);
explicit AppLoader(const std::string& Filename, bool Global);
void Load();
private:
@@ -56,5 +56,5 @@ namespace FEX::Config {
char *const *envp;
};
void SaveLayerToJSON(std::string Filename, FEXCore::Config::Layer *const Layer);
void SaveLayerToJSON(const std::string& Filename, FEXCore::Config::Layer *const Layer);
}
+3 -3
View File
@@ -174,11 +174,11 @@ namespace HostFactory {
return nullptr;
}
FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
return new HostCore(CTX, Thread, false);
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
return std::make_unique<HostCore>(CTX, Thread, false);
}
#else
FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
LOGMAN_MSG_A("HostCPU factory doesn't exist for this host");
return nullptr;
}
+5 -2
View File
@@ -1,3 +1,7 @@
#pragma once
#include <memory>
namespace FEXCore::CPU {
class CPUBackend;
}
@@ -9,6 +13,5 @@ namespace FEXCore::Core {
}
namespace HostFactory {
FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread);
FEXCore::CPU::CPUBackend *CPUCreationFactoryFallback(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread);
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread);
}
+1 -1
View File
@@ -285,7 +285,7 @@ public:
return DB.DefaultRIP();
}
bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override {
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override {
auto DoMMap = [Mapper](uint64_t Address, size_t Size, bool FixedNoReplace) -> void* {
void *Result = Mapper(reinterpret_cast<void*>(Address), Size, PROT_READ | PROT_WRITE, (FixedNoReplace ? MAP_FIXED_NOREPLACE : MAP_FIXED) | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
LOGMAN_THROW_A(Result != (void*)~0ULL, "Couldn't mmap");
+8 -4
View File
@@ -80,7 +80,8 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
return false;
} else {
auto Filename = get_fdpath(file.fd);
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0});
Sections = std::make_unique<std::vector<LoadedSection>>();
Sections->push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0});
return true;
}
@@ -203,7 +204,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
bool Executable;
};
std::vector<LoadedSection> Sections;
std::unique_ptr<std::vector<LoadedSection>> Sections;
ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *AdditionalEnvp = nullptr) :
Args {args} {
@@ -254,6 +255,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
}
}
void FreeSections() {
Sections.reset();
}
virtual uint64_t StackSize() const override { return STACK_SIZE; }
virtual uint64_t GetStackPointer() override { return StackPointer; }
virtual uint64_t DefaultRIP() const override { return Entrypoint; };
@@ -268,8 +273,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
uint64_t val;
};
virtual bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override {
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override {
for (auto Header: MainElf.phdrs) {
if (Header.p_type == PT_GNU_STACK) {
if (Header.p_flags & PF_X)
+186 -119
View File
@@ -30,10 +30,14 @@ $end_info$
#include <filesystem>
#include <algorithm>
#include <set>
#include <thread>
#include <queue>
#include <sys/sysinfo.h>
namespace {
static bool SilentLog;
static FILE *OutputFD {stderr};
static int OutputFD {STDERR_FILENO};
void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
const char *CharLevel{nullptr};
@@ -66,15 +70,17 @@ void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
}
if (!SilentLog) {
fprintf(OutputFD, "[%s] %s\n", CharLevel, Message);
fflush(OutputFD);
std::ostringstream Output;
Output << "[" << CharLevel << "] " << Message << std::endl;
write(OutputFD, Output.str().c_str(), Output.str().size());
}
}
void AssertHandler(char const *Message) {
if (!SilentLog) {
fprintf(OutputFD, "[ASSERT] %s\n", Message);
fflush(OutputFD);
std::ostringstream Output;
Output << "[ASSERT] " << Message << std::endl;
write(OutputFD, Output.str().c_str(), Output.str().size());
}
}
@@ -160,6 +166,152 @@ bool IsInterpreterInstalled() {
std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86_64");
}
void AOTGenSection(FEXCore::Context::Context *CTX, ELFCodeLoader2::LoadedSection &Section) {
// Make sure this section is executable and big enough
if (!Section.Executable || Section.Size < 16)
return;
std::set<uintptr_t> InitialBranchTargets;
// Load the ELF again with symbol parsing this time
ELFLoader::ELFContainer container{Section.Filename, "", false};
// Add symbols to the branch targets list
container.AddSymbols([&](ELFLoader::ELFSymbol* sym) {
auto Destination = sym->Address + Section.ElfBase;
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
return; // outside of current section, unlikely to be real code
}
InitialBranchTargets.insert(Destination);
});
LogMan::Msg::I("Symbol seed: %ld", InitialBranchTargets.size());
// Add unwind entries to the branch target list
container.AddUnwindEntries([&](uintptr_t Entry) {
auto Destination = Entry + Section.ElfBase;
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
return; // outside of current section, unlikely to be real code
}
InitialBranchTargets.insert(Destination);
});
LogMan::Msg::I("Symbol + Unwind seed: %ld", InitialBranchTargets.size());
// Scan the executable section and try to find function entries
for (size_t Offset = 0; Offset < (Section.Size - 16); Offset++) {
uint8_t *pCode = (uint8_t *)(Section.Base + Offset);
// Possible CALL <disp32>
if (*pCode == 0xE8) {
uintptr_t Destination = (int)(pCode[1] | (pCode[2] << 8) | (pCode[3] << 16) | (pCode[4] << 24));
Destination += (uintptr_t)pCode + 5;
auto DestinationPtr = (uint8_t*)Destination;
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
continue; // outside of current section, unlikely to be real code
if (DestinationPtr[0] == 0 && DestinationPtr[1] == 0)
continue; // add al, [rax], unlikely to be real code
InitialBranchTargets.insert(Destination);
}
// endbr64 marker marks an indirect branch destination
if (pCode[0] == 0xf3 && pCode[1] == 0x0f && pCode[2] == 0x1e && pCode[3] == 0xfa) {
InitialBranchTargets.insert((uintptr_t)pCode);
}
}
uint64_t SectionMaxAddress = Section.Base + Section.Size;
std::set<uint64_t> Compiled;
std::atomic<int> counter = 0;
std::queue<uint64_t> BranchTargets;
// Setup BranchTargets, Compiled sets from InitiaBranchTargets
Compiled.insert(InitialBranchTargets.begin(), InitialBranchTargets.end());
for (auto BranchTarget: InitialBranchTargets) {
BranchTargets.push(BranchTarget);
}
InitialBranchTargets.clear();
std::mutex QueueMutex;
std::vector<std::thread> ThreadPool;
for (int i = 0; i < get_nprocs_conf(); i++) {
std::thread thd([&BranchTargets, CTX, &counter, &Compiled, &Section, &QueueMutex, SectionMaxAddress]() {
// Setup thread - Each compilation thread uses its own backing FEX thread
FEXCore::Core::CPUState state;
auto Thread = FEXCore::Context::CreateThread(CTX, &state, gettid());
std::set<uint64_t> ExternalBranchesLocal;
FEXCore::Context::ConfigureAOTGen(Thread, &ExternalBranchesLocal, SectionMaxAddress);
for (;;) {
uint64_t BranchTarget;
// Get a entrypoint to process from the queue
QueueMutex.lock();
if (BranchTargets.empty()) {
QueueMutex.unlock();
break; // no entrypoint to process - exit
}
BranchTarget = BranchTargets.front();
BranchTargets.pop();
QueueMutex.unlock();
// Compile entrypoint
counter++;
FEXCore::Context::CompileRIP(Thread, BranchTarget);
// Are there more branches?
if (ExternalBranchesLocal.size() > 0) {
// Add them to the "to process" list
QueueMutex.lock();
for(auto Destination: ExternalBranchesLocal) {
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
continue;
if (Compiled.contains(Destination))
continue;
Compiled.insert(Destination);
BranchTargets.push(Destination);
}
QueueMutex.unlock();
ExternalBranchesLocal.clear();
}
}
// All entryproints processed, cleanup this thread
FEXCore::Context::DestroyThread(CTX, Thread);
});
// Add to the thread pool
ThreadPool.push_back(std::move(thd));
}
// Make sure all threads are finished
for (auto & Thread: ThreadPool) {
Thread.join();
}
ThreadPool.clear();
LogMan::Msg::I("\nAll Done: %d", counter.load());
}
int main(int argc, char **argv, char **const envp) {
bool IsInterpreter = RanAsInterpreter(argv[0]);
LogMan::Throw::InstallHandler(AssertHandler);
@@ -217,13 +369,13 @@ int main(int argc, char **argv, char **const envp) {
if (!::SilentLog) {
auto LogFile = OutputLog();
if (LogFile == "stderr") {
OutputFD = stderr;
OutputFD = STDERR_FILENO;
}
else if (LogFile == "stdout") {
OutputFD = stdout;
OutputFD = STDOUT_FILENO;
}
else if (!LogFile.empty()) {
OutputFD = fopen(LogFile.c_str(), "wb");
OutputFD = open(LogFile.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY);
}
}
@@ -292,6 +444,7 @@ int main(int argc, char **argv, char **const envp) {
}
}
// System allocator is now system allocator or FEX
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
auto CTX = FEXCore::Context::CreateNewContext();
@@ -327,7 +480,6 @@ int main(int argc, char **argv, char **const envp) {
});
}
if (AOTIRLoad() || AOTIRCapture() || AOTIRGenerate()) {
LogMan::Msg::I("Warning: AOTIR is experimental, and might lead to crashes. Capture doesn't work with programs that fork.");
}
@@ -338,98 +490,26 @@ int main(int argc, char **argv, char **const envp) {
return open(filepath.c_str(), O_RDONLY);
});
for(auto Section: Loader.Sections) {
FEXCore::Context::SetAOTIRWriter(CTX, [](const std::string& fileid) -> std::unique_ptr<std::ostream> {
auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir");
auto AOTWrite = std::make_unique<std::ofstream>(filepath, std::ios::out | std::ios::binary);
if (*AOTWrite) {
std::filesystem::resize_file(filepath, 0);
AOTWrite->seekp(0);
LogMan::Msg::I("AOTIR: Storing %s", fileid.c_str());
} else {
LogMan::Msg::I("AOTIR: Failed to store %s", fileid.c_str());
}
return AOTWrite;
});
for(auto Section: *Loader.Sections) {
FEXCore::Context::AddNamedRegion(CTX, Section.Base, Section.Size, Section.Offs, Section.Filename);
}
if (AOTIRGenerate()) {
for(auto Section: Loader.Sections) {
if (Section.Executable && Section.Size > 16) {
ELFLoader::ELFContainer container{Section.Filename, "", false};
std::set<uintptr_t> BranchTargets;
container.AddSymbols([&](ELFLoader::ELFSymbol* sym) {
auto Destination = sym->Address + Section.ElfBase;
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
//printf("Sym : %lx %lx out of range\n", sym->Address, Destination);
return; // outside of current section, unlikely to be real code
}
BranchTargets.insert(Destination);
});
LogMan::Msg::I("Symbol seed: %ld", BranchTargets.size());
container.AddUnwindEntries([&](uintptr_t Entry) {
auto Destination = Entry + Section.ElfBase;
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
//printf("Sym : %lx %lx out of range\n", sym->Address, Destination);
return; // outside of current section, unlikely to be real code
}
BranchTargets.insert(Destination);
});
LogMan::Msg::I("Symbol + Unwind seed: %ld", BranchTargets.size());
for (size_t Offset = 0; Offset < (Section.Size - 16); Offset++) {
uint8_t *pCode = (uint8_t *)(Section.Base + Offset);
if (*pCode == 0xE8) {
uintptr_t Destination = (int)(pCode[1] | (pCode[2] << 8) | (pCode[3] << 16) | (pCode[4] << 24));
Destination += (uintptr_t)pCode + 5;
auto DestinationPtr = (uint8_t*)Destination;
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
continue; // outside of current section, unlikely to be real code
if (DestinationPtr[0] == 0 && DestinationPtr[1] == 0)
continue; // add al, [rax], unlikely to be real code
/*
if (DestinationPtr[0] == 0x44 && DestinationPtr[1] == 0x0f && DestinationPtr[2] == 0x6f)
continue; // REX.W + movq leads to frontend bugs
*/
BranchTargets.insert(Destination);
}
if (pCode[0] == 0xf3 && pCode[1] == 0x0f && pCode[2] == 0x1e && pCode[3] == 0xfa) {
BranchTargets.insert((uintptr_t)pCode);
}
}
uint64_t SectionMaxAddress = Section.Base + Section.Size;
std::set<uint64_t> ExternalBranches;
FEXCore::Context::ConfigureAOTGen(CTX, &ExternalBranches, SectionMaxAddress);
std::set<uint64_t> Compiled;
int counter = 0;
do {
LogMan::Msg::I("Discovered %ld Branch Targets in this pass", BranchTargets.size());
for (auto RIP: BranchTargets) {
if ((counter++) % 1000 == 0)
LogMan::Msg::I("Compiling %d %lX", counter, RIP - Section.ElfBase);
FEXCore::Context::CompileRIP(CTX, RIP);
Compiled.insert(RIP);
}
LogMan::Msg::I("\nPass Done");
BranchTargets.clear();
for (auto Destination: ExternalBranches) {
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
continue;
if (Compiled.contains(Destination))
continue;
BranchTargets.insert(Destination);
}
ExternalBranches.clear();
} while (BranchTargets.size() > 0);
LogMan::Msg::I("\nAll Done: %d", counter);
}
for(auto &Section: *Loader.Sections) {
AOTGenSection(CTX, Section);
}
} else {
FEXCore::Context::RunUntilExit(CTX);
@@ -448,24 +528,10 @@ int main(int argc, char **argv, char **const envp) {
if (AOTIRCapture() || AOTIRGenerate()) {
auto WroteCache = FEXCore::Context::WriteAOTIR(CTX, [](const std::string& fileid) -> std::unique_ptr<std::ostream> {
auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir");
auto AOTWrite = std::make_unique<std::ofstream>(filepath, std::ios::out | std::ios::binary);
if (*AOTWrite) {
std::filesystem::resize_file(filepath, 0);
AOTWrite->seekp(0);
LogMan::Msg::I("AOTIR: Storing %s", fileid.c_str());
} else {
LogMan::Msg::I("AOTIR: Failed to store %s", fileid.c_str());
}
return AOTWrite;
});
if (WroteCache) {
LogMan::Msg::I("AOTIR Cache Stored");
} else {
LogMan::Msg::E("AOTIR Cache Store Failed");
}
FEXCore::Context::FinalizeAOTIRCache(CTX);
LogMan::Msg::I("AOTIR Cache Stored");
}
auto ProgramStatus = FEXCore::Context::GetProgramStatus(CTX);
@@ -474,16 +540,17 @@ int main(int argc, char **argv, char **const envp) {
SignalDelegation.reset();
FEXCore::Context::DestroyContext(CTX);
FEXCore::Context::ShutdownStaticTables();
Loader.FreeSections();
FEXCore::Config::Shutdown();
LogMan::Throw::UnInstallHandlers();
LogMan::Msg::UnInstallHandlers();
if (OutputFD != stderr &&
OutputFD != stdout &&
OutputFD != nullptr) {
fclose(OutputFD);
}
FEXCore::Allocator::ClearHooks();
// Allocator is now original system allocator
if (ShutdownReason == FEXCore::Context::ExitReason::EXIT_SHUTDOWN) {
return ProgramStatus;
+11 -8
View File
@@ -1,8 +1,8 @@
#pragma once
#include "Common/Config.h"
#include "Common/MathUtils.h"
#include <FEXCore/Core/CodeLoader.h>
#include <array>
#include <bitset>
#include <cassert>
@@ -10,13 +10,16 @@
#include <fstream>
#include <sys/mman.h>
#include <vector>
#include <FEXCore/Core/CodeLoader.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/BitUtils.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/ELFContainer.h>
#include <FEXCore/Utils/ELFSymbolDatabase.h>
#include <FEXCore/Utils/LogManager.h>
namespace FEX::HarnessHelper {
inline bool CompareStates(FEXCore::Core::CPUState const& State1,
@@ -214,7 +217,7 @@ namespace FEX::HarnessHelper {
[[maybe_unused]] std::bitset<64> RegFlags = RegData->RegKey;
assert(RegFlags.count() == 1 && "Must set reg data explicitly per register");
size_t NameIndex = __builtin_ffsl(RegData->RegKey)- 1;
size_t NameIndex = FEXCore::FindFirstSetBit(RegData->RegKey) - 1;
auto Offset = OffsetArray[NameIndex];
uint64_t *State1Data = reinterpret_cast<uint64_t*>(reinterpret_cast<uint64_t>(State1) + Offset);
uint64_t *State2Data = reinterpret_cast<uint64_t*>(reinterpret_cast<uint64_t>(State2) + Offset);
@@ -306,24 +309,24 @@ namespace FEX::HarnessHelper {
uint32_t OptionMemDataOffset;
uint32_t OptionMemDataCount;
uint8_t AdditionalData[];
}__attribute__((packed));
} FEX_PACKED;
struct MemoryRegionBase {
uint64_t Region;
uint64_t Size;
} __attribute__((packed));
} FEX_PACKED;
struct RegDataStructBase {
uint32_t RegDataCount;
uint64_t RegKey;
uint64_t RegValues[];
} __attribute__((packed));
} FEX_PACKED;
struct MemDataStructBase {
uint64_t address;
uint32_t length;
uint8_t data[];
} __attribute__((packed));
} FEX_PACKED;
std::vector<char> RawConfigFile;
ConfigStructBase BaseConfig;
@@ -365,7 +368,7 @@ namespace FEX::HarnessHelper {
return RIP;
}
bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override {
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override {
bool LimitedSize = true;
auto DoMMap = [](uint64_t Address, size_t Size) -> void* {
void *Result = FEXCore::Allocator::mmap(reinterpret_cast<void*>(Address), Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+1 -1
View File
@@ -84,7 +84,7 @@ public:
return IR->GetEntryRIP();
}
bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override
{
// Map the memory regions the test file asks for
IR->MapRegions();
+1 -1
View File
@@ -16,7 +16,7 @@ namespace FEX::IRLoader {
return;
}
ParsedCode.reset(FEXCore::IR::Parse(&fp));
ParsedCode = FEXCore::IR::Parse(&fp);
if (ParsedCode) {
auto NewIR = ParsedCode->ViewIR();
@@ -41,6 +41,7 @@ add_library(LinuxEmulation STATIC
Syscalls/FS.cpp
Syscalls/Info.cpp
Syscalls/IO.cpp
Syscalls/IOUring.cpp
Syscalls/Key.cpp
Syscalls/Memory.cpp
Syscalls/Msg.cpp
@@ -58,6 +59,7 @@ add_library(LinuxEmulation STATIC
target_link_libraries(LinuxEmulation FEXCore pthread numa)
target_include_directories(LinuxEmulation PRIVATE ${CMAKE_BINARY_DIR}/generated)
target_include_directories(LinuxEmulation PRIVATE ${PROJECT_SOURCE_DIR}/External/drm-headers/include/)
set(HEADERS_TO_VERIFY
x32/Types.h x86_32 # This needs to match structs to 32bit structs
@@ -546,38 +546,41 @@ namespace FEX::EmulatedFile {
}
for (int i = 0; i < CPUCores; ++i) {
cpu_stream << "processor : " << i << std::endl; // Logical id
cpu_stream << "vendor_id : " << vendorid.Str << std::endl;
cpu_stream << "cpu family : " << Family << std::endl;
cpu_stream << "model : " << (info.Model + (info.FamilyID >= 6 ? (info.ExModelID << 4) : 0)) << std::endl;
cpu_stream << "model name : " << modelname.Str << std::endl;
cpu_stream << "stepping : " << info.Stepping << std::endl;
cpu_stream << "microcode : 0x0" << std::endl;
cpu_stream << "cpu MHz : 3000" << std::endl;
cpu_stream << "cache size : 512 KB" << std::endl;
cpu_stream << "physical id : 0" << std::endl; // Socket id (always 0 for a single socket system)
cpu_stream << "siblings : " << CPUCores << std::endl; // Number of logical cores
cpu_stream << "core id : " << i << std::endl; // Physical id
cpu_stream << "cpu cores : " << CPUCores << std::endl; // Number of physical cores
cpu_stream << "apicid : " << i << std::endl;
cpu_stream << "initial apicid : " << i << std::endl;
cpu_stream << "fpu : " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
cpu_stream << "fpu_exception : " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
cpu_stream << "cpuid level : " << vendorid.id << std::endl;
cpu_stream << "wp : yes" << std::endl;
cpu_stream << "flags : " << flags_data.str() << std::endl;
cpu_stream << "processor\t: " << i << std::endl; // Logical id
cpu_stream << "vendor_id\t: " << vendorid.Str << std::endl;
cpu_stream << "cpu family\t: " << Family << std::endl;
cpu_stream << "model\t\t: " << (info.Model + (info.FamilyID >= 6 ? (info.ExModelID << 4) : 0)) << std::endl;
cpu_stream << "model name\t: " << modelname.Str << std::endl;
cpu_stream << "stepping\t: " << info.Stepping << std::endl;
cpu_stream << "microcode\t: 0x0" << std::endl;
cpu_stream << "cpu MHz\t\t: 3000" << std::endl;
cpu_stream << "cache size\t: 512 KB" << std::endl;
cpu_stream << "physical id\t: 0" << std::endl; // Socket id (always 0 for a single socket system)
cpu_stream << "siblings\t: " << CPUCores << std::endl; // Number of logical cores
cpu_stream << "core id\t\t: " << i << std::endl; // Physical id
cpu_stream << "cpu cores\t: " << CPUCores << std::endl; // Number of physical cores
cpu_stream << "apicid\t\t: " << i << std::endl;
cpu_stream << "initial apicid\t: " << i << std::endl;
cpu_stream << "fpu\t\t: " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
cpu_stream << "fpu_exception\t: " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
cpu_stream << "cpuid level\t: " << vendorid.id << std::endl;
cpu_stream << "wp\t\t: yes" << std::endl;
cpu_stream << "flags\t\t: " << flags_data.str() << std::endl;
// We don't have any bugs, don't question it
cpu_stream << "bugs : " << std::endl;
cpu_stream << "bogomips : 8000.0" << std::endl;
cpu_stream << "bugs\t\t: " << std::endl;
cpu_stream << "bogomips\t: 8000.0" << std::endl;
// These next four aren't necessarily correct
cpu_stream << "TLB size : 2560 4K pages" << std::endl;
cpu_stream << "clflush size : 64" << std::endl;
cpu_stream << "cache_alignment : 64" << std::endl;
cpu_stream << "TLB size\t: 2560 4K pages" << std::endl;
cpu_stream << "clflush size\t: 64" << std::endl;
cpu_stream << "cache_alignment\t : 64" << std::endl;
// Cortex-A is 40 or 44 bits physical, and 48/52 virtual
// Choose the lesser configuration
cpu_stream << "address sizes : 40 bits physical, 48 bits virtual" << std::endl;
cpu_stream << "address sizes\t: 40 bits physical, 48 bits virtual" << std::endl;
// No power management but required to report
cpu_stream << "power management: " << std::endl;
cpu_stream << std::endl;
}
@@ -597,8 +600,12 @@ namespace FEX::EmulatedFile {
FDReadCreators["/proc/sys/kernel/osrelease"] = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t {
FILE *fp = tmpfile();
const char kernel_version[] = "5.0.0\0";
fwrite(kernel_version, sizeof(uint8_t), strlen(kernel_version) + 1, fp);
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
fprintf(fp, "%d.%d.%d\n",
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
fputc('\0', fp);
fseek(fp, 0, SEEK_SET);
int32_t f = fileno(fp);
return f;
@@ -608,8 +615,13 @@ namespace FEX::EmulatedFile {
FILE *fp = tmpfile();
// UTS version NEEDS to be in a format that can pass to `date -d`
// Format of this is Linux version <Release> (<Compile By>@<Compile Host>) (<Linux Compiler>) #<version> {SMP, PREEMPT, PREEMPT_RT} <UTS version>\n"
const char kernel_version[] = "Linux version 5.0.0 (FEX@FEX) (clang) #" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__ "\n\0";
fwrite(kernel_version, sizeof(uint8_t), strlen(kernel_version) + 1, fp);
const char kernel_version[] = "Linux version %d.%d.%d (FEX@FEX) (clang) #" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__ "\n";
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
fprintf(fp, kernel_version,
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
fputc('\0', fp);
fseek(fp, 0, SEEK_SET);
int32_t f = fileno(fp);
return f;
@@ -642,6 +654,10 @@ namespace FEX::EmulatedFile {
// Finish off with a null terminator
fwrite("\0", sizeof(uint8_t), 1, fp);
}
// One additional null terminator to finish the list
fwrite("\0", sizeof(uint8_t), 1, fp);
fseek(fp, 0, SEEK_SET);
int32_t f = fileno(fp);
return f;
+73 -5
View File
@@ -6,6 +6,7 @@ $end_info$
*/
#include "Tests/LinuxSyscalls/FileManagement.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
#include <FEXCore/Utils/LogManager.h>
#include <cstring>
@@ -121,7 +122,7 @@ FileManager::FileManager(FEXCore::Context::Context *ctx)
FileManager::~FileManager() {
}
std::string FileManager::GetEmulatedPath(const char *pathname) {
std::string FileManager::GetEmulatedPath(const char *pathname, bool FollowSymlink) {
auto RootFSPath = LDPath();
if (!pathname ||
pathname[0] != '/' ||
@@ -134,7 +135,20 @@ std::string FileManager::GetEmulatedPath(const char *pathname) {
return thunkOverlay->second;
}
return RootFSPath + pathname;
std::string Path = RootFSPath + pathname;
if (FollowSymlink) {
std::error_code ec;
while(std::filesystem::is_symlink(Path, ec)) {
auto SymlinkTarget = std::filesystem::read_symlink(Path);
if (SymlinkTarget.is_absolute()) {
Path = RootFSPath + SymlinkTarget.string();
}
else {
break;
}
}
}
return Path;
}
@@ -148,7 +162,9 @@ std::optional<std::string> FileManager::GetSelf(const char *Pathname) {
char PidSelfPath[50];
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
if (strcmp(Pathname, "/proc/self/exe") == 0 || strcmp(Pathname, PidSelfPath) == 0) {
if (strcmp(Pathname, "/proc/self/exe") == 0 ||
strcmp(Pathname, "/proc/thread-self/exe") == 0 ||
strcmp(Pathname, PidSelfPath) == 0) {
return Filename();
}
@@ -169,6 +185,26 @@ uint64_t FileManager::Close(int fd) {
return ::close(fd);
}
uint64_t FileManager::CloseRange(unsigned int first, unsigned int last, unsigned int flags) {
#ifndef SYS_close_range
#define SYS_close_range 436
#endif
#ifndef CLOSE_RANGE_CLOEXEC
#define CLOSE_RANGE_CLOEXEC (1U << 2)
#endif
if (!(flags & CLOSE_RANGE_CLOEXEC)) {
// If the flag was set then it doesn't actually close the FDs
// Just sets the flag on a range
std::lock_guard<std::mutex> lk(FDLock);
for (unsigned int i = first; i <= last; ++i) {
// We remove from first to last inclusive
FDToNameMap.erase(i);
}
}
return ::syscall(SYS_close_range, first, last, flags);
}
uint64_t FileManager::Stat(const char *pathname, void *buf) {
auto NewPath = GetSelf(pathname);
const char *SelfPath = NewPath ? NewPath->c_str() : nullptr;
@@ -249,7 +285,9 @@ uint64_t FileManager::Readlink(const char *pathname, char *buf, size_t bufsiz) {
char PidSelfPath[50];
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
if (strcmp(pathname, "/proc/self/exe") == 0 || strcmp(pathname, PidSelfPath) == 0) {
if (strcmp(pathname, "/proc/self/exe") == 0 ||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
strcmp(pathname, PidSelfPath) == 0) {
auto App = Filename();
strncpy(buf, App.c_str(), bufsiz);
return std::min(bufsiz, App.size());
@@ -301,7 +339,7 @@ uint64_t FileManager::Openat([[maybe_unused]] int dirfs, const char *pathname, i
fd = EmuFD.OpenAt(dirfs, SelfPath, flags, mode);
if (fd == -1) {
auto Path = GetEmulatedPath(SelfPath);
auto Path = GetEmulatedPath(SelfPath, true);
if (!Path.empty()) {
fd = ::openat(dirfs, Path.c_str(), flags, mode);
}
@@ -318,6 +356,36 @@ uint64_t FileManager::Openat([[maybe_unused]] int dirfs, const char *pathname, i
return fd;
}
uint64_t FileManager::Openat2(int dirfs, const char *pathname, FEX::HLE::open_how *how, size_t usize) {
#ifndef SYS_openat2
#define SYS_openat2 437
#endif
auto NewPath = GetSelf(pathname);
const char *SelfPath = NewPath ? NewPath->c_str() : nullptr;
int32_t fd = -1;
fd = EmuFD.OpenAt(dirfs, SelfPath, how->flags, how->mode);
if (fd == -1) {
auto Path = GetEmulatedPath(SelfPath, true);
if (!Path.empty()) {
fd = ::syscall(SYS_openat2, dirfs, Path.c_str(), how, usize);
}
if (fd == -1)
fd = ::syscall(SYS_openat2, dirfs, SelfPath, how, usize);
}
if (fd != -1) {
std::lock_guard<std::mutex> lk(FDLock);
FDToNameMap[fd] = SelfPath;
}
return fd;
}
uint64_t FileManager::Statx(int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf) {
auto NewPath = GetSelf(pathname);
const char *SelfPath = NewPath ? NewPath->c_str() : nullptr;
+5 -1
View File
@@ -23,6 +23,8 @@ struct Context;
namespace FEX::HLE {
struct open_how;
class FileManager final {
public:
FileManager() = delete;
@@ -32,6 +34,7 @@ public:
~FileManager();
uint64_t Open(const char *pathname, int flags, uint32_t mode);
uint64_t Close(int fd);
uint64_t CloseRange(unsigned int first, unsigned int last, unsigned int flags);
uint64_t Stat(const char *pathname, void *buf);
uint64_t Lstat(const char *path, void *buf);
uint64_t Access(const char *pathname, int mode);
@@ -41,6 +44,7 @@ public:
uint64_t Chmod(const char *pathname, mode_t mode);
uint64_t Readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
uint64_t Openat(int dirfs, const char *pathname, int flags, uint32_t mode);
uint64_t Openat2(int dirfs, const char *pathname, FEX::HLE::open_how *how, size_t usize);
uint64_t Statx(int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf);
uint64_t Mknod(const char *pathname, mode_t mode, dev_t dev);
uint64_t NewFSStatAt(int dirfd, const char *pathname, struct stat *buf, int flag);
@@ -58,7 +62,7 @@ private:
std::mutex FDLock;
std::unordered_map<int32_t, std::string> FDToNameMap;
std::string GetEmulatedPath(const char *pathname);
std::string GetEmulatedPath(const char *pathname, bool FollowSymlink = false);
std::map<std::string, std::string> ThunkOverlays;
FEX_CONFIG_OPT(Filename, APP_FILENAME);
+15 -14
View File
@@ -17,6 +17,7 @@ $end_info$
#include <linux/futex.h>
#include <bits/types/stack_t.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
@@ -170,7 +171,7 @@ namespace FEX::HLE {
// Doesn't return
FEXCore::Context::StopThread(Thread->CTX, Thread);
std::unexpected();
std::terminate();
}
}
else if (Handler.GuestAction.sigaction_handler.handler == SIG_IGN) {
@@ -300,7 +301,7 @@ namespace FEX::HLE {
// Most signals default to termination
// These ones are slightly different
const std::vector<std::pair<int, SignalDelegator::DefaultBehaviour>> SignalDefaultBehaviours = {
static constexpr std::array<std::pair<int, SignalDelegator::DefaultBehaviour>, 14> SignalDefaultBehaviours = {{
{SIGQUIT, DEFAULT_COREDUMP},
{SIGILL, DEFAULT_COREDUMP},
{SIGTRAP, DEFAULT_COREDUMP},
@@ -315,10 +316,10 @@ namespace FEX::HLE {
{SIGXFSZ, DEFAULT_COREDUMP},
{SIGSYS, DEFAULT_COREDUMP},
{SIGWINCH, DEFAULT_IGNORE},
};
}};
for (auto Behaviour : SignalDefaultBehaviours) {
HostHandlers[Behaviour.first].DefaultBehaviour = Behaviour.second;
for (const auto [Signal, Behaviour] : SignalDefaultBehaviours) {
HostHandlers[Signal].DefaultBehaviour = Behaviour;
}
}
@@ -342,7 +343,7 @@ namespace FEX::HLE {
// Set up our signal alternative stack
// This is per thread rather than per signal
ThreadData.AltStackPtr = FEXCore::Allocator::malloc(SIGSTKSZ);
ThreadData.AltStackPtr = FEXCore::Allocator::mmap(nullptr, SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
stack_t altstack{};
altstack.ss_sp = ThreadData.AltStackPtr;
altstack.ss_size = SIGSTKSZ;
@@ -357,7 +358,7 @@ namespace FEX::HLE {
}
void SignalDelegator::UninstallTLSState(FEXCore::Core::InternalThreadState *Thread) {
free(ThreadData.AltStackPtr);
FEXCore::Allocator::munmap(ThreadData.AltStackPtr, SIGSTKSZ);
ThreadData.Thread = nullptr;
ThreadData.AltStackPtr = nullptr;
@@ -420,27 +421,27 @@ namespace FEX::HLE {
void SignalDelegator::RegisterHostSignalHandler(int Signal, FEXCore::HostSignalDelegatorFunction Func) {
// Linux signal handlers are per-process rather than per thread
// Multiple threads could be calling in to this
std::lock_guard<std::mutex> lk(HostDelegatorMutex);
HostHandlers[Signal].Handler = Func;
std::lock_guard lk(HostDelegatorMutex);
HostHandlers[Signal].Handler = std::move(Func);
InstallHostThunk(Signal);
}
void SignalDelegator::RegisterFrontendHostSignalHandler(int Signal, FEXCore::HostSignalDelegatorFunction Func) {
// Linux signal handlers are per-process rather than per thread
// Multiple threads could be calling in to this
std::lock_guard<std::mutex> lk(HostDelegatorMutex);
HostHandlers[Signal].FrontendHandler = Func;
std::lock_guard lk(HostDelegatorMutex);
HostHandlers[Signal].FrontendHandler = std::move(Func);
InstallHostThunk(Signal);
}
void SignalDelegator::RegisterHostSignalHandlerForGuest(int Signal, FEXCore::HostSignalDelegatorFunctionForGuest Func) {
std::lock_guard<std::mutex> lk(HostDelegatorMutex);
HostHandlers[Signal].GuestHandler = Func;
std::lock_guard lk(HostDelegatorMutex);
HostHandlers[Signal].GuestHandler = std::move(Func);
InstallHostThunk(Signal);
}
uint64_t SignalDelegator::RegisterGuestSignalHandler(int Signal, const FEXCore::GuestSigAction *Action, FEXCore::GuestSigAction *OldAction) {
std::lock_guard<std::mutex> lk(GuestDelegatorMutex);
std::lock_guard lk(GuestDelegatorMutex);
// Invalid signal specified
if (Signal > MAX_SIGNALS) {
+3 -2
View File
@@ -7,6 +7,7 @@ $end_info$
#pragma once
#include <array>
#include <atomic>
#include <functional>
#include <mutex>
@@ -29,7 +30,7 @@ namespace FEX::HLE {
// Returns true if the host handled the signal
// Arguments are the same as sigaction handler
SignalDelegator();
virtual ~SignalDelegator();
~SignalDelegator() override;
/**
* @brief Registers an emulated thread's object to a TLS object
@@ -108,7 +109,7 @@ namespace FEX::HLE {
DefaultBehaviour DefaultBehaviour {DEFAULT_TERM};
};
SignalHandler HostHandlers[MAX_SIGNALS + 1]{};
std::array<SignalHandler, MAX_SIGNALS + 1> HostHandlers{};
bool InstallHostThunk(int Signal);
void UpdateHostThunk(int Signal);
+155 -5
View File
@@ -10,11 +10,13 @@ $end_info$
#include "Common/MathUtils.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
#include "Tests/LinuxSyscalls/Syscalls/Thread.h"
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Core/CodeLoader.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/ELFContainer.h>
#include <fcntl.h>
@@ -95,7 +97,7 @@ static bool IsSupportedByInterpreter(std::string const &Filename) {
return false;
}
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp) {
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp, ExecveAtArgs *Args) {
std::string Filename{};
std::error_code ec;
@@ -123,7 +125,9 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
char PidSelfPath[50];
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
if (strcmp(pathname, "/proc/self/exe") == 0 || strcmp(pathname, PidSelfPath) == 0) {
if (strcmp(pathname, "/proc/self/exe") == 0 ||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
strcmp(pathname, PidSelfPath) == 0) {
// If pointing to self then redirect to the application
// JRE and shapez.io does this
Filename = FEX::HLE::_SyscallHandler->Filename();
@@ -132,7 +136,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
uint64_t Result{};
if (FEX::HLE::_SyscallHandler->IsInterpreterInstalled()) {
// If the FEX interpreter is installed then just execve the thing
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
if (Args) {
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
}
else {
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
}
SYSCALL_ERRNO();
}
@@ -151,7 +160,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
// We are trying to execute an ELF of a different architecture
// We can't know if we can support this without architecture specific checks and binfmt_misc parsing
// Just execve it and let the kernel handle the process
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
if (Args) {
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
}
else {
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
}
SYSCALL_ERRNO();
}
@@ -170,11 +184,136 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
// Append the arguments together
ExecveArgs.insert(ExecveArgs.end(), argv.begin(), argv.end());
Result = execve("/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)));
if (Args) {
Result = ::syscall(SYS_execveat, Args->dirfd, "/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
}
else {
Result = execve("/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)));
}
SYSCALL_ERRNO();
}
static bool AnyFlagsSet(uint64_t Flags, uint64_t Mask) {
return (Flags & Mask) != 0;
}
static bool AllFlagsSet(uint64_t Flags, uint64_t Mask) {
return (Flags & Mask) == Mask;
}
uint64_t CloneHandler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) {
uint64_t flags = args->flags;
#define FLAGPRINT(x, y) if (args->flags & (y)) LogMan::Msg::I("\tFlag: " #x)
FLAGPRINT(CSIGNAL, 0x000000FF);
FLAGPRINT(CLONE_VM, 0x00000100);
FLAGPRINT(CLONE_FS, 0x00000200);
FLAGPRINT(CLONE_FILES, 0x00000400);
FLAGPRINT(CLONE_SIGHAND, 0x00000800);
FLAGPRINT(CLONE_PTRACE, 0x00002000);
FLAGPRINT(CLONE_VFORK, 0x00004000);
FLAGPRINT(CLONE_PARENT, 0x00008000);
FLAGPRINT(CLONE_THREAD, 0x00010000);
FLAGPRINT(CLONE_NEWNS, 0x00020000);
FLAGPRINT(CLONE_SYSVSEM, 0x00040000);
FLAGPRINT(CLONE_SETTLS, 0x00080000);
FLAGPRINT(CLONE_PARENT_SETTID, 0x00100000);
FLAGPRINT(CLONE_CHILD_CLEARTID, 0x00200000);
FLAGPRINT(CLONE_DETACHED, 0x00400000);
FLAGPRINT(CLONE_UNTRACED, 0x00800000);
FLAGPRINT(CLONE_CHILD_SETTID, 0x01000000);
FLAGPRINT(CLONE_NEWCGROUP, 0x02000000);
FLAGPRINT(CLONE_NEWUTS, 0x04000000);
FLAGPRINT(CLONE_NEWIPC, 0x08000000);
FLAGPRINT(CLONE_NEWUSER, 0x10000000);
FLAGPRINT(CLONE_NEWPID, 0x20000000);
FLAGPRINT(CLONE_NEWNET, 0x40000000);
FLAGPRINT(CLONE_IO, 0x80000000);
auto Thread = Frame->Thread;
if (AnyFlagsSet(flags, CLONE_UNTRACED | CLONE_PTRACE)) {
LogMan::Msg::D("clone: Ptrace* not supported");
}
// Clone3 flags
#ifndef CLONE_CLEAR_SIGHAND
#define CLONE_CLEAR_SIGHAND 0x100000000ULL
#endif
#ifndef CLONE_INTO_CGROUP
#define CLONE_INTO_CGROUP 0x200000000ULL
#endif
if (AnyFlagsSet(flags, CLONE_CLEAR_SIGHAND)) {
LogMan::Msg::D("clone3: CLONE_CLEAR_SIGHAND unsupported");
}
if (AnyFlagsSet(flags, CLONE_INTO_CGROUP)) {
LogMan::Msg::D("clone3: CLONE_INTO_CGROUP unsupported");
return -EOPNOTSUPP;
}
if (args->set_tid_size > 0) {
LogMan::Msg::D("clone3: set_tid unsupported");
return -EPERM;
}
if (AnyFlagsSet(flags, CLONE_NEWNS | CLONE_NEWCGROUP | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)) {
// NEWUSER doesn't need any privileges from 3.8 onward
// We just don't support it yet
LogMan::Msg::I("Unconditionally returning EPERM on clone namespace");
return -EPERM;
}
if (!(flags & CLONE_THREAD)) {
if (flags & CLONE_VFORK) {
flags &= ~CLONE_VFORK;
flags &= ~CLONE_VM;
LogMan::Msg::D("clone: WARNING: CLONE_VFORK w/o CLONE_THREAD");
}
if (AnyFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM)) {
LogMan::Msg::I("clone: Unsuported flags w/o CLONE_THREAD (Shared Resources), %X", flags);
return -EPERM;
}
// CLONE_PARENT is ignored (Implied by CLONE_THREAD)
return FEX::HLE::ForkGuest(Thread, Frame, flags,
reinterpret_cast<void*>(args->stack),
reinterpret_cast<pid_t*>(args->parent_tid),
reinterpret_cast<pid_t*>(args->child_tid),
reinterpret_cast<void*>(args->tls));
} else {
if (!AllFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) {
LogMan::Msg::I("clone: CLONE_THREAD: Unsuported flags w/ CLONE_THREAD (Shared Resources), %X", flags);
return -EPERM;
}
auto NewThread = FEX::HLE::CreateNewThread(Thread->CTX, Frame, args);
// Return the new threads TID
uint64_t Result = NewThread->ThreadManager.GetTID();
if (flags & CLONE_VFORK) {
NewThread->DestroyedByParent = true;
}
// Actually start the thread
FEXCore::Context::RunThread(Thread->CTX, NewThread);
if (flags & CLONE_VFORK) {
// If VFORK is set then the calling process is suspended until the thread exits with execve or exit
NewThread->ExecutionThread->join(nullptr);
// Normally a thread cleans itself up on exit. But because we need to join, we are now responsible
FEXCore::Context::DestroyThread(Thread->CTX, NewThread);
}
SYSCALL_ERRNO();
}
};
uint64_t SyscallHandler::HandleBRK(FEXCore::Core::CpuStateFrame *Frame, void *Addr) {
std::lock_guard<std::mutex> lk(MMapMutex);
@@ -251,6 +390,8 @@ SyscallHandler::SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalD
, SignalDelegation {_SignalDelegation} {
FEX::HLE::_SyscallHandler = this;
HostKernelVersion = CalculateHostKernelVersion();
GuestKernelVersion = CalculateGuestKernelVersion();
}
SyscallHandler::~SyscallHandler() {
@@ -276,7 +417,16 @@ uint32_t SyscallHandler::CalculateHostKernelVersion() {
return (Major << 24) | (Minor << 16) | Patch;
}
uint32_t SyscallHandler::CalculateGuestKernelVersion() {
// We currently only emulate a kernel between the ranges of Kernel 5.0.0 and 5.12.0
return std::max(KernelVersion(5, 0), std::min(KernelVersion(5, 12), GetHostKernelVersion()));
}
uint64_t SyscallHandler::HandleSyscall(FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args) {
if (Args->Argument[0] >= Definitions.size()) {
return -ENOSYS;
}
auto &Def = Definitions[Args->Argument[0]];
uint64_t Result{};
switch (Def.NumArgs) {
+93 -24
View File
@@ -13,6 +13,7 @@ $end_info$
#include <FEXCore/Config/Config.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <atomic>
#include <condition_variable>
@@ -20,6 +21,7 @@ $end_info$
#include <unordered_map>
#include <sys/epoll.h>
#include <fcntl.h>
// #define DEBUG_STRACE
@@ -34,6 +36,7 @@ class SyscallHandler;
void RegisterFS();
void RegisterInfo();
void RegisterIO();
void RegisterIOUring(FEX::HLE::SyscallHandler *const Handler);
void RegisterKey();
void RegisterMemory();
void RegisterMsg();
@@ -41,7 +44,7 @@ class SyscallHandler;
void RegisterSched();
void RegisterSemaphore();
void RegisterSHM();
void RegisterSignals();
void RegisterSignals(FEX::HLE::SyscallHandler *const Handler);
void RegisterSocket();
void RegisterThread();
void RegisterTime();
@@ -52,7 +55,12 @@ class SyscallHandler;
uint64_t UnimplementedSyscall(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber);
uint64_t UnimplementedSyscallSafe(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber);
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp);
struct ExecveAtArgs {
int dirfd;
int flags;
};
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp, ExecveAtArgs *Args);
class SyscallHandler : public FEXCore::HLE::SyscallHandler {
public:
@@ -113,12 +121,19 @@ public:
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
uint32_t GetHostKernelVersion() const { return HostKernelVersion; }
uint32_t GetGuestKernelVersion() const { return GuestKernelVersion; }
static uint32_t CalculateHostKernelVersion();
uint32_t CalculateGuestKernelVersion();
static uint32_t KernelVersion(uint32_t Major, uint32_t Minor = 0, uint32_t Patch = 0) {
return (Major << 24) | (Minor << 16) | Patch;
}
static uint32_t KernelMajor(uint32_t Version) { return Version >> 24; }
static uint32_t KernelMinor(uint32_t Version) { return (Version >> 16) & 0xFF; }
static uint32_t KernelPatch(uint32_t Version) { return Version & 0xFFFF; }
protected:
std::vector<SyscallFunctionDefinition> Definitions{};
std::mutex MMapMutex;
@@ -131,6 +146,7 @@ protected:
// (Major << 24) | (Minor << 16) | Patch
uint32_t HostKernelVersion{};
uint32_t GuestKernelVersion{};
private:
@@ -201,12 +217,6 @@ std::string CollectArgsFmtString() {
#define ARG_TO_STR(tpy, str)
#endif
/////
// REGISTER_SYSCALL_FORWARD_ERRNO implementation
// Given a syscall wrapper, it generate a syscall implementation using the wrapper's signature, forward the arguments
// and register to syscalls via RegisterSyscall
/////
// Helper that allows us to create a variadic template lambda from a given signature
// by creating a function that expects a fuction pointer with the given signature as a parameter
template <typename T>
@@ -231,7 +241,7 @@ struct FunctionToLambda<R(*)(Args...) noexcept> {
}
};
struct __attribute__((packed)) epoll_event_x86 {
struct FEX_PACKED epoll_event_x86 {
uint32_t events;
epoll_data_t data;
@@ -251,23 +261,82 @@ struct __attribute__((packed)) epoll_event_x86 {
};
static_assert(std::is_trivial<epoll_event_x86>::value, "Needs to be trivial");
static_assert(sizeof(epoll_event_x86) == 12, "Incorrect size");
struct open_how {
uint64_t flags;
uint64_t mode;
uint64_t resolve;
};
struct clone3_args {
uint64_t flags;
uint64_t pidfd;
uint64_t child_tid;
uint64_t parent_tid;
uint64_t exit_signal;
uint64_t stack;
uint64_t stack_size;
uint64_t tls;
uint64_t set_tid;
uint64_t set_tid_size;
uint64_t cgroup;
};
uint64_t CloneHandler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args);
inline static int RemapFromX86Flags(int flags) {
#ifdef _M_X86_64
// Nothing to change here
#elif _M_ARM_64
constexpr int X86_64_FLAG_O_DIRECT = 040000;
constexpr int X86_64_FLAG_O_LARGEFILE = 0100000;
constexpr int X86_64_FLAG_O_DIRECTORY = 0200000;
constexpr int X86_64_FLAG_O_NOFOLLOW = 0400000;
constexpr int AARCH64_FLAG_O_DIRECTORY = 040000;
constexpr int AARCH64_FLAG_O_NOFOLLOW = 0100000;
constexpr int AARCH64_FLAG_O_DIRECT = 0200000;
constexpr int AARCH64_FLAG_O_LARGEFILE = 0400000;
int new_flags{};
if (flags & X86_64_FLAG_O_DIRECT) { flags = (flags & ~X86_64_FLAG_O_DIRECT); new_flags |= AARCH64_FLAG_O_DIRECT; }
if (flags & X86_64_FLAG_O_LARGEFILE) { flags = (flags & ~X86_64_FLAG_O_LARGEFILE); new_flags |= AARCH64_FLAG_O_LARGEFILE; }
if (flags & X86_64_FLAG_O_DIRECTORY) { flags = (flags & ~X86_64_FLAG_O_DIRECTORY); new_flags |= AARCH64_FLAG_O_DIRECTORY; }
if (flags & X86_64_FLAG_O_NOFOLLOW) { flags = (flags & ~X86_64_FLAG_O_NOFOLLOW); new_flags |= AARCH64_FLAG_O_NOFOLLOW; }
flags |= new_flags;
#else
#error Unknown flag remappings for this host platform
#endif
return flags;
}
inline static int RemapToX86Flags(int flags) {
#ifdef _M_X86_64
// Nothing to change here
#elif _M_ARM_64
constexpr int X86_64_FLAG_O_DIRECT = 040000;
constexpr int X86_64_FLAG_O_LARGEFILE = 0100000;
constexpr int X86_64_FLAG_O_DIRECTORY = 0200000;
constexpr int X86_64_FLAG_O_NOFOLLOW = 0400000;
constexpr int AARCH64_FLAG_O_DIRECTORY = 040000;
constexpr int AARCH64_FLAG_O_NOFOLLOW = 0100000;
constexpr int AARCH64_FLAG_O_DIRECT = 0200000;
constexpr int AARCH64_FLAG_O_LARGEFILE = 0400000;
int new_flags{};
if (flags & AARCH64_FLAG_O_DIRECT) { flags = (flags & ~AARCH64_FLAG_O_DIRECT); new_flags |= X86_64_FLAG_O_DIRECT; }
if (flags & AARCH64_FLAG_O_LARGEFILE) { flags = (flags & ~AARCH64_FLAG_O_LARGEFILE); new_flags |= X86_64_FLAG_O_LARGEFILE; }
if (flags & AARCH64_FLAG_O_DIRECTORY) { flags = (flags & ~AARCH64_FLAG_O_DIRECTORY); new_flags |= X86_64_FLAG_O_DIRECTORY; }
if (flags & AARCH64_FLAG_O_NOFOLLOW) { flags = (flags & ~AARCH64_FLAG_O_NOFOLLOW); new_flags |= X86_64_FLAG_O_NOFOLLOW; }
flags |= new_flags;
#else
#error Unknown flag remappings for this host platform
#endif
return flags;
}
}
// Creates a variadic template lambda from a global function (via FunctionToLambda), then forwards the arguments to the specified function
// also handles errno
#define SYSCALL_FORWARD_ERRNO(function) \
FEX::HLE::FunctionToLambda<decltype(&::function)>::ReturnFunctionPointer([](FEXCore::Core::CpuStateFrame *Frame, auto... Args) { \
FEX::HLE::FunctionToLambda<decltype(&::function)>::RType Result = ::function(Args...); \
do { if (Result == -1) return (FEX::HLE::FunctionToLambda<decltype(&::function)>::RType)-errno; return Result; } while(0); \
})
// Helpers to register a syscall implementation
// Creates a syscall forward from a glibc wrapper, and registers it
#define REGISTER_SYSCALL_FORWARD_ERRNO(function) do { \
FEX::HLE::x64::RegisterSyscall(FEX::HLE::x64::SYSCALL_x64_##function, #function, SYSCALL_FORWARD_ERRNO(function)); \
FEX::HLE::x32::RegisterSyscall(FEX::HLE::x32::SYSCALL_x86_##function, #function, SYSCALL_FORWARD_ERRNO(function)); \
} while(0)
// Registers syscall for both 32bit and 64bit
#define REGISTER_SYSCALL_IMPL(name, lambda) \
struct impl_##name { \
+70 -38
View File
@@ -24,24 +24,6 @@ $end_info$
#include <sys/syscall.h>
namespace FEX::HLE {
static int RemapFlags(int flags) {
#ifdef _M_X86_64
// Nothing to change here
#elif _M_ARM_64
constexpr int X86_64_FLAG_O_DIRECT = 040000;
constexpr int X86_64_FLAG_O_LARGEFILE = 0100000;
constexpr int X86_64_FLAG_O_DIRECTORY = 0200000;
constexpr int X86_64_FLAG_O_NOFOLLOW = 0400000;
if (flags & X86_64_FLAG_O_DIRECT) flags = (flags & ~X86_64_FLAG_O_DIRECT) | O_DIRECT;
if (flags & X86_64_FLAG_O_LARGEFILE) flags = (flags & ~X86_64_FLAG_O_LARGEFILE) | O_LARGEFILE;
if (flags & X86_64_FLAG_O_DIRECTORY) flags = (flags & ~X86_64_FLAG_O_DIRECTORY) | O_DIRECTORY;
if (flags & X86_64_FLAG_O_NOFOLLOW) flags = (flags & ~X86_64_FLAG_O_NOFOLLOW) | O_NOFOLLOW;
#else
#error Unknown flag remappings for this host platform
#endif
return flags;
}
void RegisterFD(FEX::HLE::SyscallHandler *const Handler) {
REGISTER_SYSCALL_IMPL(read, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, size_t count) -> uint64_t {
uint64_t Result = ::read(fd, buf, count);
@@ -54,7 +36,7 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(open, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, int flags, uint32_t mode) -> uint64_t {
flags = RemapFlags(flags);
flags = FEX::HLE::RemapFromX86Flags(flags);
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Open(pathname, flags, mode);
SYSCALL_ERRNO();
});
@@ -64,23 +46,20 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(chown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
SYSCALL_STUB(chown);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(chown);
uint64_t Result = ::chown(pathname, owner, group);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(fchown, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uid_t owner, gid_t group) -> uint64_t {
SYSCALL_STUB(fchown);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(fchown);
uint64_t Result = ::fchown(fd, owner, group);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(lchown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
SYSCALL_STUB(lchown);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(lchown);
uint64_t Result = ::lchown(pathname, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(lseek, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uint64_t offset, int whence) -> uint64_t {
uint64_t Result = ::lseek(fd, offset, whence);
@@ -108,7 +87,7 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(dup3, [](FEXCore::Core::CpuStateFrame* Frame, int oldfd, int newfd, int flags) -> uint64_t {
flags = RemapFlags(flags);
flags = FEX::HLE::RemapFromX86Flags(flags);
uint64_t Result = ::dup3(oldfd, newfd, flags);
SYSCALL_ERRNO();
});
@@ -149,7 +128,7 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(fadvise64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, off_t offset, off_t len, int advice) -> uint64_t {
uint64_t Result = ::posix_fadvise64(fd, offset, len, advice);
uint64_t Result = ::syscall(SYS_fadvise64, fd, offset, len, advice);
SYSCALL_ERRNO();
});
@@ -169,7 +148,7 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(openat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfs, const char *pathname, int flags, uint32_t mode) -> uint64_t {
flags = RemapFlags(flags);
flags = FEX::HLE::RemapFromX86Flags(flags);
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat(dirfs, pathname, flags, mode);
SYSCALL_ERRNO();
});
@@ -185,11 +164,13 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(fchownat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::fchownat(dirfd, pathname, owner, group, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(unlinkat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::unlinkat(dirfd, pathname, flags);
SYSCALL_ERRNO();
});
@@ -200,6 +181,7 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(linkat, [](FEXCore::Core::CpuStateFrame *Frame, int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::linkat(olddirfd, oldpath, newdirfd, newpath, flags);
SYSCALL_ERRNO();
});
@@ -230,32 +212,57 @@ namespace FEX::HLE {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.FAccessat2(dirfd, pathname, mode, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(pidfd_getfd, [](FEXCore::Core::CpuStateFrame *Frame, int pidfd, int fd, unsigned int flags) -> uint64_t {
#ifndef SYS_pidfd_getfd
#define SYS_pidfd_getfd 438
#endif
uint64_t Result = ::syscall(SYS_pidfd_getfd, pidfd, fd, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(openat2, [](FEXCore::Core::CpuStateFrame *Frame, int dirfs, const char *pathname, struct open_how *how, size_t usize) -> uint64_t {
open_how HostHow{};
size_t HostSize = std::min(sizeof(open_how), usize);
memcpy(&HostHow, how, HostSize);
HostHow.flags = FEX::HLE::RemapFromX86Flags(HostHow.flags);
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat2(dirfs, pathname, &HostHow, HostSize);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(faccessat2, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(pidfd_getfd, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(openat2, UnimplementedSyscallSafe);
}
REGISTER_SYSCALL_IMPL(splice, [](FEXCore::Core::CpuStateFrame *Frame, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::splice(fd_in, off_in, fd_out, off_out, len, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(tee, [](FEXCore::Core::CpuStateFrame *Frame, int fd_in, int fd_out, size_t len, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::tee(fd_in, fd_out, len, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(sync_file_range, [](FEXCore::Core::CpuStateFrame *Frame, int fd, off64_t offset, off64_t nbytes, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::sync_file_range(fd, offset, nbytes, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(timerfd_create, [](FEXCore::Core::CpuStateFrame *Frame, int32_t clockid, int32_t flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::timerfd_create(clockid, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(timerfd_settime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::timerfd_settime(fd, flags, new_value, old_value);
SYSCALL_ERRNO();
});
@@ -271,52 +278,77 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(pipe2, [](FEXCore::Core::CpuStateFrame *Frame, int pipefd[2], int flags) -> uint64_t {
flags = RemapFlags(flags);
flags = FEX::HLE::RemapFromX86Flags(flags);
uint64_t Result = ::pipe2(pipefd, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(inotify_init1, [](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
flags = RemapFlags(flags);
// Flags don't need remapped
uint64_t Result = ::inotify_init1(flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(renameat2, [](FEXCore::Core::CpuStateFrame *Frame, int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::renameat2(olddirfd, oldpath, newdirfd, newpath, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(memfd_create, [](FEXCore::Core::CpuStateFrame *Frame, const char *name, uint32_t flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::memfd_create(name, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(statx, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf) -> uint64_t {
// Flags don't need remapped
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statx(dirfd, pathname, flags, mask, statxbuf);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(name_to_handle_at, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags) -> uint64_t {
flags = RemapFlags(flags);
// Flags don't need remapped
uint64_t Result = ::name_to_handle_at(dirfd, pathname, handle, mount_id, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(open_by_handle_at, [](FEXCore::Core::CpuStateFrame *Frame, int mount_fd, struct file_handle *handle, int flags) -> uint64_t {
flags = RemapFlags(flags);
// Flags don't need remapped
uint64_t Result = ::open_by_handle_at(mount_fd, handle, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(eventfd2, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int count, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::syscall(SYS_eventfd2, count, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(copy_file_range, [](FEXCore::Core::CpuStateFrame *Frame, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
SYSCALL_ERRNO();
});
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 3, 0)) {
REGISTER_SYSCALL_IMPL(pidfd_open, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYS_pidfd_open, pid, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(pidfd_open, UnimplementedSyscallSafe);
}
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 9, 0)) {
REGISTER_SYSCALL_IMPL(close_range, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int first, unsigned int last, unsigned int flags) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.CloseRange(first, last, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(close_range, UnimplementedSyscallSafe);
}
}
}
+39 -52
View File
@@ -166,83 +166,70 @@ namespace FEX::HLE {
});
/*
REGISTER_SYSCALL_IMPL(syncfs, [](FEXCore::Core::CpuStateFrame *Frame, int fd) -> uint64_t {
SYSCALL_STUB(syncfs);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(syncfs);
uint64_t Result = ::syncfs(fd);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(setxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, const void *value, size_t size, int flags) -> uint64_t {
SYSCALL_STUB(setxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(setxattr);
uint64_t Result = ::setxattr(path, name, value, size, flags);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(lsetxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, const void *value, size_t size, int flags) -> uint64_t {
SYSCALL_STUB(lsetxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(lsetxattr);
uint64_t Result = ::lsetxattr(path, name, value, size, flags);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(fsetxattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name, const void *value, size_t size, int flags) -> uint64_t {
SYSCALL_STUB(fsetxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(fsetxattr);
uint64_t Result = ::fsetxattr(fd, name, value, size, flags);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(getxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, void *value, size_t size) -> uint64_t {
SYSCALL_STUB(getxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(getxattr);
uint64_t Result = ::getxattr(path, name, value, size);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(lgetxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, void *value, size_t size) -> uint64_t {
SYSCALL_STUB(lgetxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(lgetxattr);
uint64_t Result = ::lgetxattr(path, name, value, size);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(fgetxattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name, void *value, size_t size) -> uint64_t {
SYSCALL_STUB(fgetxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(fgetxattr);
uint64_t Result = ::fgetxattr(fd, name, value, size);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(listxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, char *list, size_t size) -> uint64_t {
SYSCALL_STUB(listxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(listxattr);
uint64_t Result = ::listxattr(path, list, size);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(llistxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, char *list, size_t size) -> uint64_t {
SYSCALL_STUB(llistxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(llistxattr);
uint64_t Result = ::llistxattr(path, list, size);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(flistxattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, char *list, size_t size) -> uint64_t {
SYSCALL_STUB(flistxattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(flistxattr);
uint64_t Result = ::flistxattr(fd, list, size);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(removexattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name) -> uint64_t {
SYSCALL_STUB(removexattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(removexattr);
uint64_t Result = ::removexattr(path, name);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(lremovexattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name) -> uint64_t {
SYSCALL_STUB(lremovexattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(lremovexattr);
uint64_t Result = ::lremovexattr(path, name);
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(fremovexattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name) -> uint64_t {
SYSCALL_STUB(fremovexattr);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(fremovexattr);
uint64_t Result = ::fremovexattr(fd, name);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(fanotify_init, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int flags, unsigned int event_f_flags) -> uint64_t {
uint64_t Result = ::fanotify_init(flags, event_f_flags);
@@ -0,0 +1,45 @@
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "Tests/LinuxSyscalls/Syscalls.h"
#include "Tests/LinuxSyscalls/Syscalls/Thread.h"
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
#include <signal.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace SignalDelegator {
struct GuestSigAction;
}
namespace FEX::HLE {
void RegisterIOUring(FEX::HLE::SyscallHandler *const Handler) {
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 1, 0)) {
REGISTER_SYSCALL_IMPL(io_uring_setup, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t entries, void* params) -> uint64_t {
uint64_t Result = ::syscall(SYS_io_uring_setup, entries, params);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(io_uring_enter, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, uint32_t to_submit, uint32_t min_complete, uint32_t flags, void *argp, size_t argsz) -> uint64_t {
uint64_t Result = ::syscall(SYS_io_uring_enter, fd, to_submit, min_complete, flags, argp, argsz);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(io_uring_register, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, unsigned int opcode, void *arg, uint32_t nr_args) -> uint64_t {
uint64_t Result = ::syscall(SYS_io_uring_register, fd, opcode, arg, nr_args);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(io_uring_setup, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(io_uring_enter, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(io_uring_register, UnimplementedSyscallSafe);
}
}
}
+8 -1
View File
@@ -34,13 +34,20 @@ namespace FEX::HLE {
if (::uname(&Local) == 0) {
memcpy(buf->nodename, Local.nodename, sizeof(Local.nodename));
static_assert(sizeof(Local.nodename) <= sizeof(buf->nodename));
memcpy(buf->domainname, Local.domainname, sizeof(Local.domainname));
static_assert(sizeof(Local.domainname) <= sizeof(buf->domainname));
}
else {
strcpy(buf->nodename, "FEXCore");
LogMan::Msg::E("Couldn't determine host nodename. Defaulting to '%s'", buf->nodename);
}
strcpy(buf->sysname, "Linux");
strcpy(buf->release, "5.0.0");
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
snprintf(buf->release, sizeof(buf->release), "%d.%d.%d",
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
const char version[] = "#" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__;
strcpy(buf->version, version);
static_assert(sizeof(version) <= sizeof(buf->version), "uname version define became too large!");
@@ -21,7 +21,7 @@ namespace SignalDelegator {
}
namespace FEX::HLE {
void RegisterSignals() {
void RegisterSignals(FEX::HLE::SyscallHandler *const Handler) {
REGISTER_SYSCALL_IMPL(rt_sigprocmask, [](FEXCore::Core::CpuStateFrame *Frame, int how, const uint64_t *set, uint64_t *oldset) -> uint64_t {
return FEX::HLE::_SyscallHandler->GetSignalDelegator()->GuestSigProcMask(how, set, oldset);
});
@@ -35,8 +35,21 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(userfaultfd, [](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
// Disable userfaultfd until we can properly emulate it
// This is okay because the kernel configuration allows you to disable it at compile time
return -ENOSYS;
uint64_t Result = ::syscall(SYS_userfaultfd, flags);
SYSCALL_ERRNO();
});
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 1, 0)) {
REGISTER_SYSCALL_IMPL(pidfd_send_signal, [](FEXCore::Core::CpuStateFrame *Frame, int pidfd, int sig, siginfo_t *info, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYS_pidfd_send_signal);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(pidfd_send_signal, UnimplementedSyscallSafe);
}
}
}
@@ -64,11 +64,6 @@ namespace FEX::HLE {
SYSCALL_STUB(rt_tgsigqueueinfo);
});
// execute program relative to a directory file descriptor
REGISTER_SYSCALL_IMPL(execveat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags) -> uint64_t {
SYSCALL_STUB(execveat);
});
REGISTER_SYSCALL_IMPL(rseq, [](FEXCore::Core::CpuStateFrame *Frame, struct rseq *rseq, uint32_t rseq_len, int flags, uint32_t sig) -> uint64_t {
SYSCALL_STUB(rseq);
});
+57 -30
View File
@@ -33,7 +33,8 @@ $end_info$
ARG_TO_STR(idtype_t, "%u")
namespace FEX::HLE {
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context:: Context *CTX, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls) {
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context:: Context *CTX, FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) {
uint64_t flags = args->flags;
FEXCore::Core::CPUState NewThreadState{};
// Clone copies the parent thread's state
memcpy(&NewThreadState, Frame, sizeof(FEXCore::Core::CPUState));
@@ -41,21 +42,21 @@ namespace FEX::HLE {
NewThreadState.gregs[FEXCore::X86State::REG_RAX] = 0;
NewThreadState.gregs[FEXCore::X86State::REG_RBX] = 0;
NewThreadState.gregs[FEXCore::X86State::REG_RBP] = 0;
NewThreadState.gregs[FEXCore::X86State::REG_RSP] = reinterpret_cast<uint64_t>(stack);
NewThreadState.gregs[FEXCore::X86State::REG_RSP] = args->stack;
auto NewThread = FEXCore::Context::CreateThread(CTX, &NewThreadState, reinterpret_cast<uint64_t>(parent_tid));
auto NewThread = FEXCore::Context::CreateThread(CTX, &NewThreadState, args->parent_tid);
FEXCore::Context::InitializeThread(CTX, NewThread);
if (FEX::HLE::_SyscallHandler->Is64BitMode()) {
if (flags & CLONE_SETTLS) {
x64::SetThreadArea(NewThread->CurrentFrame, tls);
x64::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(args->tls));
}
// Set us to start just after the syscall instruction
x64::AdjustRipForNewThread(NewThread->CurrentFrame);
}
else {
if (flags & CLONE_SETTLS) {
x32::SetThreadArea(NewThread->CurrentFrame, tls);
x32::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(args->tls));
}
x32::AdjustRipForNewThread(NewThread->CurrentFrame);
}
@@ -65,20 +66,32 @@ namespace FEX::HLE {
// Sets the child TID to pointer in ParentTID
if (flags & CLONE_PARENT_SETTID) {
*parent_tid = Result;
*reinterpret_cast<pid_t*>(args->parent_tid) = Result;
}
// Sets the child TID to the pointer in ChildTID
if (flags & CLONE_CHILD_SETTID) {
NewThread->ThreadManager.set_child_tid = child_tid;
*child_tid = Result;
NewThread->ThreadManager.set_child_tid = reinterpret_cast<int32_t*>(args->child_tid);
*reinterpret_cast<pid_t*>(args->child_tid) = Result;
}
// When the thread exits, clear the child thread ID at ChildTID
// Additionally wakeup a futex at that address
// Address /may/ be changed with SET_TID_ADDRESS syscall
if (flags & CLONE_CHILD_CLEARTID) {
NewThread->ThreadManager.clear_child_tid = child_tid;
NewThread->ThreadManager.clear_child_tid = reinterpret_cast<int32_t*>(args->child_tid);
}
// clone3 flag
if (flags & CLONE_PIDFD) {
// Use pidfd_open to emulate this flag
int pidfd = ::syscall(SYS_pidfd_open, Result, 0);
if (Result == ~0ULL) {
LogMan::Msg::E("Couldn't get pidfd of TID %d\n", Result);
}
else {
*reinterpret_cast<int*>(args->pidfd) = pidfd;
}
}
return NewThread;
@@ -162,6 +175,12 @@ namespace FEX::HLE {
return ForkGuest(Frame->Thread, Frame, 0, 0, 0, 0, 0);
});
REGISTER_SYSCALL_IMPL(clone3, ([](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *cl_args, size_t size) -> uint64_t {
FEX::HLE::clone3_args args{};
memcpy(&args, cl_args, std::min(sizeof(FEX::HLE::clone3_args), size));
return CloneHandler(Frame, &args);
}));
REGISTER_SYSCALL_IMPL(exit, [](FEXCore::Core::CpuStateFrame *Frame, int status) -> uint64_t {
auto Thread = Frame->Thread;
if (Thread->ThreadManager.clear_child_tid) {
@@ -303,13 +322,22 @@ namespace FEX::HLE {
});
REGISTER_SYSCALL_IMPL(arch_prctl, [](FEXCore::Core::CpuStateFrame *Frame, int code, unsigned long addr) -> uint64_t {
constexpr uint64_t TASK_MAX = (1ULL << 48); // 48-bits until we can query the host side VA sanely. AArch64 doesn't expose this in cpuinfo
uint64_t Result{};
switch (code) {
case 0x1001: // ARCH_SET_GS
if (addr >= TASK_MAX) {
// Ignore a non-canonical address
return -EPERM;
}
Frame->State.gs = addr;
Result = 0;
break;
case 0x1002: // ARCH_SET_FS
if (addr >= TASK_MAX) {
// Ignore a non-canonical address
return -EPERM;
}
Frame->State.fs = addr;
Result = 0;
break;
@@ -348,7 +376,7 @@ namespace FEX::HLE {
Thread->StatusCode = status;
FEXCore::Context::Stop(Thread->CTX);
// This will never be reached
std::unexpected();
std::terminate();
});
REGISTER_SYSCALL_IMPL(prlimit64, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit) -> uint64_t {
@@ -356,31 +384,30 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
/*
REGISTER_SYSCALL_IMPL(setpgid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, pid_t pgid) -> uint64_t {
SYSCALL_STUB(setpgid);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(setpgid);
uint64_t Result = ::setpgid(pid, pgid);
SYSCALL_ERRNO();
});
/*REGISTER_SYSCALL_IMPL(getpgid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
SYSCALL_STUB(getpgid);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(getpgid);
REGISTER_SYSCALL_IMPL(getpgid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
uint64_t Result = ::getpgid(pid);
SYSCALL_ERRNO();
});
/*REGISTER_SYSCALL_IMPL(setfsuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsuid) -> uint64_t {
SYSCALL_STUB(setfsuid);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(setfsuid);
REGISTER_SYSCALL_IMPL(setfsuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsuid) -> uint64_t {
uint64_t Result = ::setfsuid(fsuid);
SYSCALL_ERRNO();
});
/*REGISTER_SYSCALL_IMPL(setfsgid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsgid) -> uint64_t {
SYSCALL_STUB(setfsgid);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(setfsgid);
REGISTER_SYSCALL_IMPL(setfsgid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsgid) -> uint64_t {
uint64_t Result = ::setfsgid(fsgid);
SYSCALL_ERRNO();
});
/*REGISTER_SYSCALL_IMPL(getsid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
SYSCALL_STUB(getsid);
});*/
REGISTER_SYSCALL_FORWARD_ERRNO(getsid);
REGISTER_SYSCALL_IMPL(getsid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
uint64_t Result = ::getsid(pid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(waitid, [](FEXCore::Core::CpuStateFrame *Frame, idtype_t idtype, id_t id, siginfo_t *infop, int options) -> uint64_t {
uint64_t Result = ::waitid(idtype, id, infop, options);
+1 -1
View File
@@ -14,6 +14,6 @@ struct CPUState;
}
namespace FEX::HLE {
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context::Context *CTX, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls);
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context::Context *CTX, FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args);
uint64_t ForkGuest(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls);
}
+50 -12
View File
@@ -12,18 +12,20 @@ $end_info$
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
#include <stdint.h>
#include <algorithm>
#include <cstdint>
#include <sys/epoll.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <vector>
ARG_TO_STR(FEX::HLE::x32::compat_ptr<FEX::HLE::epoll_event_x86>, "%lx")
namespace FEX::HLE::x32 {
void RegisterEpoll() {
void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler) {
REGISTER_SYSCALL_IMPL_X32(epoll_wait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevents, int timeout) -> uint64_t {
std::vector<struct epoll_event> Events;
Events.resize(maxevents);
uint64_t Result = epoll_wait(epfd, &Events.at(0), maxevents, timeout);
std::vector<struct epoll_event> Events(std::max(0, maxevents));
uint64_t Result = ::syscall(SYS_epoll_pwait, epfd, Events.data(), maxevents, timeout, nullptr);
if (Result != -1) {
for (size_t i = 0; i < Result; ++i) {
@@ -35,23 +37,23 @@ namespace FEX::HLE::x32 {
REGISTER_SYSCALL_IMPL_X32(epoll_ctl, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, int op, int fd, epoll_event_x86 *event) -> uint64_t {
struct epoll_event Event = *event;
uint64_t Result = epoll_ctl(epfd, op, fd, &Event);
uint64_t Result = ::syscall(SYS_epoll_ctl, epfd, op, fd, &Event);
if (Result != -1) {
*event = Event;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(epoll_pwait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevent, int timeout, const void* sigmask) -> uint64_t {
std::vector<struct epoll_event> Events;
Events.resize(maxevent);
REGISTER_SYSCALL_IMPL_X32(epoll_pwait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevent, int timeout, const uint64_t* sigmask, size_t sigsetsize) -> uint64_t {
std::vector<struct epoll_event> Events(std::max(0, maxevent));
uint64_t Result = epoll_pwait(
uint64_t Result = ::syscall(SYS_epoll_pwait,
epfd,
&Events.at(0),
Events.data(),
maxevent,
timeout,
reinterpret_cast<const sigset_t*>(sigmask));
sigmask,
sigsetsize);
if (Result != -1) {
for (size_t i = 0; i < Result; ++i) {
@@ -61,5 +63,41 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 11, 0)) {
#ifndef SYS_epoll_pwait2
#define SYS_epoll_pwait2 354
#endif
REGISTER_SYSCALL_IMPL_X32(epoll_pwait2, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevent, compat_ptr<timespec32> timeout, const uint64_t* sigmask, size_t sigsetsize) -> uint64_t {
std::vector<struct epoll_event> Events(std::max(0, maxevent));
struct timespec tp64{};
struct timespec *timed_ptr{};
if (timeout) {
tp64 = *timeout;
timed_ptr = &tp64;
}
uint64_t Result = ::syscall(SYS_epoll_pwait2,
epfd,
Events.data(),
maxevent,
timed_ptr,
sigmask,
sigsetsize);
if (Result != -1) {
for (size_t i = 0; i < Result; ++i) {
events[i] = Events[i];
}
}
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL_X32(epoll_pwait2, UnimplementedSyscallSafe);
}
}
}
+56 -86
View File
@@ -13,6 +13,7 @@ $end_info$
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
#include <algorithm>
#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
@@ -32,7 +33,27 @@ $end_info$
ARG_TO_STR(FEX::HLE::x32::compat_ptr<FEX::HLE::x32::sigset_argpack32>, "%lx")
namespace FEX::HLE::x32 {
// Used to ensure no bogus values are passed into readv/writev family syscalls.
// This is mainly to sanitize vector sizing. It's fine for the bogus value
// itself to pass into the syscall, since the kernel will handle it.
static constexpr int SanitizeIOCount(int count) {
return std::max(0, count);
}
using fd_set32 = uint32_t;
#ifdef _M_X86_64
uint32_t ioctl_32(FEXCore::Core::CpuStateFrame*, int fd, uint32_t cmd, uint32_t args) {
uint32_t Result{};
__asm volatile("int $0x80;"
: "=a" (Result)
: "a" (SYSCALL_x86_ioctl)
, "b" (fd)
, "c" (cmd)
, "d" (args)
: "memory");
return Result;
}
#endif
void RegisterFD() {
REGISTER_SYSCALL_IMPL_X32(poll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, int timeout) -> uint64_t {
@@ -49,23 +70,12 @@ namespace FEX::HLE::x32 {
timed_ptr = &tp64;
}
sigset_t HostSet{};
if (sigmask) {
sigemptyset(&HostSet);
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
if (*sigmask & (1ULL << i)) {
sigaddset(&HostSet, i + 1);
}
}
}
uint64_t Result = ppoll(
uint64_t Result = ::syscall(SYS_ppoll,
fds,
nfds,
timed_ptr,
sigmask ? &HostSet : nullptr);
sigmask,
sigsetsize);
if (timeout_ts) {
*timeout_ts = tp64;
@@ -75,24 +85,13 @@ namespace FEX::HLE::x32 {
});
REGISTER_SYSCALL_IMPL_X32(ppoll_time64, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, struct timespec *timeout_ts, const uint64_t *sigmask, size_t sigsetsize) -> uint64_t {
// sigsetsize is unused here since it is currently a constant and not exposed through glibc
sigset_t HostSet{};
if (sigmask) {
sigemptyset(&HostSet);
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
if (*sigmask & (1ULL << i)) {
sigaddset(&HostSet, i + 1);
}
}
}
uint64_t Result = ppoll(
uint64_t Result = ::syscall(SYS_ppoll,
fds,
nfds,
timeout_ts,
sigmask ? &HostSet : nullptr);
sigmask,
sigsetsize);
SYSCALL_ERRNO();
});
@@ -109,21 +108,14 @@ namespace FEX::HLE::x32 {
});
REGISTER_SYSCALL_IMPL_X32(readv, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt) -> uint64_t {
std::vector<iovec> Host_iovec(iovcnt);
for (int i = 0; i < iovcnt; ++i) {
Host_iovec[i] = iov[i];
}
uint64_t Result = ::readv(fd, &Host_iovec.at(0), iovcnt);
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::readv(fd, Host_iovec.data(), iovcnt);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(writev, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt) -> uint64_t {
std::vector<iovec> Host_iovec(iovcnt);
for (int i = 0; i < iovcnt; ++i) {
Host_iovec[i] = iov[i];
}
uint64_t Result = ::writev(fd, &Host_iovec.at(0), iovcnt);
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::writev(fd, Host_iovec.data(), iovcnt);
SYSCALL_ERRNO();
});
@@ -250,13 +242,15 @@ namespace FEX::HLE::x32 {
break;
}
case F_SETFL:
lock_arg = (void*)FEX::HLE::RemapFromX86Flags(arg);
break;
// Maps directly
case F_DUPFD:
case F_DUPFD_CLOEXEC:
case F_GETFD:
case F_SETFD:
case F_GETFL:
case F_SETFL:
break;
default: LOGMAN_MSG_A("Unhandled fcntl64: 0x%x", cmd); break;
@@ -281,8 +275,12 @@ namespace FEX::HLE::x32 {
break;
case F_DUPFD:
case F_DUPFD_CLOEXEC:
FEX::HLE::x32::CheckAndAddFDDuplication(fd, Result);
break;
FEX::HLE::x32::CheckAndAddFDDuplication(fd, Result);
break;
case F_GETFL: {
Result = FEX::HLE::RemapToX86Flags(Result);
break;
}
default: break;
}
}
@@ -306,74 +304,46 @@ namespace FEX::HLE::x32 {
});
REGISTER_SYSCALL_IMPL_X32(preadv, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset) -> uint64_t {
std::vector<iovec> Host_iovec(iovcnt);
for (int i = 0; i < iovcnt; ++i) {
Host_iovec[i] = iov[i];
}
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::preadv(fd, &Host_iovec.at(0), iovcnt, offset);
uint64_t Result = ::preadv(fd, Host_iovec.data(), iovcnt, offset);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pwritev, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset) -> uint64_t {
std::vector<iovec> Host_iovec(iovcnt);
for (int i = 0; i < iovcnt; ++i) {
Host_iovec[i] = iov[i];
}
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::pwritev(fd, &Host_iovec.at(0), iovcnt, offset);
uint64_t Result = ::pwritev(fd, Host_iovec.data(), iovcnt, offset);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(process_vm_readv, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec32 *local_iov, unsigned long liovcnt, const struct iovec32 *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
std::vector<iovec> Host_local_iovec(liovcnt);
std::vector<iovec> Host_remote_iovec(riovcnt);
std::vector<iovec> Host_local_iovec(local_iov, local_iov + SanitizeIOCount(liovcnt));
std::vector<iovec> Host_remote_iovec(remote_iov, remote_iov + SanitizeIOCount(riovcnt));
for (int i = 0; i < liovcnt; ++i) {
Host_local_iovec[i] = local_iov[i];
}
for (int i = 0; i < riovcnt; ++i) {
Host_remote_iovec[i] = remote_iov[i];
}
uint64_t Result = ::process_vm_readv(pid, &Host_local_iovec.at(0), liovcnt, &Host_remote_iovec.at(0), riovcnt, flags);
uint64_t Result = ::process_vm_readv(pid, Host_local_iovec.data(), liovcnt, Host_remote_iovec.data(), riovcnt, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(process_vm_writev, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec32 *local_iov, unsigned long liovcnt, const struct iovec32 *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
std::vector<iovec> Host_local_iovec(liovcnt);
std::vector<iovec> Host_remote_iovec(riovcnt);
std::vector<iovec> Host_local_iovec(local_iov, local_iov + SanitizeIOCount(liovcnt));
std::vector<iovec> Host_remote_iovec(remote_iov, remote_iov + SanitizeIOCount(riovcnt));
for (int i = 0; i < liovcnt; ++i) {
Host_local_iovec[i] = local_iov[i];
}
for (int i = 0; i < riovcnt; ++i) {
Host_remote_iovec[i] = remote_iov[i];
}
uint64_t Result = ::process_vm_writev(pid, &Host_local_iovec.at(0), liovcnt, &Host_remote_iovec.at(0), riovcnt, flags);
uint64_t Result = ::process_vm_writev(pid, Host_local_iovec.data(), liovcnt, Host_remote_iovec.data(), riovcnt, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(preadv2, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset, int flags) -> uint64_t {
std::vector<iovec> Host_iovec(iovcnt);
for (int i = 0; i < iovcnt; ++i) {
Host_iovec[i] = iov[i];
}
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::preadv2(fd, &Host_iovec.at(0), iovcnt, offset, flags);
uint64_t Result = ::preadv2(fd, Host_iovec.data(), iovcnt, offset, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pwritev2, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset, int flags) -> uint64_t {
std::vector<iovec> Host_iovec(iovcnt);
for (int i = 0; i < iovcnt; ++i) {
Host_iovec[i] = iov[i];
}
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::pwritev2(fd, &Host_iovec.at(0), iovcnt, offset, flags);
uint64_t Result = ::pwritev2(fd, Host_iovec.data(), iovcnt, offset, flags);
SYSCALL_ERRNO();
});
@@ -625,7 +595,7 @@ namespace FEX::HLE::x32 {
}
}
if (sigmaskpack) {
if (sigmaskpack && sigmaskpack->sigset) {
uint64_t *sigmask = sigmaskpack->sigset;
size_t sigsetsize = sigmaskpack->size;
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
@@ -749,7 +719,7 @@ namespace FEX::HLE::x32 {
}
}
if (sigmaskpack) {
if (sigmaskpack && sigmaskpack->sigset) {
uint64_t *sigmask = sigmaskpack->sigset;
size_t sigsetsize = sigmaskpack->size;
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
+68 -66
View File
@@ -1,5 +1,7 @@
#pragma once
#include <FEXCore/Utils/CompilerDefs.h>
#include "Tests/LinuxSyscalls/x32/Types.h"
#include "Tests/LinuxSyscalls/x32/Ioctl/HelperDefines.h"
@@ -20,8 +22,8 @@ namespace FEX::HLE::x32 {
namespace DRM {
struct
__attribute__((annotate("alias-x86_32-drm_version")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_version")
FEX_ANNOTATE("fex-match")
fex_drm_version {
int version_major; /**< Major version */
int version_minor; /**< Minor version */
@@ -66,8 +68,8 @@ fex_drm_version {
};
struct
__attribute__((annotate("alias-x86_32-drm_unique")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_unique")
FEX_ANNOTATE("fex-match")
fex_drm_unique {
compat_size_t unique_len;
compat_ptr<char> unique;
@@ -88,8 +90,8 @@ fex_drm_unique {
};
struct
__attribute__((annotate("alias-x86_32-drm_map")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_map")
FEX_ANNOTATE("fex-match")
fex_drm_map {
uint32_t offset;
uint32_t size;
@@ -122,8 +124,8 @@ fex_drm_map {
};
struct
__attribute__((annotate("alias-x86_32-drm_client")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_client")
FEX_ANNOTATE("fex-match")
fex_drm_client {
int32_t idx;
int32_t auth;
@@ -156,8 +158,8 @@ fex_drm_client {
};
struct
__attribute__((annotate("alias-x86_32-drm_stats")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_stats")
FEX_ANNOTATE("fex-match")
fex_drm_stats {
uint32_t count;
struct {
@@ -187,8 +189,8 @@ fex_drm_stats {
};
struct
__attribute__((annotate("alias-x86_32-drm_buf_desc")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_buf_desc")
FEX_ANNOTATE("fex-match")
fex_drm_buf_desc {
int32_t count;
int32_t size;
@@ -225,8 +227,8 @@ fex_drm_buf_desc {
};
struct
__attribute__((annotate("alias-x86_32-drm_buf_info")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_buf_info")
FEX_ANNOTATE("fex-match")
fex_drm_buf_info {
int32_t count;
compat_ptr<struct drm_buf_desc> list;
@@ -247,8 +249,8 @@ fex_drm_buf_info {
};
struct
__attribute__((annotate("alias-x86_32-drm_buf_pub")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_buf_pub")
FEX_ANNOTATE("fex-match")
fex_drm_buf_pub {
int32_t idx;
int32_t total;
@@ -275,8 +277,8 @@ fex_drm_buf_pub {
};
struct
__attribute__((annotate("alias-x86_32-drm_buf_map")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_buf_map")
FEX_ANNOTATE("fex-match")
fex_drm_buf_map {
int32_t count;
#ifdef __cplusplus
@@ -313,8 +315,8 @@ fex_drm_buf_map {
};
struct
__attribute__((annotate("alias-x86_32-drm_buf_free")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_buf_free")
FEX_ANNOTATE("fex-match")
fex_drm_buf_free {
int32_t count;
compat_ptr<int> list;
@@ -335,8 +337,8 @@ fex_drm_buf_free {
};
struct
__attribute__((annotate("alias-x86_32-drm_ctx_priv_map")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_ctx_priv_map")
FEX_ANNOTATE("fex-match")
fex_drm_ctx_priv_map {
uint32_t ctx_id;
compat_ptr<void> handle;
@@ -357,8 +359,8 @@ fex_drm_ctx_priv_map {
};
struct
__attribute__((annotate("alias-x86_32-drm_ctx_res")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_ctx_res")
FEX_ANNOTATE("fex-match")
fex_drm_ctx_res {
int32_t count;
compat_ptr<struct drm_ctx> contexts;
@@ -378,8 +380,8 @@ fex_drm_ctx_res {
};
struct
__attribute__((annotate("alias-x86_32-drm_dma")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_dma")
FEX_ANNOTATE("fex-match")
fex_drm_dma {
int32_t context;
int32_t send_count;
@@ -424,8 +426,8 @@ fex_drm_dma {
};
struct
__attribute__((annotate("alias-x86_32-drm_scatter_gather")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_scatter_gather")
FEX_ANNOTATE("fex-match")
fex_drm_scatter_gather {
uint32_t size;
uint32_t handle;
@@ -446,8 +448,8 @@ fex_drm_scatter_gather {
};
struct
__attribute__((annotate("alias-x86_32-drm_wait_vblank_request")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_wait_vblank_request")
FEX_ANNOTATE("fex-match")
fex_drm_wait_vblank_request {
enum drm_vblank_seq_type type;
uint32_t sequence;
@@ -471,8 +473,8 @@ fex_drm_wait_vblank_request {
};
struct
__attribute__((annotate("alias-x86_32-drm_wait_vblank_reply")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_wait_vblank_reply")
FEX_ANNOTATE("fex-match")
fex_drm_wait_vblank_reply {
enum drm_vblank_seq_type type;
uint32_t sequence;
@@ -499,8 +501,8 @@ fex_drm_wait_vblank_reply {
};
union
__attribute__((annotate("alias-x86_32-drm_wait_vblank")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_wait_vblank")
FEX_ANNOTATE("fex-match")
fex_drm_wait_vblank {
fex_drm_wait_vblank_request request;
fex_drm_wait_vblank_reply reply;
@@ -509,9 +511,9 @@ fex_drm_wait_vblank {
};
struct
__attribute__((annotate("alias-x86_32-drm_update_draw")))
__attribute__((annotate("fex-match")))
__attribute__((packed))
FEX_ANNOTATE("alias-x86_32-drm_update_draw")
FEX_ANNOTATE("fex-match")
FEX_PACKED
fex_drm_update_draw {
drm_drawable_t handle;
uint32_t type;
@@ -538,9 +540,9 @@ fex_drm_update_draw {
};
struct
__attribute__((annotate("alias-x86_32-drm_mode_get_plane_res")))
__attribute__((annotate("fex-match")))
__attribute__((packed))
FEX_ANNOTATE("alias-x86_32-drm_mode_get_plane_res")
FEX_ANNOTATE("fex-match")
FEX_PACKED
fex_drm_mode_get_plane_res {
compat_uint64_t plane_id_ptr;
uint32_t count_planes;
@@ -560,9 +562,9 @@ fex_drm_mode_get_plane_res {
};
struct
__attribute__((annotate("alias-x86_32-drm_mode_fb_cmd2")))
__attribute__((annotate("fex-match")))
__attribute__((packed))
FEX_ANNOTATE("alias-x86_32-drm_mode_fb_cmd2")
FEX_ANNOTATE("fex-match")
FEX_PACKED
fex_drm_mode_fb_cmd2 {
uint32_t fb_id;
uint32_t width;
@@ -608,9 +610,9 @@ fex_drm_mode_fb_cmd2 {
};
struct
__attribute__((annotate("alias-x86_32-drm_mode_obj_get_properties")))
__attribute__((annotate("fex-match")))
__attribute__((packed))
FEX_ANNOTATE("alias-x86_32-drm_mode_obj_get_properties")
FEX_ANNOTATE("fex-match")
FEX_PACKED
fex_drm_mode_obj_get_properties {
compat_uint64_t props_ptr;
compat_uint64_t prop_values_ptr;
@@ -639,9 +641,9 @@ fex_drm_mode_obj_get_properties {
};
struct
__attribute__((annotate("alias-x86_32-drm_mode_obj_set_property")))
__attribute__((annotate("fex-match")))
__attribute__((packed))
FEX_ANNOTATE("alias-x86_32-drm_mode_obj_set_property")
FEX_ANNOTATE("fex-match")
FEX_PACKED
fex_drm_mode_obj_set_property {
compat_uint64_t value;
uint32_t prop_id;
@@ -671,8 +673,8 @@ fex_drm_mode_obj_set_property {
namespace AMDGPU {
struct
__attribute__((annotate("alias-x86_32-drm_amdgpu_gem_metadata")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_amdgpu_gem_metadata")
FEX_ANNOTATE("fex-match")
fex_drm_amdgpu_gem_metadata {
__u32 handle;
__u32 op;
@@ -708,8 +710,8 @@ fex_drm_amdgpu_gem_metadata {
namespace MSM {
struct
__attribute__((annotate("alias-x86_32-drm_msm_timespec")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_msm_timespec")
FEX_ANNOTATE("fex-match")
fex_drm_msm_timespec {
compat_int64_t tv_sec;
compat_int64_t tv_nsec;
@@ -729,9 +731,9 @@ fex_drm_msm_timespec {
};
struct
__attribute__((annotate("alias-x86_32-drm_msm_wait_fence")))
__attribute__((annotate("fex-match")))
__attribute__((packed))
FEX_ANNOTATE("alias-x86_32-drm_msm_wait_fence")
FEX_ANNOTATE("fex-match")
FEX_PACKED
fex_drm_msm_wait_fence {
uint32_t fence;
uint32_t pad;
@@ -762,8 +764,8 @@ fex_drm_msm_wait_fence {
namespace I915 {
struct
__attribute__((annotate("alias-x86_32-drm_i915_batchbuffer")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_i915_batchbuffer")
FEX_ANNOTATE("fex-match")
fex_drm_i915_batchbuffer_t {
int32_t start;
int32_t used;
@@ -796,8 +798,8 @@ fex_drm_i915_batchbuffer_t {
};
struct
__attribute__((annotate("alias-x86_32-drm_i915_irq_emit")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_i915_irq_emit")
FEX_ANNOTATE("fex-match")
fex_drm_i915_irq_emit_t {
compat_ptr<int> irq_seq;
@@ -815,8 +817,8 @@ fex_drm_i915_irq_emit_t {
};
struct
__attribute__((annotate("alias-x86_32-drm_i915_getparam")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_i915_getparam")
FEX_ANNOTATE("fex-match")
fex_drm_i915_getparam_t
{
int32_t param;
@@ -837,8 +839,8 @@ fex_drm_i915_getparam_t
};
struct
__attribute__((annotate("alias-x86_32-drm_i915_mem_alloc")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-drm_i915_mem_alloc")
FEX_ANNOTATE("fex-match")
fex_drm_i915_mem_alloc_t
{
int32_t region;
@@ -865,8 +867,8 @@ fex_drm_i915_mem_alloc_t
};
struct
__attribute__((annotate("alias-x86_32-_drm_i915_cmdbuffer")))
__attribute__((annotate("fex-match")))
FEX_ANNOTATE("alias-x86_32-_drm_i915_cmdbuffer")
FEX_ANNOTATE("fex-match")
fex_drm_i915_cmdbuffer_t
{
compat_ptr<char> buf;
+111 -52
View File
@@ -27,20 +27,6 @@ extern "C" {
#include <unistd.h>
namespace FEX::HLE::x32 {
#ifdef _M_X86_64
uint32_t ioctl_32(int fd, uint32_t cmd, uint32_t args) {
uint32_t Result{};
__asm volatile("int $0x80;"
: "=a" (Result)
: "a" (SYSCALL_x86_ioctl)
, "b" (fd)
, "c" (cmd)
, "d" (args)
: "memory");
return Result;
}
#endif
static void UnhandledIoctl(const char *Type, int fd, uint32_t cmd, uint32_t args) {
LogMan::Msg::E("@@@@@@@@@@@@@@@@@@@@@@@@@");
LogMan::Msg::E("Unhandled %s ioctl(%d, 0x%08x, 0x%08x)", Type, fd, cmd, args);
@@ -59,14 +45,108 @@ namespace FEX::HLE::x32 {
}
namespace DRM {
std::map<uint32_t, std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)>> FDToHandler;
uint32_t AddAndRunHandler(int fd, uint32_t cmd, uint32_t args);
void AssignDeviceTypeToFD(int fd, drm_version const &Version);
template <size_t LRUSize>
class LRUCacheFDCache {
public:
LRUCacheFDCache() {
// Set the last element to our handler
// This element will always be the last one
LRUCache[LRUSize] = std::make_pair(0, AddAndRunHandler);
}
using HandlerType = uint32_t(*)(int fd, uint32_t cmd, uint32_t args);
void SetFDHandler(uint32_t FD, HandlerType Handler) {
FDToHandler[FD] = Handler;
}
void DuplicateFD(int fd, int NewFD) {
auto it = FDToHandler.find(fd);
if (it != FDToHandler.end()) {
FDToHandler[NewFD] = it->second;
}
}
HandlerType FindHandler(uint32_t FD) {
HandlerType Handler{};
for (size_t i = 0; i < LRUSize; ++i) {
auto &it = LRUCache[i];
if (it.first == FD) {
if (i == 0) {
// If we are the first in the queue then just return it
return it.second;
}
Handler = it.second;
break;
}
}
if (Handler) {
AddToFront(FD, Handler);
return Handler;
}
return LRUCache[LRUSize].second;
}
uint32_t AddAndRunMapHandler(int fd, uint32_t cmd, uint32_t args) {
// Couldn't find in cache, check map
{
auto it = FDToHandler.find(fd);
if (it != FDToHandler.end()) {
// Found, add to the cache
AddToFront(fd, it->second);
return it->second(fd, cmd, args);
}
}
// Wasn't found in map, query it
drm_version Host_Version{};
Host_Version.name = reinterpret_cast<char*>(alloca(128));
Host_Version.name_len = 128;
uint64_t Result = ioctl(fd, DRM_IOCTL_VERSION, &Host_Version);
// Add it to the map and double check that it was added
// Next time around when the ioctl is used then it will be added to cache
if (Result != -1) {
AssignDeviceTypeToFD(fd, Host_Version);
}
auto it = FDToHandler.find(fd);
if (it == FDToHandler.end()) {
// We don't understand this DRM ioctl
return -EPERM;
}
Result = it->second(fd, cmd, args);
SYSCALL_ERRNO();
}
private:
void AddToFront(uint32_t FD, HandlerType Handler) {
// Push the element to the front if we found one
// First copy all the other elements back one
// Ensuring the final element isn't written over
memmove(&LRUCache[1], &LRUCache[0], (LRUSize - 1) * sizeof(LRUCache[0]));
// Now set the first element to the one we just found
LRUCache[0] = std::make_pair(FD, Handler);
}
// With four elements total (3 + 1) then this is a single cacheline in size
std::pair<uint32_t, HandlerType> LRUCache[LRUSize + 1];
std::map<uint32_t, HandlerType> FDToHandler;
};
static LRUCacheFDCache<3> FDToHandler;
uint32_t AddAndRunHandler(int fd, uint32_t cmd, uint32_t args) {
return FDToHandler.AddAndRunMapHandler(fd, cmd, args);
}
void CheckAndAddFDDuplication(int fd, int NewFD) {
auto it = FDToHandler.find(fd);
if (it != FDToHandler.end()) {
FDToHandler[NewFD] = it->second;
}
FDToHandler.DuplicateFD(fd, NewFD);
}
uint32_t AMDGPU_Handler(int fd, uint32_t cmd, uint32_t args) {
switch (_IOC_NR(cmd)) {
case _IOC_NR(FEX_DRM_IOCTL_AMDGPU_GEM_METADATA): {
@@ -260,22 +340,22 @@ namespace FEX::HLE::x32 {
void AssignDeviceTypeToFD(int fd, drm_version const &Version) {
if (Version.name) {
if (strcmp(Version.name, "amdgpu") == 0) {
FDToHandler[fd] = AMDGPU_Handler;
FDToHandler.SetFDHandler(fd, AMDGPU_Handler);
}
else if (strcmp(Version.name, "msm") == 0) {
FDToHandler[fd] = MSM_Handler;
FDToHandler.SetFDHandler(fd, MSM_Handler);
}
else if (strcmp(Version.name, "nouveau") == 0) {
FDToHandler[fd] = Nouveau_Handler;
FDToHandler.SetFDHandler(fd, Nouveau_Handler);
}
else if (strcmp(Version.name, "i915") == 0) {
FDToHandler[fd] = I915_Handler;
FDToHandler.SetFDHandler(fd, I915_Handler);
}
else if (strcmp(Version.name, "panfrost") == 0) {
FDToHandler[fd] = Panfrost_Handler;
FDToHandler.SetFDHandler(fd, Panfrost_Handler);
}
else if (strcmp(Version.name, "lima") == 0) {
FDToHandler[fd] = Lima_Handler;
FDToHandler.SetFDHandler(fd, Lima_Handler);
}
else {
LogMan::Msg::E("Unknown DRM device: '%s'", Version.name);
@@ -360,24 +440,8 @@ namespace FEX::HLE::x32 {
case DRM_COMMAND_BASE ... (DRM_COMMAND_END - 1): {
// This is the space of the DRM device commands
auto it = FDToHandler.find(fd);
if (it == FDToHandler.end()) {
drm_version Host_Version{};
Host_Version.name = reinterpret_cast<char*>(alloca(128));
Host_Version.name_len = 128;
uint64_t Result = ioctl(fd, DRM_IOCTL_VERSION, &Host_Version);
if (Result != -1) {
AssignDeviceTypeToFD(fd, Host_Version);
}
it = FDToHandler.find(fd);
if (it == FDToHandler.end()) {
return -EPERM;
}
}
return it->second(fd, cmd, args);
auto it = FDToHandler.FindHandler(fd);
return it(fd, cmd, args);
break;
}
default:
@@ -400,7 +464,7 @@ namespace FEX::HLE::x32 {
std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)> Handler;
};
static std::unordered_map<uint32_t, std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)>> Handlers;
static std::vector<std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)>> Handlers;
void InitializeStaticIoctlHandlers() {
using namespace DRM;
@@ -452,20 +516,15 @@ namespace FEX::HLE::x32 {
#undef _CUSTOM_META_OFFSET
}};
Handlers.assign(1U << _IOC_TYPEBITS, FEX::HLE::x32::BasicHandler::BasicHandler);
for (auto &Arg : LocalHandlers) {
Handlers[Arg.Command] = Arg.Handler;
}
}
uint32_t ioctl32(FEXCore::Core::CpuStateFrame *Frame, int fd, uint32_t request, uint32_t args) {
//return ioctl_32(fd, request, args);
auto It = Handlers.find(_IOC_TYPE(request));
if (It == Handlers.end()) {
UnhandledIoctl("Base", fd, request, args);
return -EPERM;
}
return It->second(fd, request, args);
return Handlers[_IOC_TYPE(request)](fd, request, args);
}
void CheckAndAddFDDuplication(int fd, int NewFD) {
+29 -12
View File
@@ -33,7 +33,7 @@ namespace FEX::HLE::x32 {
};
struct msgbuf_32 {
uint32_t mtype;
compat_long_t mtype;
char mtext[1];
};
@@ -640,13 +640,29 @@ namespace FEX::HLE::x32 {
std::vector<uint8_t> Tmp(second + sizeof(size_t));
struct msgbuf *TmpMsg = reinterpret_cast<struct msgbuf *>(&Tmp.at(0));
Result = ::msgrcv(first, TmpMsg, second, *reinterpret_cast<uint32_t*>(fifth), third);
if (call >> 16) {
Result = ::msgrcv(first, TmpMsg, second, fifth, third);
if (Result != -1) {
msgbuf_32 *src = reinterpret_cast<msgbuf_32*>(ptr);
src->mtype = TmpMsg->mtype;
memcpy(src->mtext, TmpMsg->mtext, Result);
}
if (Result != -1) {
msgbuf_32 *src = reinterpret_cast<msgbuf_32*>(*reinterpret_cast<uint32_t*>(ptr));
src->mtype = TmpMsg->mtype;
memcpy(src->mtext, TmpMsg->mtext, Result);
}
else {
struct compat_ipc_kludge {
compat_uptr_t msgp;
compat_long_t msgtyp;
};
compat_ipc_kludge *ipck = reinterpret_cast<compat_ipc_kludge*>(ptr);
Result = ::msgrcv(first, TmpMsg, second, ipck->msgtyp, third);
if (Result != -1) {
msgbuf_32 *src = reinterpret_cast<msgbuf_32*>(ipck->msgp);
ipck->msgtyp = TmpMsg->mtype;
memcpy(src->mtext, TmpMsg->mtext, Result);
}
}
break;
}
case OP_MSGGET: {
@@ -655,9 +671,10 @@ namespace FEX::HLE::x32 {
}
case OP_MSGCTL: {
uint32_t msqid = first;
int32_t cmd = third & 0xFF;
msgun_32 *msgun = reinterpret_cast<msgun_32*>(ptr);
bool IPC64 = third & 0x100;
int32_t cmd = second & 0xFF;
msgun_32 msgun{};
msgun.val = ptr;
bool IPC64 = second & 0x100;
#define UNHANDLED(x) case x: LOGMAN_MSG_A("Unhandled msgctl cmd: " #x); break
switch (cmd) {
UNHANDLED(IPC_SET);
@@ -668,10 +685,10 @@ namespace FEX::HLE::x32 {
Result = ::msgctl(msqid, cmd, &buf);
if (Result != -1) {
if (IPC64) {
*msgun->buf64 = buf;
*msgun.buf64 = buf;
}
else {
*msgun->buf32 = buf;
*msgun.buf32 = buf;
}
}
break;
@@ -681,7 +698,7 @@ namespace FEX::HLE::x32 {
struct msginfo mi{};
Result = ::msgctl(msqid, cmd, reinterpret_cast<struct msqid_ds*>(&mi));
if (Result != -1) {
memcpy(msgun->__buf, &mi, sizeof(mi));
memcpy(msgun.__buf, &mi, sizeof(mi));
}
break;
}
+7 -3
View File
@@ -29,7 +29,11 @@ namespace FEX::HLE::x32 {
return static_cast<uint32_t>(reinterpret_cast<uint64_t>(oldact.sigaction_handler.handler));
});
REGISTER_SYSCALL_IMPL_X32(rt_sigaction, [](FEXCore::Core::CpuStateFrame *Frame, int signum, const GuestSigAction_32 *act, GuestSigAction_32 *oldact) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(rt_sigaction, [](FEXCore::Core::CpuStateFrame *Frame, int signum, const GuestSigAction_32 *act, GuestSigAction_32 *oldact, size_t sigsetsize) -> uint64_t {
if (sigsetsize != 8) {
return -EINVAL;
}
FEXCore::GuestSigAction *act64_p{};
FEXCore::GuestSigAction *old64_p{};
@@ -45,11 +49,11 @@ namespace FEX::HLE::x32 {
}
uint64_t Result = FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterGuestSignalHandler(signum, act64_p, old64_p);
if (Result != -1 && oldact) {
if (Result == 0 && oldact) {
*oldact = old64;
}
SYSCALL_ERRNO();
return Result;
});
REGISTER_SYSCALL_IMPL_X32(rt_sigtimedwait, [](FEXCore::Core::CpuStateFrame *Frame, uint64_t *set, siginfo_t *info, const struct timespec32* timeout, size_t sigsetsize) -> uint64_t {
Loaded 100 of 185 files, more files were not shown because too many files have changed in this diff. Show more