Compare commits

..
471 Commits
Author SHA1 Message Date
Ryan Houdek 33fe6813fc Docs: Update for release FEX-2104 2021-04-02 11:35:29 -07:00
Ryan Houdek c6c2555e1b Merge pull request #938 from Sonicadvance1/FEXConfig_improvements
FEXConfig improvements
2021-04-01 23:55:16 -07:00
Stefanos Kornilios Mitsis Poiitidis 283a5c1293 Merge pull request #937 from Sonicadvance1/fix_inverted_bool
Fixes inverted boolean FEXLoader arguments
2021-04-02 09:38:21 +03:00
Stefanos Kornilios Mitsis Poiitidis b31976d0e2 Merge pull request #936 from Sonicadvance1/fix_cp_config_error
Fixes Thunk Guest libs short arg. C&P error made it not be j
2021-04-02 09:36:53 +03:00
Ryan Houdek f922e6420b Allow opening the first configuration file passed in
Lets us easily open any configuration file
2021-04-01 19:25:17 -07:00
Ryan Houdek 7ff06c828f Loads default config if doesn't exist
Allows us to save a folder with the default if we want it
2021-04-01 19:22:53 -07:00
Ryan Houdek be16cc1456 Fixes window ordering so popup message is visible without config open
This was a child of the config popup before.
Moves it to a child of the workspace instead so that if we had a load error the message is still visible
2021-04-01 19:16:03 -07:00
Ryan Houdek 59df3d13cc Don't set up RootFS folder or inotify thread on config load failure
Can happen in the case that the default file tries to get loaded and doens't exist
2021-04-01 19:15:08 -07:00
Ryan Houdek 1adf518e6f Don't try creating inotify thread twice
If the inotify FD is already opened then just early exit
Can happen in the case that a config tries to get opened and didn't exist
2021-04-01 19:13:43 -07:00
Ryan Houdek 68171ad9b5 Creates RootFS data folder as a user convenience
To make sure users don't mess this up, create the folder if it isn't found
2021-04-01 19:12:56 -07:00
Ryan Houdek 78a923b5b0 Fixes inverted boolean FEXLoader arguments
The default_value doesn't have anything to do with the boolean argument being set.
Setting a boolean argument will always set true, inverted always sets false.
We don't currently have a need to support the inverted case where passing in an argument sets it to false
2021-04-01 19:10:29 -07:00
Ryan Houdek 0c6741438b Update config generator python to check for duplicate options
Checks for duplicate short and long option arguments
2021-04-01 19:08:19 -07:00
Ryan Houdek 5d0c080a9d Fixes Thunk Guest libs short arg. C&P error made it not be j 2021-04-01 19:08:00 -07:00
Ryan Houdek 5f49b57948 Merge pull request #928 from Sonicadvance1/pthreads_threading
Switches FEXCore over to pthreads implementation
2021-04-01 01:15:43 -07:00
Ryan Houdek 38778953a1 Merge pull request #924 from Sonicadvance1/select_named_rootfs
Adds support for Named RootFS folders in FEXConfig
2021-04-01 00:32:15 -07:00
Ryan Houdek 21a364c2ad Switches FEXCore over to pthreads implementation
This defaults to a pthread implementation but it can be switched over to a custom thread
handler if the frontend desires.
2021-04-01 00:31:45 -07:00
Ryan Houdek 3294cc209f Adds support for Named RootFS folders in FEXConfig
Provides a drop down dialog of RootFS folders to select.
Will be empty if the user doesn't have any folders in place.
Just allowing directory paths to be inserted

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fills out this data structure as well just incase we hit another cacheline size bug
2021-03-06 07:30:25 -08:00
Ryan Houdek 852ab4e245 Merge pull request #815 from Sonicadvance1/sse4_1_movnt
Implements MOVNTDQA
2021-03-06 07:25:56 -08:00
Ryan Houdek bb6e7360a2 Merge pull request #813 from Sonicadvance1/disable_python_dev_check
Disabled cmake check for python development
2021-03-06 07:25:16 -08:00
Ryan Houdek 50ae18cbaf Adds MOVNTDQA unit test 2021-03-06 06:23:27 -08:00
Ryan Houdek 1adbf66fee Implements MOVNTDQA
Found this when running Steamlink under FEX on my laptop.
Turns out the Iris video driver now uses this unconditionally in a couple of locations.
Makes sense there since all the Iris targets are expected to support SSE4.1 atm.
2021-03-06 06:23:18 -08:00
Ryan Houdek 8222acdf20 Disabled cmake check for python development
We only need the interpreter
2021-03-06 06:06:59 -08:00
Stefanos Kornilios Mitsis Poiitidis 3978097f15 Merge pull request #705 from FEX-Emu/skmp/mmap-cache-flushing
Flush IR/Code cache on mmap, mmunmap & mprot
2021-03-03 12:59:29 +02:00
Stefanos Kornilios Mitsis Poiitidis 613e5b66ba SMC: Expand --smc-checks options to 'none', 'mman', 'full' 2021-03-03 12:49:23 +02:00
Ryan Houdek e95d1bd761 Merge pull request #811 from phire/remove-operator-names
X64_64 JIt: Remove -fno-operator-names
2021-03-02 18:55:53 -08:00
Scott Mansell ec95610587 X64_64 JIt: Remove -fno-operator-names
Some IDEs didn't recognize this flag and threw
phantom errors when these files were open.

Lets lean towards a smoother developer experience
2021-03-03 15:47:58 +13:00
Ryan Houdek b57cf78cc7 Merge pull request #810 from phire/fix_include
Add missing unordered_map include
2021-03-02 18:00:39 -08:00
Scott Mansell f5ac5d1667 Add missing unordered_map include
It's really unhelpful that libstdc++ includes this by default
2021-03-03 14:06:16 +13:00
Stefanos Kornilios Mitsis Poiitidis e92578817d SMC: Flush IR/Code cache on mmap, mmunmap & mprot 2021-02-26 13:22:05 +02:00
Stefanos Kornilios Mitsis Poiitidis 5b4696976d Merge pull request #805 from FEX-Emu/skmp/asm-thunks
Thunks: Convert guest thunk to asm, to avoid issues with older gcc versions
2021-02-26 12:57:14 +02:00
Stefanos Kornilios Mitsis Poiitidis aecea294d5 Merge pull request #792 from Sonicadvance1/implemented_unaligned_memory_ops
Implements unaligned atomic memory ops for ARMv8.1+
2021-02-26 12:50:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 70a98cd465 Thunks: Convert guest thunk to asm, to avoid issues with older gcc verions 2021-02-26 12:43:37 +02:00
Stefanos Kornilios Mitsis Poiitidis 2071bab238 Merge pull request #803 from Sonicadvance1/faccessat2
Implements support for faccessat2 in syscallhandler
2021-02-25 23:58:11 +02:00
Ryan Houdek 9746e0175e Change unsupported faccessat2 to use safe syscall unsupported 2021-02-25 13:45:24 -08:00
Ryan Houdek d78a3784fb Guards SYS_faccessat2 define for new enough glibc defines 2021-02-25 13:44:40 -08:00
Ryan Houdek 7dd5252d68 Implements support for faccessat2 in syscallhandler
Only exists if the host kernel is >= 5.8.0.
2021-02-24 09:52:30 -08:00
Ryan Houdek ac92a103df Adds support for faccessat2 to FileManager 2021-02-24 09:51:29 -08:00
Ryan Houdek 74a00fee0f Updates syscall definitions enums 2021-02-24 09:51:03 -08:00
Ryan Houdek ebe447f86a Merge pull request #801 from Sonicadvance1/fix_cmpxchg_flags
Fixes CMPXCHG flags being incorrect aside from ZF
2021-02-24 09:01:01 -08:00
Ryan Houdek ebac071ae1 Adds cmpxchg to register zext and flags unit test 2021-02-23 23:22:24 -08:00
Ryan Houdek 68db163efc Adds cmpxchg to memory zext and flag unit test 2021-02-23 23:22:00 -08:00
Ryan Houdek 6c469c21db Fixes Zext and flags behaviour of CMPXCHG to register 2021-02-23 23:21:28 -08:00
Ryan Houdek 9798522bc5 Fixes Zext behaviour of CMPXCHG to memory 2021-02-23 23:21:05 -08:00
Ryan Houdek 55ddc5b6e4 Adds unit tests to ensure cmpxchg flag correctness 2021-02-23 22:02:39 -08:00
Ryan Houdek 373f4275f6 Fixes CMPXCHG flags being incorrect aside from ZF
Almost everything only checks ZF but we had the arguments reversed for
the rest of the comparison flag results.
2021-02-23 22:01:52 -08:00
Scott Mansell 6fee5b9ca2 Merge pull request #789 from FEX-Emu/skmp/add-cacheinfo-cpuid
CPUID: Add cache information, function 0x2
2021-02-24 03:06:20 +13:00
Stefanos Kornilios Mitsis Poiitidis aaa63a2409 Merge pull request #730 from FEX-Emu/skmp/ir-cache
AOTIR: Initial implementation
2021-02-23 13:10:37 +02:00
Stefanos Kornilios Mitsis Poiitidis 6d0d3eaf04 AOTIR: Review feedback 2021-02-23 12:23:56 +02:00
Stefanos Kornilios Mitsis Poiitidis 075cd423ed AOTIR: Review feedback 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 8e06966ddc AOTIR: Make OP_REMOVECODEENTRY relocatable 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis bdcf3ff606 AOTIR: Merge IRLists, RALists and DebugDataLists to LocalIRCache; cleanups and fixups 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis b29121c71a AOTIR: Rename AOTCache to AOTIRCache 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis a5ee4bdf3c AOTIR: Fix double free issues 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis abb2e09c20 AOTIR: Add support for 32-bit process 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 56d10dcbbb IR: Make OP_THUNK use an inline sha256 hash of the thunk name, update thunk scripts 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 1f919949cc JIT: Fix arm64 build 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 63507f5ece IR: Fix int64_t parsing 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis dbc8b8e8e9 AOTIR: Append optimization flags to fileid 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 681deca4eb IR: Make entrypoint implicit, Add InlineEntrypointOffset, make ValidateCode Entrypoint-relative 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis b3d12fbb7b AOTIR: Cleanup interface 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis fafc987eab AOTIR: Rename Generate to Capture 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis d825ed2b03 AOTIR: Reduce map lookup 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis f2f62c5f2c AOTIR: Implement EntrypointOffset for aarch64 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 3e1e093b24 AOTIR: Store in ~/.fex-emu/aotir, per so 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 71dceb5339 Fix relocation support for FinishOp 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis be40d1604a Improve aotir format 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 47d4481a75 AOTIR: Support relocations via new ir op 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis fc30efe040 AOTIR: Add hashing of code bytes 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 57680a8614 AOTIR: Add --aotir-generate and --aotir-load to FEXLoader & Config 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 66bdface49 AOTIR: Make copies for insertions, only cache RA'd blocks 2021-02-23 12:08:57 +02:00
Stefanos Kornilios Mitsis Poiitidis 816c4656df IR: Add AOT Cache 2021-02-23 12:08:57 +02:00
Scott Mansell 7b97c3fa23 Merge pull request #755 from Sonicadvance1/host_uname
Pulls uname nodename from host system
2021-02-23 23:07:29 +13:00
Stefanos Kornilios Mitsis Poiitidis 8642406b8f CPUID: Add cache information, function 0x2 2021-02-23 11:01:15 +02:00
Stefanos Kornilios Mitsis Poiitidis e2599db3ed Merge pull request #793 from Sonicadvance1/assert_on_missing_lock
Adds assert checks on missing LOCK support
2021-02-23 10:51:30 +02:00
Ryan Houdek bb0850eab8 Adds assert checks on missing LOCK support
These usually don't get hit, but Geekbench4 DOES manage to hit LOCK on
BTS.

Which we just don't support right now.
2021-02-22 23:20:18 -08:00
Stefanos Kornilios Mitsis Poiitidis 1af541475a Merge pull request #795 from Sonicadvance1/atomic_bittest_ops
Implements BTC, BTR, BTS atomic variants
2021-02-23 09:08:39 +02:00
Stefanos Kornilios Mitsis Poiitidis 6669bd2d4a Merge pull request #794 from Sonicadvance1/fix_log_move_fail
Pass log moves on buildbot stage failure
2021-02-23 08:59:21 +02:00
Ryan Houdek d3e98fedb4 Pulls uname nodename from host system
Hardcoding FEXCore as the nodename is an annoyance.
Pull the host's nodename instead.

Fixes #600
2021-02-22 22:39:05 -08:00
Ryan Houdek 8a39f4b25c Implements BTC, BTR, BTS atomic unit tests
This just takes the regular non-atomic unit tests and changes them to
have lock prefixes.
These are all handled as byte sized atomics so there aren't any
alignment problems.
2021-02-22 22:29:31 -08:00
Ryan Houdek 232eeff483 Implements BTC, BTR, BTS atomic variants
BTS specifically is being used for threading related tasks. Which could
be why our threading has been unstable.
2021-02-22 22:27:10 -08:00
Ryan Houdek 12ef69650f Pass log moves on buildbot stage failure
Passes the log movement stage to clean up the output.
Really it is only the first unit test stage that is failing, but each
subsequent log run will have failed to move since the previous stage
didn't run.

Makes it easier to scan over a failure as only the first unit test
failure step.
2021-02-22 18:42:28 -08:00
Ryan Houdek f1349becb4 Disables the unaligned atomic memory op tests
These don't run on the armv8.0 runner
2021-02-22 18:33:17 -08:00
Ryan Houdek f848957e17 Implements unit tests for the new unaligned atomics
Tests all the unaligned atomic ops we support now
2021-02-22 18:25:25 -08:00
Ryan Houdek 55fc47e0b7 Passes SIGBUS handler to unaligned atomic op handler for ARMv8.1
ARMv8.0 still unsupported here.

This allows us to support unaligned atomic ops for every atomic x86 op
that we support.
2021-02-22 18:22:10 -08:00
Ryan Houdek 4f40166902 Implements unaligned atomic memory ops for ARMv8.1+
This takes the existing unaligned CAS handling code and makes it more
robust to handle both true unaligned CAS and unaligned atomic memory
ops.

Specifically it inserts some lambda helpers to calculate the Desired and
Expected values inside of the CAS loops to account for CAS and memory
operation.
2021-02-22 18:17:53 -08:00
Stefanos Kornilios Mitsis Poiitidis fba626c547 Merge pull request #790 from FEX-Emu/skmp/workaround-exit-group
Threading: Workaround exit_group bug
2021-02-22 17:50:59 +02:00
Stefanos Kornilios Mitsis Poiitidis 9b360e66dc Threading: Workaround exit_group bug 2021-02-22 14:24:42 +02:00
Stefanos Kornilios Mitsis Poiitidis 3a6fd00154 Merge pull request #785 from FEX-Emu/skmp/fix-sar8-16
OpDisp: Imm SAR OpSize < 32 needs sign extension
2021-02-18 22:33:46 +02:00
Stefanos Kornilios Mitsis Poiitidis 3bc12de017 OpDisp: Imm SAR OpSize < 32 needs sign extension 2021-02-17 08:17:20 +02:00
Stefanos Kornilios Mitsis Poiitidis 5ae6a64800 Merge pull request #778 from FEX-Emu/skmp/x87-round-trunc
x87: Round, Truncate & Precision control
2021-02-17 05:27:56 +02:00
Stefanos Kornilios Mitsis Poiitidis 6615d5972f x87: Support for FCW, rounding, truncation, precision control 2021-02-17 05:16:53 +02:00
Stefanos Kornilios Mitsis Poiitidis d075b689a5 Merge pull request #784 from FEX-Emu/skmp/fix-cvt-cvtt
OpDisp: Fix cvt/cvtt mapping to dispatcher handlers
2021-02-17 05:14:44 +02:00
Stefanos Kornilios Mitsis Poiitidis eb60bda1d3 OpDisp: Fix cvt/cvtt mapping to dispatcher handlers 2021-02-16 11:19:28 +02:00
Stefanos Kornilios Mitsis Poiitidis 8c5d88816f Merge pull request #781 from FEX-Emu/skmp/hackfix-frem
X87: FPREM needs to set C2 to 0 to indicate finished iteration
2021-02-16 09:54:37 +02:00
Ryan Houdek 73e9a35e36 Merge pull request #783 from FEX-Emu/skmp/fix-selects
Syscalls/x86-32: Fix selects to remarshal the fd_sets
2021-02-15 23:26:33 -08:00
Stefanos Kornilios Mitsis Poiitidis 568fb3643d Syscalls/x86-32: Fix selects to actually remarshal the sets after running the host syscall 2021-02-16 02:35:38 +02:00
Stefanos Kornilios Mitsis Poiitidis 82367f49e2 X87: FPERM needs to set C2 to 0 to indicate finished iteration 2021-02-16 02:34:52 +02:00
Stefanos Kornilios Mitsis Poiitidis 9f2d49026e Merge pull request #768 from FEX-Emu/skmp/fix-csgo
Fixes for Counter Strike Global Offensive
2021-02-15 03:47:14 +02:00
Stefanos Kornilios Mitsis Poiitidis bed00d11f1 Tests: Disable pr57275.c because it uses VMOVAPS 2021-02-14 21:36:56 +02:00
Stefanos Kornilios Mitsis Poiitidis 9f0a3dab87 Merge pull request #769 from FEX-Emu/skmp/jit-fallbacks
x87: Call C++ handlers instead of forcing interpreter
2021-02-14 21:27:32 +02:00
Stefanos Kornilios Mitsis Poiitidis 07bd2244c7 IR: Remove ShouldInterpret & related logic 2021-02-12 14:21:47 +02:00
Stefanos Kornilios Mitsis Poiitidis 077093eacc JIT/x64: Use xmm0 as temp instead of xmm11, add xmm11 to RA list 2021-02-12 14:15:22 +02:00
Stefanos Kornilios Mitsis Poiitidis 428111b44b JIT/arm64: fallback support 2021-02-12 01:23:15 +02:00
Stefanos Kornilios Mitsis Poiitidis b564927825 IprOps: Add BCDLOAD, BCDSTORE fallbacks 2021-02-11 19:24:57 +02:00
Stefanos Kornilios Mitsis Poiitidis b0a046dfcb JIT/IPR: Add fallbacks for almost all x87 ops 2021-02-11 18:52:05 +02:00
Stefanos Kornilios Mitsis Poiitidis 8f5bbde4dd JIT/x86: Implement VBSL 2021-02-11 18:51:05 +02:00
Stefanos Kornilios Mitsis Poiitidis 7dc0c275cf Add some basic infrastructure + a few fpr ops 2021-02-11 17:20:29 +02:00
Stefanos Kornilios Mitsis Poiitidis 4e31c1d1ce RA: Remove erratic assert 2021-02-11 14:31:53 +02:00
Stefanos Kornilios Mitsis Poiitidis df1c38908c JIT: Support edge case in CreateElementPair 2021-02-11 14:31:32 +02:00
Stefanos Kornilios Mitsis Poiitidis fc4b875ff1 Frontend: Only return error if invalid op in first block 2021-02-11 14:31:02 +02:00
Ryan Houdek 40b891ca69 Merge pull request #766 from phire/brk_fix
BRK: don't munmap if mmap failed
2021-02-08 21:32:02 -08:00
Scott Mansell 3d6461f0ca BRK: don't munmap if mmap failed 2021-02-09 17:29:44 +13:00
Ryan Houdek c11b629189 Merge pull request #764 from phire/fix_cpuinfo
Fix the number of physical cpus reported
2021-02-08 16:13:09 -08:00
Scott Mansell 46ec7c406f emulated-cpuid: add some comments 2021-02-09 13:03:49 +13:00
Ryan Houdek 2a0b9ae681 Merge pull request #763 from phire/bigger_spills
arm64: Fix blocks with over 256 spill slots.
2021-02-08 15:57:15 -08:00
Scott Mansell a28acf5505 Fix the number of physical cpus reported. 2021-02-09 12:52:27 +13:00
Scott Mansell c4dda4a3f3 arm64: Fix blocks with over 256 spill slots.
With multiblock and -n4000, at least one block in geekbench5's camera
benchmark was spilling 324 values. This meant the required stack
adjustment was 5184 bytes, which is larger than can fit into a 12bit
immediate.

Now, it's probally a bug that our RA is spilling that many values, and
we probally should be packing our spill slots closer together, but we
still shouldn't fail to run in this edge case.

So this commit adds a fallback path which uses a temp register to load
the stack adjustment.
2021-02-09 12:08:32 +13:00
Stefanos Kornilios Mitsis Poiitidis b40131ede3 Merge pull request #759 from phire/fix_mul_op
Fix power of 2 OP_MUL optimisation (Fixes Geekbench result upload)
2021-02-07 08:08:37 +02:00
Scott Mansell 5b0ade0bd8 Fix power of 2 OP_MUL optimisation
The new left-shift that replaced the multiply takes
it's size from arg[0]. If arg[0] was 64bit and the original
OP_MUL was 32bit, then the original code would truncate
upper bits, and the replacement code wouldn't.

This bug broke the SSL cert checking code in geekbench,
causing it to fail to upload results.
Fixes #647
2021-02-07 18:49:47 +13:00
Stefanos Kornilios Mitsis Poiitidis fcf1c2572b Merge pull request #756 from Sonicadvance1/python_check
Adds python version check
2021-02-06 14:20:49 +02:00
Ryan Houdek 69891867b1 Adds python version check 2021-02-05 10:36:52 -08:00
Ryan Houdek abefae2b58 Merge pull request #754 from Sonicadvance1/fix_artifacts_on_failure
Always upload artifact results even on failure
2021-02-03 16:08:51 -08:00
Ryan Houdek 143cb6d0a5 Always upload artifact results even on failure
This was an oversight of the original commit
2021-02-03 15:59:55 -08:00
Ryan Houdek e0042cc3fc Merge pull request #752 from Sonicadvance1/upload_artifacts
Uploads testing artifacts to github
2021-02-03 09:40:57 -08:00
Ryan Houdek 6ccdfd11a3 Uploads testing artifacts to github 2021-02-02 06:56:06 -08:00
Ryan Houdek 0aaed41f72 Merge pull request #751 from FEX-Emu/skmp/fix-fchmodat-faccessat
Syscalls: chmodat, faccessat glibc interface differs from kernel
2021-02-02 05:52:38 -08:00
Stefanos Kornilios Mitsis Poiitidis 3eed439728 gvisor: chmod passes now 2021-02-02 14:58:51 +02:00
Stefanos Kornilios Mitsis Poiitidis 4d05438796 Merge pull request #747 from phire/android_build_fixes
Android build fixes
2021-02-02 14:40:34 +02:00
Stefanos Kornilios Mitsis Poiitidis f9aa457e3f Syscalls: chmodat, faccessat glibc interface differs from kernel 2021-02-02 14:35:27 +02:00
Ryan Houdek eea14690c4 Merge pull request #750 from FEX-Emu/skmp/fix-empty-arg-forwarding
HarnessHelpers: Fix empty arg forwarding
2021-02-02 04:30:47 -08:00
Stefanos Kornilios Mitsis Poiitidis a3b5234d1c HarnessHelpers: Fix empty arg forwarding 2021-02-02 14:19:23 +02:00
Scott Mansell 4fad9a8c0b Assert on unreachable 2021-02-02 17:12:43 +13:00
Scott Mansell 9fa108ff33 Move -lnuma to correct place 2021-02-02 16:47:55 +13:00
Scott Mansell 45962098ef Refactor LinuxSyscalls to it's own static library 2021-02-02 16:47:55 +13:00
Scott Mansell 622c437829 undef PAGE_SIZE if it's defined
The android NDK defines this.
2021-02-02 16:47:55 +13:00
Scott Mansell 89bfacd845 Don't build thunks by default
Allow people to build fex without a cross-compiler installed
2021-02-02 16:47:55 +13:00
Scott Mansell 16e7ba6fa8 Move nasm check inside unit tests
Allow building of fex without nasm if unit tests are disabled
2021-02-02 16:47:55 +13:00
Scott Mansell c3ab5bca5e Fix <elf.h> incompatibilities
ELF32_ST_VISIBILITY is a pretty simple macro, just reimplement it.
2021-02-02 16:47:44 +13:00
Scott Mansell ef043f10ed Remove std::unexpected
It's deprecated in c++17, and android's bionic has removed it
2021-02-01 21:08:45 +13:00
Scott Mansell 0ff39ccad2 Remove dependance on system sigset_t
Android's sigset_t is the wrong size. Just define our own one.
2021-02-01 21:08:45 +13:00
Scott Mansell 93d5cdde1f Fix missing libc++ includes
gnu's libc++ is lenient and allows you to miss includes at times.

Android's binoic is more strict and needs these fixes to build.
2021-02-01 21:08:45 +13:00
Ryan Houdek 68beb28a56 Merge pull request #748 from phire/disable-more-tests
Disable more flaky gvisor tests
2021-01-31 21:51:07 -08:00
Scott Mansell ba3bacb65b Disable more flaky gvisor tests
They assume fair scheduling
2021-02-01 18:43:35 +13:00
Scott Mansell ebb05b56e4 Merge pull request #739 from Sonicadvance1/remove_warnings_10
Removes warning from Static Register Allocation Pass
2021-02-01 17:31:39 +13:00
Scott Mansell f5dd0fea5d Merge pull request #742 from Sonicadvance1/remove_warnings_8
Removes warning from IRCompaction
2021-02-01 17:31:28 +13:00
Ryan Houdek 4f4a8f7440 Merge pull request #746 from FEX-Emu/phire-disable-mcount-pic
Disable `mcount_pic.c.gcc-target-test-64` test
2021-01-31 19:50:45 -08:00
Scott Mansell 2e5f59cdc1 Merge pull request #734 from Sonicadvance1/remove_warnings_4
Removes warnings from OpcodeDispatcher
2021-02-01 16:45:37 +13:00
Scott Mansell 767813eb97 Merge pull request #735 from Sonicadvance1/remove_warnings_5
Removes warning from X86Tables
2021-02-01 16:44:24 +13:00
Scott Mansell 9c81fbb2fd Merge pull request #736 from Sonicadvance1/remove_warnings_6
Removes warning from IRDumper
2021-02-01 16:42:52 +13:00
Scott Mansell 5f624bec05 Merge pull request #733 from Sonicadvance1/remove_warnings_3
Removes warnings from x86-64 JIT.cpp
2021-02-01 16:42:10 +13:00
Scott Mansell a156210064 Merge pull request #738 from Sonicadvance1/remove_warnings_9
Removes warnings from Register Allocation Pass
2021-02-01 16:32:05 +13:00
Scott Mansell 03db1b2040 Disable mcount_pic.c.gcc-target-test-64 test
It's flaky on the ARMv8.0 runner
2021-02-01 16:26:39 +13:00
Scott Mansell d4fbca3cbf Merge pull request #732 from Sonicadvance1/remove_warnings_2
Removes warnings from Frontend
2021-02-01 16:23:29 +13:00
Scott Mansell 687176eca6 Merge pull request #741 from Sonicadvance1/remove_warnings_12
Removes warnings from HostFactory
2021-02-01 16:23:11 +13:00
Scott Mansell c14dfb9d52 Merge pull request #737 from Sonicadvance1/remove_warnings_7
Removes warning from DeadStoreElimination
2021-02-01 16:15:12 +13:00
Scott Mansell 27adabef83 Merge pull request #740 from Sonicadvance1/remove_warnings_11
Removes warnings from ELFLoader
2021-02-01 16:14:43 +13:00
Scott Mansell 1d102b9e59 Merge pull request #731 from Sonicadvance1/remove_warnings_1
Removes warnings from SoftFloat-3e
2021-02-01 16:08:42 +13:00
Ryan Houdek 1b1e922830 Removes warnings from HostFactory 2021-01-31 08:01:53 -08:00
Ryan Houdek 436e6e96ce Removes warnings from ELFLoader 2021-01-31 08:01:25 -08:00
Ryan Houdek d1161de4e4 Removes warning from Static Register Allocation Pass 2021-01-31 08:00:58 -08:00
Ryan Houdek 0c3ddd3016 Removes warnings from Register Allocation Pass 2021-01-31 08:00:28 -08:00
Ryan Houdek 5c9848650c Removes warning from IRCompaction 2021-01-31 07:59:59 -08:00
Ryan Houdek a90e2268f9 Removes warning from DeadStoreElimination 2021-01-31 07:59:26 -08:00
Ryan Houdek 175078b739 Removes warning from IRDumper 2021-01-31 07:58:58 -08:00
Ryan Houdek 9e55f1b177 Removes warning from X86Tables 2021-01-31 07:58:08 -08:00
Ryan Houdek 6012fcfee6 Removes warnings from OpcodeDispatcher 2021-01-31 07:57:28 -08:00
Ryan Houdek c2f4ea4646 Removes warnings from x86-64 JIT.cpp 2021-01-31 07:56:19 -08:00
Ryan Houdek 1f6017c37c Removes warnings from Frontend 2021-01-31 07:55:26 -08:00
Ryan Houdek 47a6b49f7f Removes warnings from SoftFloat-3e 2021-01-31 07:54:47 -08:00
Ryan Houdek fc7e0ede03 Merge pull request #728 from FEX-Emu/skmp/lookupcache-hotfix
LookupCache: Fix crash on L2 insertion
2021-01-29 17:14:29 -08:00
Stefanos Kornilios Mitsis Poiitidis 1f7d815f3a LookupCache: Fix crash on L2 insertion 2021-01-29 22:00:07 +02:00
Ryan Houdek 2edb14538f Merge pull request #725 from FEX-Emu/skmp/single-compiler-backend
Core: Single compiler backend
2021-01-29 05:30:47 -08:00
Stefanos Kornilios Mitsis Poiitidis 9ecde7a0b8 Interpreter: Move execution to IntepreterOps::ExecuteIR, single core refactor 2021-01-29 15:17:37 +02:00
Ryan Houdek f23fe030a1 Merge pull request #722 from FEX-Emu/skmp/ra-data
RA: Split RAData from RAPass, Optimize data packing, Cleanups
2021-01-29 05:16:18 -08:00
Stefanos Kornilios Mitsis Poiitidis 972219043d Core: Refactor compilation driver, lifetime of objects 2021-01-29 10:42:55 +02:00
Stefanos Kornilios Mitsis Poiitidis 0e00d3dc15 RA: Optimize RAData 2021-01-29 10:42:48 +02:00
Stefanos Kornilios Mitsis Poiitidis a45c2c34e9 RA: Use RAData directly 2021-01-29 10:42:40 +02:00
Stefanos Kornilios Mitsis Poiitidis 513112919b RA: Move allocation data to RAData 2021-01-29 10:42:40 +02:00
Ryan Houdek 810143d304 Merge pull request #723 from FEX-Emu/skmp/lookup-cache-refactor
BlockCache -> LookupCache, L3C
2021-01-29 00:22:27 -08:00
Ryan Houdek 3973449245 Merge pull request #721 from FEX-Emu/skmp/compile-fix
Syscalls: Define MREMAP_DONTUNMAP if missing
2021-01-29 00:18:15 -08:00
Stefanos Kornilios Mitsis Poiitidis 791aa4c32c LookupCache: Add a third level cache that will always resolve all known blocks 2021-01-29 01:53:12 +02:00
Stefanos Kornilios Mitsis Poiitidis 0c9e05ed8f Rename BlockCache to LookupCache 2021-01-29 01:31:48 +02:00
Stefanos Kornilios Mitsis Poiitidis d6ee3caad4 Syscalls: Define MREMAP_DONTUNMAP if missing 2021-01-28 17:09:08 +02:00
Ryan Houdek ded559139c Merge pull request #717 from Sonicadvance1/32bit_allocator
Finishes 32bit memory allocator
2021-01-27 11:00:46 -08:00
Ryan Houdek a98eaa6a27 Finishes 32bit memory allocator
This was partially implemented before but it had some bugs still.
This fixes those bugs and also implements mremap, shmat, and shmdt.

This now works well enough for 32bit graphical applications
2021-01-27 10:49:28 -08:00
Ryan Houdek 2d8b70bbd5 Merge pull request #710 from Sonicadvance1/brk_improvements
More improvements to BRK handling
2021-01-27 10:49:07 -08:00
Stefanos Kornilios Mitsis Poiitidis cc1000751a Merge pull request #719 from Sonicadvance1/ioctl32
Implements ioctl32 for x86-64
2021-01-27 20:18:11 +02:00
Stefanos Kornilios Mitsis Poiitidis f872e178ac Merge pull request #718 from Sonicadvance1/32bit_sigret_handler
Forces SIGRET handler to the lower 32bits VA on 32bit processes
2021-01-27 20:16:51 +02:00
Ryan Houdek a5d9e62cc6 More improvements to BRK handling
Base size is now only one page in size. We will then increment that BRK
size by 8MB alignments. 256MB for 32bit applications was causing some
applications on the edge to run out of virtual memory
I was hitting some 32bit applications that were being fairly mean with
BRK. They were allocating all of BRK space then running out of virtual
memory space with its mmap handler fallback after freeing BRK space.

This means we now munmap BRK pages on release for the guest, similar to
behaviour that Linux does.

Additionally I had an application that was getting very upset that BRK
wasn't actually at the end of program space. So allocate at the end of
program space like expected.

brk test now passes from gvisor
2021-01-27 10:12:25 -08:00
Stefanos Kornilios Mitsis Poiitidis ec31dc0872 Merge pull request #716 from Sonicadvance1/describe_tag
Puts the FEX describe tag in CPUID
2021-01-27 20:10:46 +02:00
Stefanos Kornilios Mitsis Poiitidis 4c4b797f51 Merge pull request #715 from Sonicadvance1/fix_vixl_asserts
Fixes some asserts from vixl and a bug in ExitSpillSRA
2021-01-27 20:09:33 +02:00
Ryan Houdek 15df787710 Implements ioctl32 for x86-64
If you have the COMPAT feature enabled then this just works there.
AArch64 not implemented currently since changes aren't upstream
2021-01-27 08:02:49 -08:00
Ryan Houdek 2437b89c7b Forces SIGRET handler to the lower 32bits VA on 32bit processes
This is visible to the guest process so for 32bit it needs to be in the
lower 32bits.
2021-01-27 07:54:30 -08:00
Ryan Houdek 5673a0e213 Puts the FEX describe tag in CPUID
This is what shows up in /proc/cpuinfo

example on tagged release:   'model name      : FEX-2101'
example on untagged release: 'model name      : FEX-2101-64-g536be23f'

Fixes #661
2021-01-27 07:20:14 -08:00
Ryan Houdek 3df344a78d Fixes some asserts from vixl and a bug in ExitSpillSRA
Assembler by default doesn't allow you to use the assembler. Needs a
CodeBufferCheckScope.

Labels with binds and not links will throw an assert.
ExitSpillSRA was using b instead of bind incorrectly.
Thus branching to a invalid location
2021-01-27 00:53:27 -08:00
Ryan Houdek f58d018934 Enables vixl asserts when in debug mode globally
Fixes an issue where some header asserts were missed
2021-01-27 00:52:37 -08:00
Ryan Houdek 536be23f68 Merge pull request #704 from FEX-Emu/skmp/add-gcc-target-tests
Add gcc target tests
2021-01-26 15:45:12 -08:00
Stefanos Kornilios Mitsis Poiitidis 8ff0b870b1 Merge pull request #712 from Sonicadvance1/fix_timers
Fixes timer syscall API
2021-01-26 15:33:00 +02:00
Ryan Houdek 3679156dd7 Fixes timer syscall API
The glibc and kernel timer interfaces are sufficiently different enough
that unwrapping this through glibc is impossible.
Pass it directly through to the kernel.
2021-01-26 05:02:44 -08:00
Stefanos Kornilios Mitsis Poiitidis 10e398a540 Merge pull request #711 from Sonicadvance1/implement_vbsl_aarch64
Implements VBSL on AArch64
2021-01-26 14:56:22 +02:00
Stefanos Kornilios Mitsis Poiitidis b453ede9ce Tests: Disable gcc target tests that fail only on arm 2021-01-26 14:55:35 +02:00
Ryan Houdek 6bca290611 Implements VBSL on AArch64
This isn't quite optimal without RA constraints
2021-01-26 03:01:48 -08:00
Stefanos Kornilios Mitsis Poiitidis dbfbfe5f0e Merge pull request #709 from Sonicadvance1/remove_boost
Removes all references to Boost
2021-01-26 12:40:43 +02:00
Stefanos Kornilios Mitsis Poiitidis 25bfab25da CI: Disable gcc target tests for x86-32 2021-01-26 12:37:07 +02:00
Ryan Houdek cab317e2f0 Removes all references to Boost
We don't require this
Fixes #699
2021-01-26 02:34:26 -08:00
Ryan Houdek 4d98c890e7 Merge pull request #708 from Sonicadvance1/decode_tsx
Add TSX names to decode tables so we know what they are
2021-01-26 02:24:47 -08:00
Ryan Houdek 513cb27825 Add TSX names to decode tables so we know what they are
Rather than just stating UND

We can't support this until we have an ARM CPU that supports TME and
more robust multiblock.
2021-01-26 02:03:42 -08:00
Stefanos Kornilios Mitsis Poiitidis 29a8fd0a4a Tests: Add gcc-target-tests for 32 and 64 bit 2021-01-26 12:00:45 +02:00
Stefanos Kornilios Mitsis Poiitidis e466dfb480 Merge pull request #707 from Sonicadvance1/update_gvisor_tests
Updates gvisor test results that have changed due to OS update
2021-01-26 11:59:38 +02:00
Ryan Houdek 8cbfef2e74 Updates gvisor test results that have changed due to OS update 2021-01-26 01:11:40 -08:00
Stefanos Kornilios Mitsis Poiitidis 53582fdf47 Merge pull request #701 from FEX-Emu/skmp/opdisp-32-bit-fixes
OpDisp: Address most 32-bit rip size issues
2021-01-25 15:31:55 +02:00
Stefanos Kornilios Mitsis Poiitidis b0365871b6 OpDisp: Address most 32-bit rip size issues 2021-01-25 15:04:12 +02:00
Ryan Houdek 2721418fb7 Merge pull request #698 from phire/better_defaults
Better Default Configuration
2021-01-22 16:56:29 -08:00
Scott Mansell 3cae0a0932 Better Default Configuration
Make FEX have good performance out of the box

resolves #685
2021-01-23 13:47:53 +13:00
Ryan Houdek d4ffea5c31 Merge pull request #670 from FEX-Emu/skmp/faster-ra
IR: Optimize runtime of optimization passes
2021-01-22 04:54:08 -08:00
Stefanos Kornilios Mitsis Poiitidis 0546da7a75 Address review feedback 2021-01-22 11:01:36 +02:00
Ryan Houdek ce1b9fcdc9 Merge pull request #674 from FEX-Emu/skmp/split-irparser
IR: Split parser from IRLoader, add IR::Parse(), Fixes, Tests
2021-01-21 23:09:53 -08:00
Stefanos Kornilios Mitsis Poiitidis 8b025d4c26 IRLoader: Actually set EntryRIP if the IR was parsed 2021-01-21 22:19:16 +02:00
Stefanos Kornilios Mitsis Poiitidis e7f2a650b5 Fix compilation 2021-01-21 22:18:48 +02:00
Stefanos Kornilios Mitsis Poiitidis 3bd8731b26 IR: Add CONFIG_VALIDATE_IR_PARSER, enable it on TestHarnessRunner 2021-01-21 21:38:41 +02:00
Stefanos Kornilios Mitsis Poiitidis 13613c6de1 IR: Change ValidateCode to use two uint64_t instead of __uint128_t 2021-01-21 21:37:58 +02:00
Stefanos Kornilios Mitsis Poiitidis 1177c375ff IR: Further parser cleanups, add fence types 2021-01-21 21:17:27 +02:00
Stefanos Kornilios Mitsis Poiitidis fb57e5183a IR: Fix TypeDefinition to actually fit used vector sizes 2021-01-21 21:10:11 +02:00
Stefanos Kornilios Mitsis Poiitidis 74160f38c4 IR: Fix IREmitter::Copy 2021-01-21 21:01:27 +02:00
Stefanos Kornilios Mitsis Poiitidis 072067b5fd IR: Split parser from IRLoader, add IR::Parse() 2021-01-21 19:17:51 +02:00
Stefanos Kornilios Mitsis Poiitidis 773e5866b4 Merge pull request #666 from Sonicadvance1/handle_unaligned_caspal32
Handle unaligned cmpxchg and cmpxchg8b on ARMv8.1+
2021-01-21 13:30:18 +02:00
Stefanos Kornilios Mitsis Poiitidis 00ca576644 IRCompaction: Only memset in debug 2021-01-21 13:06:43 +02:00
Ryan Houdek 5226f779c3 Merge pull request #673 from FEX-Emu/skmp/fix-ir-printing
IR: Fix Printing and Parsing for float conds
2021-01-21 00:28:50 -08:00
Stefanos Kornilios Mitsis Poiitidis 036771d825 IR: Fix Printing and Parsing for float conds 2021-01-21 10:16:08 +02:00
Ryan Houdek 633ea045e5 Handles unaligned cmpxchg/cmpxchg8b in SIGBUS handler
cmpxchg/cmpxchg8b doesn't have alignment requirements on x86, which means
applications rely on unaligned behaviour support with it.

Steam relies on this to work for a linked list array of jobs in some
internal job queueing system. It will end up always aligning by offsets
of 4 since it stores a couple of pointers.

Doesn't currently support the case of unaligned cmpxchg8b crossing a
cacheline, which ends up being semi-broken depending on which x86
behaviour the application is expected.
Intel CPUs do the "Big ring lock" or "split locks". Which means accesses
across cachelines are atomic.
AMD CPUs will tear the value across the cacheline, which is expected x86
behaviour by spec.

If they are expecting Intel behaviour, then that application is just
broken on non-Intel platforms unless they are fine with a tear.
2021-01-20 16:07:50 -08:00
Stefanos Kornilios Mitsis Poiitidis 0e7db64ac7 RA: Run compation after spilling, not before 2021-01-21 01:01:02 +02:00
Stefanos Kornilios Mitsis Poiitidis ac1036ac92 IR: Sync Invalid class with RA 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 9c462eb400 ConsProp: Revert to ordered set for identical codegen 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 8aef9cb6a7 RA: Exit after first spill per iteration is found 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis d3fc85e18d RA: Make spans at least 1 offset long 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 9f3ce47917 RA: Expire ending intervals before starting new ones 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis a9332e604a JIT/x64: Fix VAddV 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis e7e6b6654f RA: Cleanups 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis c3682f9a3b RA: Fix several bugs, get rid of virtual registers, remove unused complexity 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 47ee9af96c ConstProp: Keep pools in heap 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 10b6fb02c4 RA: Rename CalculatePrecessors to CalculatePredecessors 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 9c760ff83b RA: Get OP_JUMP/CONDJUMP without loop in CalculatePrecessors 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis f3e19ef015 DSE: No need to loop to find branching op 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 6bdf785223 DSE: Optimize map lookups 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 997650bafc DSE: Merge logic more for perf 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 56ca73191d DSE: Merge Flag/GPR/FPR passes for perf 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 900fc6d2a7 ConstProp: Switch maps to unordred_maps 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis af94e24ba3 RA: Simplify and optimize data structures 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 6380ab6bfd RA: Optimize register conflict handling 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 91ddc1dd38 RCLSE: Optimize FindMemberInfo to be O(1) 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis 77d072e35c RA: Switch active set to also use BucketList 2021-01-20 04:26:06 +02:00
Stefanos Kornilios Mitsis Poiitidis e847f94b69 RA: rework ConstrainedRAPass::CalculateNodeInterference 2021-01-20 04:26:06 +02:00
Ryan Houdek eb6386b921 Merge pull request #671 from FEX-Emu/skmp/ir-prettyprint
IR: Pretty print %Invalid, don't print invalid regs
2021-01-19 18:17:43 -08:00
Stefanos Kornilios Mitsis Poiitidis c42c57af35 IR: Pretty print %Invalid, don't print invalid regs 2021-01-20 04:06:37 +02:00
Stefanos Kornilios Mitsis Poiitidis 814ca4746b Merge pull request #669 from Sonicadvance1/workaround_llvm_native
Works around Clang failing to identify new Kryo CPUs
2021-01-20 03:48:55 +02:00
Ryan Houdek 921867de7e Works around Clang failing to identify new Kryo CPUs
Some of the newer CPU cores in LLVM's source claim to be a Cortex-A73,
which means they become limited to an ARMv8.0 feature set.

This is what you get if you compile FEX with -mcpu=native

To work around this issue, manually parse /proc/cpuinfo ourselves and
pull out the CPU type to pass to clang directly.
This also fixes the issue that we were using -march on AArch64, which no
longer works on newer clang versions. We instead need to use mcpu or
mtune.

Should improve all atomic op performance outside of the JITs, where they
were turning in to loadstore exclusive pairs.
2021-01-19 03:21:10 -08:00
Ryan Houdek fa542a5b9d Merge pull request #665 from FEX-Emu/skmp/continue-on-invalid-insn
Frontend, OpDisp: Don't assert on invalid instructions
2021-01-18 07:32:31 -08:00
Stefanos Kornilios Mitsis Poiitidis 567e315854 Frontend, OpDisp: Don't assert on invalid instructions 2021-01-18 17:19:11 +02:00
Stefanos Kornilios Mitsis Poiitidis 8ad32a135f Merge pull request #660 from FEX-Emu/skmp/remove-debug-printfs
OpDisp: Remove missed debug printf
2021-01-18 11:32:30 +02:00
Stefanos Kornilios Mitsis Poiitidis aa0cd76e06 OpDisp: Remove missed debug printf 2021-01-18 10:03:31 +02:00
354 changed files with 23567 additions and 12977 deletions

No files matched your search

+72 -1
View File
@@ -25,6 +25,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set runner label
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
- name : submodule checkout
# Need to update submodules
run: git submodule update --init --depth 1
@@ -45,7 +48,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
- name: Build
working-directory: ${{runner.workspace}}/build
@@ -59,20 +62,88 @@ jobs:
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: IR Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target ir_tests
- name: IR Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_IR.log || true
- name: Posix Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the posixtest
run: cmake --build . --config $BUILD_TYPE --target posix_tests
- name: Posix Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_Posix.log || true
- name: gvisor tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the gvisor tests
run: cmake --build . --config $BUILD_TYPE --target gvisor_tests
- name: GVisor Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GVisor.log || true
- name: gcc target tests 64
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the gvisor tests
run: cmake --build . --config $BUILD_TYPE --target gcc_target_tests_64
- name: GCC64 Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GCC64.log || true
- name: Struct verifier tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target struct_verifier
- name: Struct verifier Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_StructVerifier.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
# Cap out the log files at 20M in case something crash spins and dumps fault text
# ASM tests get quite close to 10MB
run: truncate --size=<20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
- name: Set runner name
if: ${{ always() }}
run: echo "runner_name=$(hostname)" >> $GITHUB_ENV
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v2'
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
retention-days: 3
+4
View File
@@ -26,3 +26,7 @@
shallow = true
path = External/fex-gvisor-tests-bins
url = https://github.com/FEX-Emu/fex-gvisor-tests-bins.git
[submodule "External/fex-gcc-target-tests-bins"]
shallow = true
path = External/fex-gcc-target-tests-bins
url = https://github.com/FEX-Emu/fex-gcc-target-tests-bins.git
+146 -29
View File
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.14)
project(FEX)
option(BUILD_TESTS "Build unit tests to ensure sanity" TRUE)
option(BUILD_THUNKS "Build thunks" FALSE)
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
option(ENABLE_IWYU "Enables include what you use program" FALSE)
option(ENABLE_LTO "Enable LTO with compilation" TRUE)
@@ -11,6 +12,8 @@ option(ENABLE_ASAN "Enables Clang ASAN" FALSE)
option(ENABLE_TSAN "Enables Clang TSAN" FALSE)
option(ENABLE_ASSERTIONS "Enables assertions in build" FALSE)
option(ENABLE_VISUAL_DEBUGGER "Enables the visual debugger for compiling" FALSE)
option(ENABLE_STRICT_WERROR "Enables stricter -Werror for CI" FALSE)
option(ENABLE_WERROR "Enables -Werror" FALSE)
set (X86_C_COMPILER "x86_64-linux-gnu-gcc" CACHE STRING "c compiler for compiling x86 guest libs")
set (X86_CXX_COMPILER "x86_64-linux-gnu-g++" CACHE STRING "c++ compiler for compiling x86 guest libs")
@@ -26,14 +29,6 @@ if (ENABLE_ASSERTIONS)
add_definitions(-DASSERTIONS_ENABLED=1)
endif()
if (ENABLE_IWYU)
find_program(IWYU_EXE "iwyu")
if (IWYU_EXE)
message(STATUS "IWYU enabled")
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXE}")
endif()
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Bin)
@@ -82,6 +77,14 @@ set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer")
set (CMAKE_LINKER_FLAGS_RELEASE "${CMAKE_LINKER_FLAGS_RELEASE} -fomit-frame-pointer")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
option(ENABLE_X86_HOST_DEBUG "Enables compiling on x86_64 host" FALSE)
if (NOT ENABLE_X86_HOST_DEBUG)
message(FATAL_ERROR
" Be warned: FEX isn't optimized for x86_64 hosts!\n"
" Support for x86_64 hosts is only for debugging and convenience!\n"
" Don't expect amazing performance or optimal code generation!\n"
" Pass -DENABLE_X86_HOST_DEBUG=True to bypass this message!")
endif()
set(_M_X86_64 1)
add_definitions(-D_M_X86_64=1)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
@@ -90,15 +93,18 @@ endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(_M_ARM_64 1)
add_definitions(-D_M_ARM_64=1)
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
endif()
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# This means we were attempted to get compiled with GCC
message(FATAL_ERROR "FEX doesn't support getting compiled with GCC!")
endif()
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter)
add_definitions(-Wno-trigraphs)
add_definitions(-DGLOBAL_DATA_DIRECTORY="${DATA_DIRECTORY}/")
@@ -143,34 +149,145 @@ if(COMPILER_SUPPORTS_MARCH_NATIVE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
if(ENABLE_WERROR OR ENABLE_STRICT_WERROR)
add_compile_options(-Werror)
if (NOT ENABLE_STRICT_WERROR)
# Disable some Werror that can add frustration when developing
add_compile_options(-Wno-error=unused-variable)
endif()
endif()
if(_M_ARM_64)
# Due to an oversight in llvm, it declares any reasonably new Kryo CPU to only be ARMv8.0
# Manually detect newer CPU revisions until clang and llvm fixes their bug
# This script will either provide a supported CPU or 'native'
# Additionally -march doesn't work under AArch64+Clang, so you have to use -mcpu or -mtune
execute_process(COMMAND python3 "${PROJECT_SOURCE_DIR}/Scripts/aarch64_fit_native.py" "/proc/cpuinfo"
OUTPUT_VARIABLE AARCH64_CPU)
string(STRIP ${AARCH64_CPU} AARCH64_CPU)
check_cxx_compiler_flag("-mcpu=${AARCH64_CPU}" COMPILER_SUPPORTS_CPU_TYPE)
if(COMPILER_SUPPORTS_CPU_TYPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${AARCH64_CPU}")
endif()
endif()
if (ENABLE_IWYU)
find_program(IWYU_EXE "iwyu")
if (IWYU_EXE)
message(STATUS "IWYU enabled")
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXE}")
endif()
endif()
if (ENABLE_CLANG_FORMAT)
find_program(CLANG_TIDY_EXE "clang-tidy")
if (NOT CLANG_TIDY_EXE)
message(FATAL_ERROR "Couldn't find clang-tidy")
endif()
set(CLANG_TIDY_FLAGS
"-checks=*"
"-fuchsia*"
"-bugprone-macro-parentheses"
"-clang-analyzer-core.*"
"-cppcoreguidelines-pro-type-*"
"-cppcoreguidelines-pro-bounds-array-to-pointer-decay"
"-cppcoreguidelines-pro-bounds-pointer-arithmetic"
"-cppcoreguidelines-avoid-c-arrays"
"-cppcoreguidelines-avoid-magic-numbers"
"-cppcoreguidelines-pro-bounds-constant-array-index"
"-cppcoreguidelines-no-malloc"
"-cppcoreguidelines-special-member-functions"
"-cppcoreguidelines-owning-memory"
"-cppcoreguidelines-macro-usage"
"-cppcoreguidelines-avoid-goto"
"-google-readability-function-size"
"-google-readability-namespace-comments"
"-google-readability-braces-around-statements"
"-google-build-using-namespace"
"-hicpp-*"
"-llvm-namespace-comment"
"-llvm-include-order" # Messes up with case sensitivity
"-llvmlibc-*"
"-misc-unused-parameters"
"-modernize-loop-convert"
"-modernize-use-auto"
"-modernize-avoid-c-arrays"
"-modernize-use-nodiscard"
"readability-*"
"-readability-function-size"
"-readability-implicit-bool-conversion"
"-readability-braces-around-statements"
"-readability-else-after-return"
"-readability-magic-numbers"
"-readability-named-parameter"
"-readability-uppercase-literal-suffix"
"-cert-err34-c"
"-cert-err58-cpp"
"-bugprone-exception-escape"
)
string(REPLACE ";" "," CLANG_TIDY_FLAGS "${CLANG_TIDY_FLAGS}")
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} "${CLANG_TIDY_FLAGS}")
endif()
add_compile_options(-Wall)
add_subdirectory(External/FEXCore)
add_subdirectory(Source/)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/Config.h.in
${CMAKE_BINARY_DIR}/generated/Config.h)
if (BUILD_TESTS)
include(CTest)
enable_testing()
message(STATUS "Unit tests are enabled")
endif()
add_subdirectory(External/FEXCore)
add_subdirectory(Source/)
add_subdirectory(Data/AppConfig/)
if (BUILD_TESTS)
add_subdirectory(unittests/)
endif()
include(ExternalProject)
if (BUILD_THUNKS)
include(ExternalProject)
ExternalProject_Add(host-libs
PREFIX host-libs
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/HostLibs"
BINARY_DIR "Host"
INSTALL_COMMAND ""
BUILD_ALWAYS ON
)
ExternalProject_Add(host-libs
PREFIX host-libs
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/HostLibs"
BINARY_DIR "Host"
CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
INSTALL_COMMAND ""
BUILD_ALWAYS ON
)
ExternalProject_Add(guest-libs
PREFIX guest-libs
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
BINARY_DIR "Guest"
CMAKE_ARGS "-DX86_C_COMPILER:STRING=${X86_C_COMPILER}" "-DX86_CXX_COMPILER:STRING=${X86_CXX_COMPILER}"
INSTALL_COMMAND ""
BUILD_ALWAYS ON
)
install(
CODE "MESSAGE(\"-- Installing: host-libs\")"
CODE "
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target ThunkHostsInstall
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Host
)"
DEPENDS host-libs
)
ExternalProject_Add(guest-libs
PREFIX guest-libs
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
BINARY_DIR "Guest"
CMAKE_ARGS "-DX86_C_COMPILER:STRING=${X86_C_COMPILER}" "-DX86_CXX_COMPILER:STRING=${X86_CXX_COMPILER}" "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
INSTALL_COMMAND ""
BUILD_ALWAYS ON
)
install(
CODE "MESSAGE(\"-- Installing: guest-libs\")"
CODE "
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target ThunkGuestsInstall
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest
)"
DEPENDS guest-libs
)
endif()
+25
View File
@@ -0,0 +1,25 @@
file(GLOB CONFIG_SOURCES CONFIGURE_DEPENDS *.json)
file(GLOB GEN_CONFIG_SOURCES CONFIGURE_DEPENDS *.json.in)
# Any application configuration json file gets installed
foreach(CONFIG_SRC ${CONFIG_SOURCES})
install(FILES ${CONFIG_SRC}
DESTINATION ${DATA_DIRECTORY}/AppConfig/)
endforeach()
# Any configuration file json file that needs to be generated
# First generate then install it
foreach(GEN_CONFIG_SRC ${GEN_CONFIG_SOURCES})
# Get the filename only component
get_filename_component(CONFIG_NAME ${GEN_CONFIG_SRC} NAME_WE)
# Configure it
configure_file(
${GEN_CONFIG_SRC}
${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json)
# Then install the configured json
install(
FILES ${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json
DESTINATION ${DATA_DIRECTORY}/AppConfig/)
endforeach()
+5
View File
@@ -0,0 +1,5 @@
{
"Config": {
"Env": "STEAM_GAME_LAUNCH_SHELL=@CMAKE_INSTALL_PREFIX@/bin/FEXBash"
}
}
+4 -3
View File
@@ -3,8 +3,9 @@ FROM ubuntu:20.04 as builder
RUN DEBIAN_FRONTEND="noninteractive" apt-get update
RUN DEBIAN_FRONTEND="noninteractive" apt install -y cmake \
libboost-dev clang-10 llvm-10 nasm ninja-build libnuma-dev \
libcap-dev libglfw3-dev libepoxy-dev
clang-10 llvm-10 nasm ninja-build libnuma-dev \
libcap-dev libglfw3-dev libepoxy-dev python3-dev \
python3 linux-headers-generic
COPY . /opt/FEX
@@ -21,7 +22,7 @@ RUN ninja
FROM ubuntu:20.04
RUN DEBIAN_FRONTEND="noninteractive" apt-get update
RUN DEBIAN_FRONTEND="noninteractive" apt install -y libboost-dev \
RUN DEBIAN_FRONTEND="noninteractive" apt install -y \
libnuma-dev libcap-dev libglfw3-dev libepoxy-dev
COPY --from=builder /opt/FEX/build/Bin/* /usr/bin/
+20 -13
View File
@@ -4,6 +4,17 @@ project(${PROJECT_NAME}
VERSION 0.01
LANGUAGES CXX)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(_M_X86_64 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(_M_ARM_64 1)
endif()
set(ENABLE_JIT_X86_64 ${_M_X86_64} CACHE BOOL "Enable the x86_64 JIT")
set(ENABLE_JIT_ARM64 ${_M_ARM_64} CACHE BOOL "Enable the ARM64 JIT")
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
option(ENABLE_JITSYMBOLS "Enable visibility of JITSymbols in profiling tools" FALSE)
@@ -17,23 +28,11 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(CheckCXXCompilerFlag)
include(CheckIncludeFileCXX)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(_M_X86_64 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-operator-names -mcx16")
set(CMAKE_REQUIRED_DEFINITIONS "-fno-operator-names")
message(STATUS "Enabling x86-64 JIT")
set(ENABLE_JIT 1)
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
message(STATUS "Enabling AArch64 JIT")
set(_M_ARM_64 1)
set(ENABLE_JIT 1)
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
# Useful to have for freestanding libFEXCore
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
endif()
endif()
set(CMAKE_CXX_STANDARD 20)
@@ -43,6 +42,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(Git)
set(GIT_SHORT_HASH "Unknown")
set(GIT_DESCRIBE_STRING "FEX-Unknown")
if (GIT_FOUND)
execute_process(
@@ -52,6 +52,13 @@ if (GIT_FOUND)
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DESCRIBE_STRING
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
configure_file(
+432
View File
@@ -0,0 +1,432 @@
import datetime
import json
import sys
def print_header():
header = '''#ifndef OPT_BASE
#define OPT_BASE(type, group, enum, json, default)
#endif
#ifndef OPT_BOOL
#define OPT_BOOL(group, enum, json, default) OPT_BASE(bool, group, enum, json, default)
#endif
#ifndef OPT_UINT8
#define OPT_UINT8(group, enum, json, default) OPT_BASE(uint8_t, group, enum, json, default)
#endif
#ifndef OPT_INT32
#define OPT_INT32(group, enum, json, default) OPT_BASE(int32_t, group, enum, json, default)
#endif
#ifndef OPT_UINT32
#define OPT_UINT32(group, enum, json, default) OPT_BASE(uint32_t, group, enum, json, default)
#endif
#ifndef OPT_UINT64
#define OPT_UINT64(group, enum, json, default) OPT_BASE(uint64_t, group, enum, json, default)
#endif
#ifndef OPT_STR
#define OPT_STR(group, enum, json, default) OPT_BASE(std::string, group, enum, json, default)
#endif
#ifndef OPT_STRARRAY
#define OPT_STRARRAY(group, enum, json, default) OPT_BASE(std::string, group, enum, json, default)
#endif
'''
output_file.write(header)
def print_tail():
tail = '''#undef OPT_BASE
#undef OPT_BOOL
#undef OPT_UINT8
#undef OPT_INT32
#undef OPT_UINT32
#undef OPT_UINT64
#undef OPT_STR
#undef OPT_STRARRAY
'''
output_file.write(tail)
def print_config(type, group_name, json_name, default_value):
output_file.write("OPT_{0} ({1}, {2}, {3}, {4})\n".format(type.upper(), group_name.upper(), json_name.upper(), json_name, default_value))
def print_options(options):
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "\"" + default + "\""
print_config(
op_vals["Type"],
op_group,
op_key,
default)
output_file.write("\n")
def print_unnamed_options(options):
output_file.write("// Unnamed configuration options\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "\"" + default + "\""
print_config(
op_vals["Type"],
op_group,
op_key.upper(), # KEY is the enum here, there is no json configuration for these
default)
output_file.write("\n")
def print_man_option(short, long, desc, default):
if (short != None):
output_man.write(".It Fl {0} , ".format(short))
else:
output_man.write(".It ")
output_man.write("Fl Fl {0}=".format(long))
output_man.write("\n");
# Print description
for line in desc:
output_man.write(".Pp\n")
output_man.write("{0}\n".format(line))
output_man.write(".Pp\n")
output_man.write("\\fBdefault:\\fR {0}\n".format(default))
output_man.write(".Pp\n\n")
def print_man_env_option(name, desc, default):
output_man.write("\\fBFEX_{0}\\fR\n".format(name))
# Print description
for line in desc:
output_man.write(".Pp\n")
output_man.write("{0}\n".format(line))
output_man.write(".Pp\n")
output_man.write("\\fBdefault:\\fR {0}\n".format(default))
output_man.write(".Pp\n\n")
def print_man_options(options):
output_man.write(".Sh OPTIONS\n")
output_man.write(".Bl -tag -width -indent\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
short = None
long = op_key.lower()
if ("ShortArg" in op_vals):
short = op_vals["ShortArg"]
default = op_vals["Default"]
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = op_vals["TextDefault"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "'" + default + "'"
print_man_option(
short,
long,
op_vals["Desc"],
default
)
output_man.write(".El\n")
def print_man_environment(options):
output_man.write(".Sh ENVIRONMENT\n")
output_man.write(".Bl -tag -width -indent\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = op_vals["TextDefault"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "'" + default + "'"
print_man_env_option(
op_key.upper(),
op_vals["Desc"],
default
)
output_man.write(".El\n")
def print_man_header():
header ='''.Dd {0}
.Dt FEX
.Os Linux
.Sh NAME
.Nm FEXLoader
.Nm FEXInterpreter
.Nm FEXBash
.Nd Fast x86-64 and x86 emulation.
.Sh SYNOPSIS
.Nm
.Op options
.Op Ar --
.Ar Application
<args> ...
.Pp
.Nm FEXInterpreter
.Ar Application
<args> ...
.Pp
.Nm FEXBash
.Ar <args> ...
.Sh DESCRIPTION
FEX allows you to run x86 and x86-64 binaries on an AArch64 host, similar to qemu-user and box86.
It has native support for a rootfs overlay, so you don't need to chroot, as well as some thunklibs so it can forward things like GL to the host.
FEX presents a Linux 5.0 interface to the guest, and supports both AArch64 and x86-64 as hosts.
FEX is very much work in progress, so expect things to change.
'''
output_man.write(header.format(datetime.datetime.now().strftime("%d-%m-%Y")))
def print_man_tail():
tail ='''.Sh FILES
.Bl -tag -width "$prefix/share/fex-emu/GuestThunks" -compact
.It Pa $XDG_HOME_DIR/.fex-emu
Default FEX user configuration directory
.It Pa $prefix/share/fex-emu/AppConfig
System level application configuration files
.It Pa $prefix/share/fex-emu/GuestThunks
guest-side thunk data libraries
.It Pa $prefix/lib/fex-emu/HostThunks
host-side thunks for guest communication
.El
'''
output_man.write(tail)
def print_config_option(type, group_name, json_name, default_value, short, choices, desc):
if (type == "bool"):
# Bool gets some special handling to add an inverted case
output_argloader.write("{0}Group".format(group_name))
options = ""
AddedArg = False
if (short != None):
AddedArg = True
options += "\"-{0}\"".format(short)
if (AddedArg):
options += ", "
options += "\"--{0}\"".format(json_name.lower())
output_argloader.write(".add_option({0})".format(options))
output_argloader.write("\n")
output_argloader.write("\t.action(\"store_true\")\n")
output_argloader.write("\t.dest(\"{0}\")\n".format(json_name));
# help
output_argloader.write("\t.help(\n")
desc_line_ender = ""
if (len(desc) > 1):
desc_line_ender = "\\n"
for line in desc:
output_argloader.write("\t\t\"{0}{1}\"\n".format(line, desc_line_ender))
output_argloader.write("\t)\n")
output_argloader.write("\t.set_default({0});\n\n".format(default_value));
output_argloader.write("{0}Group".format(group_name))
output_argloader.write(".add_option(\"--no-{0}\")\n".format(json_name.lower()))
# Inverted case sets the bool to false
output_argloader.write("\t.action(\"store_false\")\n")
output_argloader.write("\t.dest(\"{0}\");\n".format(json_name));
else:
output_argloader.write("{0}Group".format(group_name))
options = ""
AddedArg = False
if (short != None):
AddedArg = True
options += "\"-{0}\"".format(short)
if (AddedArg):
options += ", "
options += "\"--{0}\"".format(json_name.lower())
output_argloader.write(".add_option({0})".format(options))
output_argloader.write("\n")
output_argloader.write("\t.dest(\"{0}\")\n".format(json_name));
if (choices != None):
output_argloader.write("\t.choices({\n")
for choice in choices:
output_argloader.write("\t\t\"{0}\",\n".format(choice))
output_argloader.write("\t})\n")
# help
output_argloader.write("\t.help(\n")
desc_line_ender = ""
if (len(desc) > 1):
desc_line_ender = "\\n"
for line in desc:
output_argloader.write("\t\t\"{0}{1}\"\n".format(line, desc_line_ender))
output_argloader.write("\t)\n")
output_argloader.write("\t.set_default({0});\n".format(default_value));
output_argloader.write("\n");
def print_argloader_options(options):
output_argloader.write("#ifdef BEFORE_PARSE\n")
output_argloader.write("#undef BEFORE_PARSE\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
# Wrap the string argument in quotes
default = "\"" + default + "\""
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = "\"" + op_vals["TextDefault"] + "\""
short = None
choices = None
if ("ShortArg" in op_vals):
short = op_vals["ShortArg"]
if ("Choices" in op_vals):
choices = op_vals["Choices"]
print_config_option(
op_vals["Type"],
op_group,
op_key,
default,
short,
choices,
op_vals["Desc"])
output_argloader.write("\n")
output_argloader.write("#endif\n")
def print_parse_argloader_options(options):
output_argloader.write("#ifdef AFTER_PARSE\n")
output_argloader.write("#undef AFTER_PARSE\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
output_argloader.write("if (Options.is_set_by_user(\"{0}\")) {{\n".format(op_key))
value_type = op_vals["Type"]
NeedsString = False
conversion_func = "std::to_string"
if ("ArgumentHandler" in op_vals):
NeedsString = True
conversion_func = "FEX::Handler::{0}".format(op_vals["ArgumentHandler"])
if (value_type == "str"):
NeedsString = True
conversion_func = ""
if (value_type == "strarray"):
# these need a bit more help
output_argloader.write("\tauto Array = Options.all(\"{0}\");\n".format(op_key))
output_argloader.write("\tfor (auto iter = Array.begin(); iter != Array.end(); ++iter) {\n")
output_argloader.write("\t\tSet(FEXCore::Config::ConfigOption::CONFIG_{0}, *iter);\n".format(op_key.upper()))
output_argloader.write("\t}\n")
else:
if (NeedsString):
output_argloader.write("\tstd::string UserValue = Options[\"{0}\"];\n".format(op_key))
else:
output_argloader.write("\t{0} UserValue = Options.get(\"{1}\");\n".format(value_type, op_key))
output_argloader.write("\tSet(FEXCore::Config::ConfigOption::CONFIG_{0}, {1}(UserValue));\n".format(op_key.upper(), conversion_func))
output_argloader.write("}\n")
output_argloader.write("#endif\n")
def check_for_duplicate_options(options):
short_map = []
long_map = []
# Spin through all the items and see if we have a duplicate option
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
short = None
long = op_key.lower()
long_invert = None
if ("ShortArg" in op_vals):
short = op_vals["ShortArg"]
if (op_vals["Type"] == "bool"):
long_invert = "no-" + long
# Check for short key duplication
if (short != None):
if (short in short_map):
raise Exception("Short config '{0}' for option '{1}' has duplicate entry!".format(short, op_key))
else:
short_map.append(short)
# Check for long key duplication
if (long in long_map):
raise Exception("Long config '{0}' has duplicate entry!".format(long))
else:
long_map.append(long)
# Check for long key duplication
if (long_invert != None):
if (long_invert in long_map):
raise Exception("Long config '{0}' has duplicate entry!".format(long_invert))
else:
long_map.append(long_invert)
if (len(sys.argv) < 5):
sys.exit()
output_filename = sys.argv[2]
output_man_page = sys.argv[3]
output_argumentloader_filename = sys.argv[4]
json_file = open(sys.argv[1], "r")
json_text = json_file.read()
json_file.close()
json_object = json.loads(json_text)
options = json_object["Options"]
unnamed_options = json_object["UnnamedOptions"]
check_for_duplicate_options(options)
# Generate config include file
output_file = open(output_filename, "w")
print_header()
print_options(options)
print_unnamed_options(unnamed_options)
print_tail()
output_file.close()
# Generate man file
output_man = open(output_man_page, "w")
print_man_header()
print_man_options(options)
print_man_environment(options)
print_man_tail()
output_man.close()
# Generate argument loader code
output_argloader = open(output_argumentloader_filename, "w")
print_argloader_options(options);
print_parse_argloader_options(options);
output_argloader.close()
+1 -1
View File
@@ -236,7 +236,7 @@ def print_ir_arg_printer(ops, defines):
if (SSAArgs != 0):
for i in range(0, SSAArgs):
LastArg = (SSAArgs - i - 1) == 0 and not HasArgs
output_file.write("\tPrintArg(out, IR, Op->Header.Args[%d], RAPass);\n" % i)
output_file.write("\tPrintArg(out, IR, Op->Header.Args[%d], RAData);\n" % i)
if not (LastArg):
output_file.write("\t*out << \", \";\n")
+116 -100
View File
@@ -1,49 +1,4 @@
if (ENABLE_CLANG_FORMAT)
find_program(CLANG_TIDY_EXE "clang-tidy")
set(CLANG_TIDY_FLAGS
"-checks=*"
"-fuchsia*"
"-bugprone-macro-parentheses"
"-clang-analyzer-core.*"
"-cppcoreguidelines-pro-type-*"
"-cppcoreguidelines-pro-bounds-array-to-pointer-decay"
"-cppcoreguidelines-pro-bounds-pointer-arithmetic"
"-cppcoreguidelines-avoid-c-arrays"
"-cppcoreguidelines-avoid-magic-numbers"
"-cppcoreguidelines-pro-bounds-constant-array-index"
"-cppcoreguidelines-no-malloc"
"-cppcoreguidelines-special-member-functions"
"-cppcoreguidelines-owning-memory"
"-cppcoreguidelines-macro-usage"
"-cppcoreguidelines-avoid-goto"
"-google-readability-function-size"
"-google-readability-namespace-comments"
"-google-readability-braces-around-statements"
"-google-build-using-namespace"
"-hicpp-*"
"-llvm-namespace-comment"
"-llvm-include-order" # Messes up with case sensitivity
"-misc-unused-parameters"
"-modernize-loop-convert"
"-modernize-use-auto"
"-modernize-avoid-c-arrays"
"-modernize-use-nodiscard"
"readability-*"
"-readability-function-size"
"-readability-implicit-bool-conversion"
"-readability-braces-around-statements"
"-readability-else-after-return"
"-readability-magic-numbers"
"-readability-named-parameter"
"-readability-uppercase-literal-suffix"
"-cert-err34-c"
"-cert-err58-cpp"
"-bugprone-exception-escape"
)
string(REPLACE ";" "," CLANG_TIDY_FLAGS "${CLANG_TIDY_FLAGS}")
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} "${CLANG_TIDY_FLAGS}")
endif()
set (MAN_DIR ${CMAKE_INSTALL_PREFIX}/share/man CACHE PATH "MAN_DIR")
set (SRCS
Common/Paths.cpp
@@ -118,7 +73,7 @@ set (SRCS
Common/SoftFloat-3e/s_f32UIToCommonNaN.c
Interface/Config/Config.cpp
Interface/Context/Context.cpp
Interface/Core/BlockCache.cpp
Interface/Core/LookupCache.cpp
Interface/Core/BlockSamplingData.cpp
Interface/Core/CompileService.cpp
Interface/Core/Core.cpp
@@ -130,7 +85,13 @@ set (SRCS
Interface/Core/X86Tables.cpp
Interface/Core/X86DebugInfo.cpp
Interface/Core/X86HelperGen.cpp
Interface/Core/ArchHelpers/Arm64_stubs.cpp
Interface/Core/ArchHelpers/Arm64Emitter.cpp
Interface/Core/Dispatcher/Dispatcher.cpp
Interface/Core/Dispatcher/X86Dispatcher.cpp
Interface/Core/Dispatcher/Arm64Dispatcher.cpp
Interface/Core/Interpreter/InterpreterCore.cpp
Interface/Core/Interpreter/InterpreterOps.cpp
Interface/Core/X86Tables/BaseTables.cpp
Interface/Core/X86Tables/DDDTables.cpp
Interface/Core/X86Tables/EVEXTables.cpp
@@ -144,7 +105,8 @@ set (SRCS
Interface/Core/X86Tables/X87Tables.cpp
Interface/Core/X86Tables/XOPTables.cpp
Interface/HLE/Thunks/Thunks.cpp
Interface/IR/IR.cpp
Interface/IR/IRDumper.cpp
Interface/IR/IRParser.cpp
Interface/IR/IREmitter.cpp
Interface/IR/PassManager.cpp
Interface/IR/Passes/ConstProp.cpp
@@ -155,65 +117,65 @@ set (SRCS
Interface/IR/Passes/ValueDominanceValidation.cpp
Interface/IR/Passes/PhiValidation.cpp
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
Interface/IR/Passes/DeadFlagStoreElimination.cpp
Interface/IR/Passes/DeadStoreElimination.cpp
Interface/IR/Passes/StaticRegisterAllocationPass.cpp
Interface/IR/Passes/DeadGPRStoreElimination.cpp
Interface/IR/Passes/DeadFPRStoreElimination.cpp
Interface/IR/Passes/RegisterAllocationPass.cpp
Interface/IR/Passes/SyscallOptimization.cpp
Utils/ELFLoader.cpp
Utils/ELFSymbolDatabase.cpp
Utils/LogManager.cpp
Utils/Threads.cpp
)
if (_M_X86_64)
list(APPEND SRCS Interface/Core/Interpreter/x86_64Dispatcher.cpp)
endif()
if(_M_ARM_64)
list(APPEND SRCS Interface/Core/Interpreter/Arm64Dispatcher.cpp)
list(APPEND SRCS
Interface/Core/ArchHelpers/Arm64.cpp)
endif()
set (JIT_LIBS )
if (ENABLE_JIT)
if (_M_X86_64)
add_definitions(-D_M_X86_64=1)
if (NOT FORCE_AARCH64)
list(APPEND SRCS
Interface/Core/JIT/x86_64/JIT.cpp
Interface/Core/JIT/x86_64/ALUOps.cpp
Interface/Core/JIT/x86_64/AtomicOps.cpp
Interface/Core/JIT/x86_64/BranchOps.cpp
Interface/Core/JIT/x86_64/ConversionOps.cpp
Interface/Core/JIT/x86_64/EncryptionOps.cpp
Interface/Core/JIT/x86_64/FlagOps.cpp
Interface/Core/JIT/x86_64/MemoryOps.cpp
Interface/Core/JIT/x86_64/MiscOps.cpp
Interface/Core/JIT/x86_64/MoveOps.cpp
Interface/Core/JIT/x86_64/VectorOps.cpp)
endif()
endif()
if(_M_ARM_64)
add_definitions(-D_M_ARM_64=1)
add_definitions(-DVIXL_INCLUDE_TARGET_AARCH64=1)
add_definitions(-DVIXL_CODE_BUFFER_MMAP=1)
list(APPEND SRCS
Interface/Core/JIT/Arm64/JIT.cpp
Interface/Core/JIT/Arm64/ALUOps.cpp
Interface/Core/JIT/Arm64/AtomicOps.cpp
Interface/Core/JIT/Arm64/BranchOps.cpp
Interface/Core/JIT/Arm64/ConversionOps.cpp
Interface/Core/JIT/Arm64/EncryptionOps.cpp
Interface/Core/JIT/Arm64/FlagOps.cpp
Interface/Core/JIT/Arm64/MemoryOps.cpp
Interface/Core/JIT/Arm64/MiscOps.cpp
Interface/Core/JIT/Arm64/MoveOps.cpp
Interface/Core/JIT/Arm64/VectorOps.cpp)
list(APPEND JIT_LIBS vixl)
endif()
set(DEFINES )
if (_M_X86_64)
list(APPEND DEFINES -D_M_X86_64=1)
endif()
if (_M_ARM_64)
list(APPEND DEFINES -D_M_ARM_64=1)
endif()
if (ENABLE_JIT_X86_64)
list(APPEND SRCS
Interface/Core/JIT/x86_64/JIT.cpp
Interface/Core/JIT/x86_64/ALUOps.cpp
Interface/Core/JIT/x86_64/AtomicOps.cpp
Interface/Core/JIT/x86_64/BranchOps.cpp
Interface/Core/JIT/x86_64/ConversionOps.cpp
Interface/Core/JIT/x86_64/EncryptionOps.cpp
Interface/Core/JIT/x86_64/FlagOps.cpp
Interface/Core/JIT/x86_64/MemoryOps.cpp
Interface/Core/JIT/x86_64/MiscOps.cpp
Interface/Core/JIT/x86_64/MoveOps.cpp
Interface/Core/JIT/x86_64/VectorOps.cpp)
list(APPEND DEFINES -DJIT_X86_64)
endif()
if (ENABLE_JIT_ARM64)
list(APPEND DEFINES -DJIT_ARM64)
list(APPEND SRCS
Interface/Core/JIT/Arm64/JIT.cpp
Interface/Core/JIT/Arm64/ALUOps.cpp
Interface/Core/JIT/Arm64/AtomicOps.cpp
Interface/Core/JIT/Arm64/BranchOps.cpp
Interface/Core/JIT/Arm64/ConversionOps.cpp
Interface/Core/JIT/Arm64/EncryptionOps.cpp
Interface/Core/JIT/Arm64/FlagOps.cpp
Interface/Core/JIT/Arm64/MemoryOps.cpp
Interface/Core/JIT/Arm64/MiscOps.cpp
Interface/Core/JIT/Arm64/MoveOps.cpp
Interface/Core/JIT/Arm64/VectorOps.cpp)
endif()
if (ENABLE_JITSYMBOLS)
add_definitions(-DENABLE_JITSYMBOLS=1)
list(APPEND DEFINES -DENABLE_JITSYMBOLS=1)
endif()
# Generate IR include file
@@ -249,23 +211,60 @@ add_custom_command(
set_source_files_properties(${OUTPUT_IR_NAME} PROPERTIES
GENERATED TRUE)
# Create teh target
# Create the target
add_custom_target(IR_INC
DEPENDS "${OUTPUT_NAME}"
DEPENDS "${OUTPUT_IR_DOC}")
# Generate the configuration include file
set(OUTPUT_CONFIG_FOLDER "${CMAKE_BINARY_DIR}/include/FEXCore/Config")
set(OUTPUT_CONFIG_NAME "${OUTPUT_CONFIG_FOLDER}/ConfigValues.inl")
set(OUTPUT_CONFIG_OPTION_NAME "${OUTPUT_CONFIG_FOLDER}/ConfigOptions.inl")
set(INPUT_CONFIG_NAME "${CMAKE_CURRENT_SOURCE_DIR}/Interface/Config/Config.json")
set(OUTPUT_MAN_NAME "${CMAKE_BINARY_DIR}/generated/FEX.1")
add_custom_target(CREATE_CONFIG_FOLDER ALL
COMMAND ${CMAKE_COMMAND} -E make_directory "${OUTPUT_CONFIG_FOLDER}")
add_custom_command(
OUTPUT "${OUTPUT_CONFIG_NAME}"
OUTPUT "${OUTPUT_CONFIG_OPTION_NAME}"
OUTPUT "${OUTPUT_MAN_NAME}"
DEPENDS "${INPUT_CONFIG_NAME}"
DEPENDS CREATE_CONFIG_FOLDER
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/config_generator.py"
COMMAND "python3" "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/config_generator.py" "${INPUT_CONFIG_NAME}" "${OUTPUT_CONFIG_NAME}" "${OUTPUT_MAN_NAME}"
"${OUTPUT_CONFIG_OPTION_NAME}"
)
set_source_files_properties(${OUTPUT_CONFIG_NAME} PROPERTIES
GENERATED TRUE)
set_source_files_properties(${OUTPUT_CONFIG_OPTION_NAME} PROPERTIES
GENERATED TRUE)
set_source_files_properties(${OUTPUT_MAN_NAME} PROPERTIES
GENERATED TRUE)
# Create the target
add_custom_target(CONFIG_INC
DEPENDS "${OUTPUT_CONFIG_NAME}"
DEPENDS "${OUTPUT_CONFIG_OPTION_NAME}"
DEPENDS "${OUTPUT_MAN_NAME}")
# Install the man page
install(FILES ${OUTPUT_MAN_NAME} DESTINATION ${MAN_DIR}/man1)
# Add in diagnostic colours if the option is available.
# Ninja code generator will kill colours if this isn't here
check_cxx_compiler_flag(-fdiagnostics-color=always GCC_COLOR)
check_cxx_compiler_flag(-fcolor-diagnostics CLANG_COLOR)
set(LINUX_LIBS
numa)
function(AddLibrary Name Type)
function(AddObject Name Type)
add_library(${Name} ${Type} ${SRCS})
add_dependencies(${Name} IR_INC)
target_link_libraries(${Name} pthread rt ${JIT_LIBS} ${LINUX_LIBS} dl)
add_dependencies(${Name} CONFIG_INC)
target_link_libraries(${Name} pthread rt vixl ${LINUX_LIBS} dl)
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
target_include_directories(${Name} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
@@ -276,9 +275,15 @@ function(AddLibrary Name Type)
target_include_directories(${Name} PUBLIC "${PROJECT_SOURCE_DIR}/include/")
target_include_directories(${Name} PUBLIC "${CMAKE_BINARY_DIR}/include/")
target_compile_definitions(${Name} PRIVATE ${DEFINES})
target_compile_options(${Name}
PRIVATE
-Wno-trigraphs -Wall)
-Wall
-Werror=implicit-fallthrough
-Wno-trigraphs
)
if (GCC_COLOR)
target_compile_options(${Name}
@@ -292,6 +297,17 @@ function(AddLibrary Name Type)
endif()
endfunction()
function(AddLibrary Name Type)
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
target_link_libraries(${Name} pthread rt vixl ${LINUX_LIBS} dl)
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
target_include_directories(${Name} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
target_include_directories(${Name} PUBLIC "${PROJECT_SOURCE_DIR}/include/")
target_include_directories(${Name} PUBLIC "${CMAKE_BINARY_DIR}/include/")
endfunction()
AddObject(${PROJECT_NAME}_object OBJECT)
AddLibrary(${PROJECT_NAME} STATIC)
AddLibrary(${PROJECT_NAME}_shared SHARED)
@@ -96,6 +96,7 @@ extFloat80_t
switch ( roundingMode ) {
case softfloat_round_near_even:
if ( !(sigA & UINT64_C( 0x7FFFFFFFFFFFFFFF )) ) break;
__attribute__((fallthrough));
case softfloat_round_near_maxMag:
if ( exp == 0x3FFE ) goto mag1;
break;
@@ -103,7 +103,9 @@ float32_t
if ( ! sig ) exp = 0;
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
#ifdef SOFTFLOAT_ROUND_ODD
packReturn:
#endif
uiZ = packToF32UI( sign, exp, sig );
uiZ:
uZ.ui = uiZ;
@@ -107,7 +107,9 @@ float64_t
if ( ! sig ) exp = 0;
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
#ifdef SOFTFLOAT_ROUND_ODD
packReturn:
#endif
uiZ = packToF64UI( sign, exp, sig );
uiZ:
uZ.ui = uiZ;
+12 -5
View File
@@ -77,7 +77,7 @@ struct X80SoftFloat {
}
static X80SoftFloat FRNDINT(X80SoftFloat const &lhs) {
return extF80_roundToInt(lhs, softfloat_round_near_even, false);
return extF80_roundToInt(lhs, softfloat_roundingMode, false);
}
static X80SoftFloat FXTRACT_SIG(X80SoftFloat const &lhs) {
@@ -173,19 +173,26 @@ struct X80SoftFloat {
}
operator int16_t() const {
return extF80_to_i32(*this, softfloat_round_near_even, false);
auto rv = extF80_to_i32(*this, softfloat_roundingMode, false);
if (rv > INT16_MAX) {
return INT16_MAX;
} else if (rv < INT16_MIN) {
return INT16_MIN;
} else {
return rv;
}
}
operator int32_t() const {
return extF80_to_i32(*this, softfloat_round_near_even, false);
return extF80_to_i32(*this, softfloat_roundingMode, false);
}
operator int64_t() const {
return extF80_to_i64(*this, softfloat_round_near_even, false);
return extF80_to_i64(*this, softfloat_roundingMode, false);
}
operator uint64_t() const {
return extF80_to_ui64(*this, softfloat_round_near_even, false);
return extF80_to_ui64(*this, softfloat_roundingMode, false);
}
void operator=(const float rhs) {
+7
View File
@@ -38,4 +38,11 @@ namespace FEXCore::StrConv {
*Result = Value;
return true;
}
template <typename T,
typename = std::enable_if<std::is_enum<T>::value, T>>
[[maybe_unused]] static bool Conv(std::string_view Value, T *Result) {
*Result = static_cast<T>(std::stoull(std::string(Value), nullptr, 0));
return true;
}
}
+225 -87
View File
@@ -4,99 +4,101 @@
#include "Interface/Context/Context.h"
#include <FEXCore/Config/Config.h>
#include <filesystem>
#include <pwd.h>
#include <map>
#include <sys/sysinfo.h>
#include <unistd.h>
namespace FEXCore::Config {
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config) {
switch (Option) {
case FEXCore::Config::CONFIG_MULTIBLOCK:
CTX->Config.Multiblock = Config != 0;
break;
case FEXCore::Config::CONFIG_MAXBLOCKINST:
CTX->Config.MaxInstPerBlock = Config;
break;
case FEXCore::Config::CONFIG_DEFAULTCORE:
CTX->Config.Core = static_cast<FEXCore::Config::ConfigCore>(Config);
break;
case FEXCore::Config::CONFIG_VIRTUALMEMSIZE:
CTX->Config.VirtualMemSize = Config;
break;
case FEXCore::Config::CONFIG_SINGLESTEP:
CTX->Config.RunningMode = Config != 0 ? FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP : FEXCore::Context::CoreRunningMode::MODE_RUN;
break;
case FEXCore::Config::CONFIG_GDBSERVER:
Config != 0 ? CTX->StartGdbServer() : CTX->StopGdbServer();
break;
case FEXCore::Config::CONFIG_IS64BIT_MODE:
CTX->Config.Is64BitMode = Config != 0;
break;
case FEXCore::Config::CONFIG_TSO_ENABLED:
CTX->Config.TSOEnabled = Config != 0;
break;
case FEXCore::Config::CONFIG_SMC_CHECKS:
CTX->Config.SMCChecks = Config != 0;
break;
case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS:
CTX->Config.ABILocalFlags = Config != 0;
break;
case FEXCore::Config::CONFIG_ABI_NO_PF:
CTX->Config.ABINoPF = Config != 0;
break;
default: LogMan::Msg::A("Unknown configuration option");
char const* FindUserHomeThroughUID() {
auto passwd = getpwuid(geteuid());
if (passwd) {
return passwd->pw_dir;
}
return nullptr;
}
const char *GetHomeDirectory() {
char const *HomeDir = getenv("HOME");
// Try to get home directory from uid
if (!HomeDir) {
HomeDir = FindUserHomeThroughUID();
}
// try the PWD
if (!HomeDir) {
HomeDir = getenv("PWD");
}
// Still doesn't exit? You get local
if (!HomeDir) {
HomeDir = ".";
}
return HomeDir;
}
std::string GetConfigDirectory(bool Global) {
std::string ConfigDir;
if (Global) {
ConfigDir = GLOBAL_DATA_DIRECTORY;
}
else {
char const *HomeDir = GetHomeDirectory();
char const *ConfigXDG = getenv("XDG_CONFIG_HOME");
ConfigDir = ConfigXDG ? ConfigXDG : HomeDir;
ConfigDir += "/.fex-emu/";
// Ensure the folder structure is created for our configuration
if (!std::filesystem::exists(ConfigDir) &&
!std::filesystem::create_directories(ConfigDir)) {
LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigDir.c_str());
// Let's go local in this case
return "./";
}
}
return ConfigDir;
}
std::string GetConfigFileLocation() {
std::string ConfigFile = GetConfigDirectory(false) + "Config.json";
return ConfigFile;
}
std::string GetApplicationConfig(std::string &Filename, bool Global) {
std::string ConfigFile = GetConfigDirectory(Global);
if (!Global &&
!std::filesystem::exists(ConfigFile) &&
!std::filesystem::create_directories(ConfigFile)) {
LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigFile.c_str());
// Let's go local in this case
return "./";
}
ConfigFile += "AppConfig/" + Filename + ".json";
return ConfigFile;
}
std::string GetDataDirectory() {
std::string DataDir{};
char const *HomeDir = GetHomeDirectory();
char const *DataXDG = getenv("XDG_DATA_HOME");
DataDir = DataXDG ?: HomeDir;
DataDir += "/.fex-emu/";
return DataDir;
}
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config) {
}
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, std::string const &Config) {
switch (Option) {
case CONFIG_ROOTFSPATH:
CTX->Config.RootFSPath = Config;
break;
case CONFIG_THUNKLIBSPATH:
CTX->Config.ThunkLibsPath = Config;
break;
case FEXCore::Config::CONFIG_DUMPIR:
CTX->Config.DumpIR = Config;
break;
default: LogMan::Msg::A("Unknown configuration option");
}
}
uint64_t GetConfig(FEXCore::Context::Context *CTX, ConfigOption Option) {
switch (Option) {
case FEXCore::Config::CONFIG_MULTIBLOCK:
return CTX->Config.Multiblock;
break;
case FEXCore::Config::CONFIG_MAXBLOCKINST:
return CTX->Config.MaxInstPerBlock;
break;
case FEXCore::Config::CONFIG_DEFAULTCORE:
return CTX->Config.Core;
break;
case FEXCore::Config::CONFIG_VIRTUALMEMSIZE:
return CTX->Config.VirtualMemSize;
break;
case FEXCore::Config::CONFIG_SINGLESTEP:
return CTX->Config.RunningMode == FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP ? 1 : 0;
case FEXCore::Config::CONFIG_GDBSERVER:
return CTX->GetGdbServerStatus();
break;
case FEXCore::Config::CONFIG_IS64BIT_MODE:
return CTX->Config.Is64BitMode;
break;
case FEXCore::Config::CONFIG_TSO_ENABLED:
return CTX->Config.TSOEnabled;
break;
case FEXCore::Config::CONFIG_SMC_CHECKS:
return CTX->Config.SMCChecks;
break;
case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS:
return CTX->Config.ABILocalFlags;
break;
case FEXCore::Config::CONFIG_ABI_NO_PF:
return CTX->Config.ABINoPF;
break;
default: LogMan::Msg::A("Unknown configuration option");
}
return 0;
}
@@ -130,6 +132,7 @@ namespace FEXCore::Config {
private:
void MergeConfigMap(const LayerOptions &Options);
void MergeEnvironmentVariables(ConfigOption const &Option, LayerValue const &Value);
};
void MetaLayer::Load() {
@@ -144,10 +147,56 @@ namespace FEXCore::Config {
}
}
void MetaLayer::MergeEnvironmentVariables(ConfigOption const &Option, LayerValue const &Value) {
// Environment variables need a bit of additional work
// We want to merge the arrays rather than overwrite entirely
auto MetaEnvironment = OptionMap.find(Option);
if (MetaEnvironment == OptionMap.end()) {
// Doesn't exist, just insert
OptionMap.insert_or_assign(Option, Value);
return;
}
// If an environment variable exists in both current meta and in the incoming layer then the meta layer value is overwritten
std::unordered_map<std::string, std::string> LookupMap;
const auto AddToMap = [&LookupMap](FEXCore::Config::LayerValue const &Value) {
for (const auto &EnvVar : Value) {
const auto ItEq = EnvVar.find_first_of('=');
if (ItEq == std::string::npos) {
// Broken environment variable
// Skip
continue;
}
auto Key = std::string(EnvVar.begin(), EnvVar.begin() + ItEq);
auto Value = std::string(EnvVar.begin() + ItEq + 1, EnvVar.end());
// Add the key to the map, overwriting whatever previous value was there
LookupMap.insert_or_assign(std::move(Key), std::move(Value));
}
};
AddToMap(MetaEnvironment->second);
AddToMap(Value);
// Now with the two layers merged in the map
// Add all the values to the option
Erase(Option);
for (auto &Val : LookupMap) {
// Set will emplace multiple options in to its list
Set(Option, Val.first + "=" + Val.second);
}
}
void MetaLayer::MergeConfigMap(const LayerOptions &Options) {
// Insert this layer's options, overlaying previous options that exist here
for (auto &it : Options) {
OptionMap.insert_or_assign(it.first, it.second);
if (it.first == FEXCore::Config::ConfigOption::CONFIG_ENV) {
MergeEnvironmentVariables(it.first, it.second);
}
else {
OptionMap.insert_or_assign(it.first, it.second);
}
}
}
@@ -170,8 +219,91 @@ namespace FEXCore::Config {
}
}
std::string ExpandPath(std::string PathName) {
if (PathName.empty()) {
return {};
}
std::filesystem::path Path{PathName};
// Expand home if it exists
if (Path.is_relative()) {
std::string Home = getenv("HOME") ?: "";
// Home expansion only works if it is the first character
// This matches bash behaviour
if (PathName.at(0) == '~') {
PathName.replace(0, 1, Home);
return PathName;
}
// Expand relative path to absolute
Path = std::filesystem::absolute(Path);
// Only return if it exists
if (std::filesystem::exists(Path)) {
return Path;
}
}
return {};
}
void ReloadMetaLayer() {
Meta->Load();
// Do configuration option fix ups after everything is reloaded
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THREADS)) {
FEX_CONFIG_OPT(Cores, THREADS);
if (Cores == 0) {
// When the number of emulated CPU cores is zero then auto detect
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_THREADS, std::to_string(get_nprocs_conf()));
}
}
auto ExpandPathIfExists = [](FEXCore::Config::ConfigOption Config, std::string PathName) {
auto NewPath = ExpandPath(PathName);
if (!NewPath.empty()) {
FEXCore::Config::EraseSet(Config, NewPath);
}
};
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_ROOTFS)) {
FEX_CONFIG_OPT(PathName, ROOTFS);
auto ExpandedString = ExpandPath(PathName());
if (!ExpandedString.empty()) {
// Adjust the path if it ended up being relative
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, ExpandedString);
}
else if (!PathName().empty()) {
// If the filesystem doesn't exist then let's see if it exists in the fex-emu folder
std::string NamedRootFS = GetDataDirectory() + "RootFS/" + PathName();
if (std::filesystem::exists(NamedRootFS)) {
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, NamedRootFS);
}
}
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKHOSTLIBS)) {
FEX_CONFIG_OPT(PathName, THUNKHOSTLIBS);
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKHOSTLIBS, PathName());
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKGUESTLIBS)) {
FEX_CONFIG_OPT(PathName, THUNKGUESTLIBS);
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKGUESTLIBS, PathName());
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKCONFIG)) {
FEX_CONFIG_OPT(PathName, THUNKCONFIG);
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKCONFIG, PathName());
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_OUTPUTLOG)) {
FEX_CONFIG_OPT(PathName, OUTPUTLOG);
if (PathName() != "stdout" && PathName() != "stderr") {
ExpandPathIfExists(FEXCore::Config::CONFIG_OUTPUTLOG, PathName());
}
}
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_SINGLESTEP)) {
// Single stepping also enforces single instruction size blocks
Set(FEXCore::Config::ConfigOption::CONFIG_MAXINST, std::to_string(1u));
}
}
void AddLayer(std::unique_ptr<FEXCore::Config::Layer> _Layer) {
@@ -233,8 +365,14 @@ namespace FEXCore::Config {
}
}
template bool Value<bool>::GetIfExists(FEXCore::Config::ConfigOption Option, bool Default);
template uint8_t Value<uint8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint8_t Default);
template bool Value<bool>::GetIfExists(FEXCore::Config::ConfigOption Option, bool Default);
template int8_t Value<int8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int8_t Default);
template uint8_t Value<uint8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint8_t Default);
template int16_t Value<int16_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int16_t Default);
template uint16_t Value<uint16_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint16_t Default);
template int32_t Value<int32_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int32_t Default);
template uint32_t Value<uint32_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint32_t Default);
template int64_t Value<int64_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int64_t Default);
template uint64_t Value<uint64_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint64_t Default);
// Constructor
+232
View File
@@ -0,0 +1,232 @@
{
"Options": {
"CPU": {
"Core": {
"Type": "uint32",
"Default": "FEXCore::Config::ConfigCore::CONFIG_IRJIT",
"TextDefault": "irjit",
"ShortArg": "c",
"Choices": [ "irint", "irjit", "host" ],
"ArgumentHandler": "CoreHandler",
"Desc": [
"Which CPU core to use",
"host only exists on x86_64",
"[irint, irjit, host]"
]
},
"Multiblock": {
"Type": "bool",
"Default": "true",
"ShortArg": "m",
"Desc": [
"Controls multiblock code compilation"
]
},
"MaxInst": {
"Type": "int32",
"Default": "5000",
"ShortArg": "n",
"Desc": [
"Maximum number of instruction to store in a block"
]
},
"Threads": {
"Type": "uint32",
"Default": "1",
"ShortArg": "T",
"Desc": [
"Number of physical hardware threads to tell the process we have.",
"0 will auto detect."
]
}
},
"Emulation": {
"RootFS": {
"Type": "str",
"Default": "",
"ShortArg": "R",
"Desc": [
"Which Root filesystem prefix to use",
"This can be a filesystem path",
"\teg: ~/RootFS/Debian_x86_64",
"Or this can be a name of a rootfs",
"If the named rootfs exists in the FEX data folder then it will use that one",
"\teg: $HOME/.fex-emu/RootFS/<RootFS name>/",
"Or if you have XDG_DATA_HOME the config will search in that directory",
"\teg: $XDG_DATA_HOME/.fex-emu/RootFS/<RootFS name>/"
]
},
"ThunkHostLibs": {
"Type": "str",
"Default": "",
"ShortArg": "t",
"Desc": [
"Folder to find the host-side thunking libraries."
]
},
"ThunkGuestLibs": {
"Type": "str",
"Default": "",
"ShortArg": "j",
"Desc": [
"Folder to find the guest-side thunking libraries."
]
},
"ThunkConfig": {
"Type": "str",
"Default": "",
"ShortArg": "k",
"Desc": [
"A json file specifying where to overlay the thunks."
]
},
"Env": {
"Type": "strarray",
"Default": "",
"ShortArg": "E",
"Desc": [
"Adds an environment variable to the emulated environment."
]
}
},
"Debug": {
"SingleStep": {
"Type": "bool",
"Default": "false",
"ShortArg": "S",
"Desc": [
"Single stepping configuration."
]
},
"GdbServer": {
"Type": "bool",
"Default": "false",
"ShortArg": "G",
"Desc": [
"Enables the GDB server."
]
},
"DumpIR": {
"Type": "str",
"Default": "no",
"Desc": [
"Folder to dump the IR in to.",
"[no, stdout, stderr, <Folder>]"
]
},
"DumpGPRs": {
"Type": "bool",
"Default": "false",
"ShortArg": "g",
"Desc": [
"When the test harness ends, print the GPR state."
]
},
"O0": {
"Type": "bool",
"Default": "false",
"ShortArg": "O0",
"Desc": [
"Disables optimizations passes for debugging."
]
}
},
"Logging": {
"SilentLog": {
"Type": "bool",
"Default": "true",
"ShortArg": "s",
"Desc": [
"Disables logging"
]
},
"OutputLog": {
"Type": "str",
"Default": "stdout",
"ShortArg": "o",
"Desc": [
"File to write FEX output to.",
"[stdout, stderr, <Filename>]"
]
}
},
"Hacks": {
"SMCChecks": {
"Type": "uint8",
"Default": "FEXCore::Config::CONFIG_SMC_MMAN",
"TextDefault": "mman",
"ArgumentHandler": "SMCCheckHandler",
"Desc": [
"Checks code for modification before execution.",
"\tnone: No checks",
"\tmman: Invalidate on mmap, mprotect, munmap",
"\tfull: Validate code before every run (slow)"
]
},
"TSOEnabled": {
"Type": "bool",
"Default": "true",
"Desc": [
"Controls TSO IR ops.",
"Highly likely to break any multithreaded application if disabled."
]
},
"ABILocalFlags": {
"Type": "bool",
"Default": "false",
"Desc": [
"When enabled enables an optimization around flags.",
"Assumes flags are not used across cals.",
"Hand-written assembly can violate this assumption."
]
},
"ABINoPF": {
"Type": "bool",
"Default": "false",
"Desc": [
"When enabled enables an optimization around parity flag calculation.",
"Removes the calculation of the parity flag from GPR instructions.",
"Assuming no uses rely on it"
]
}
},
"Misc": {
"AOTIRCapture": {
"Type": "bool",
"Default": "false",
"Desc": [
"Captures IR and generates an AOT IR cache.",
"Captures both the loaded executable and libraries it loads."
]
},
"AOTIRLoad": {
"Type": "bool",
"Default": "false",
"Desc": [
"Loads an AOT IR cache for the loaded executable."
]
}
}
},
"UnnamedOptions": {
"Misc": {
"IS_INTERPRETER": {
"Type": "bool",
"Default": "false"
},
"INTERPRETER_INSTALLED": {
"Type": "bool",
"Default": "false"
},
"APP_FILENAME": {
"Type": "str",
"Default": ""
},
"IS64BIT_MODE": {
"Type": "bool",
"Default": "false"
}
}
}
}
+27 -29
View File
@@ -23,6 +23,9 @@ namespace FEXCore::Context {
}
void DestroyContext(FEXCore::Context::Context *CTX) {
if (CTX->ParentThread) {
CTX->DestroyThread(CTX->ParentThread);
}
delete CTX;
}
@@ -65,11 +68,11 @@ namespace FEXCore::Context {
}
void GetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
memcpy(State, &CTX->ParentThread->State.State, sizeof(FEXCore::Core::CPUState));
memcpy(State, CTX->ParentThread->CurrentFrame, sizeof(FEXCore::Core::CPUState));
}
void SetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
memcpy(&CTX->ParentThread->State.State, State, sizeof(FEXCore::Core::CPUState));
memcpy(CTX->ParentThread->CurrentFrame, State, sizeof(FEXCore::Core::CPUState));
}
void Pause(FEXCore::Context::Context *CTX) {
@@ -84,10 +87,6 @@ namespace FEXCore::Context {
CTX->CustomCPUFactory = std::move(Factory);
}
void SetFallbackCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory) {
CTX->FallbackCPUFactory = std::move(Factory);
}
bool AddVirtualMemoryMapping([[maybe_unused]] FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t VirtualAddress, [[maybe_unused]] uint64_t PhysicalAddress, [[maybe_unused]] uint64_t Size) {
return false;
}
@@ -123,20 +122,12 @@ namespace FEXCore::Context {
CTX->StopThread(Thread);
}
void DeleteForkedThreads(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
// This function is called after fork
// We need to cleanup some of the thread data that is dead
for (auto &DeadThread : CTX->Threads) {
if (DeadThread == Thread) {
continue;
}
void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->DestroyThread(Thread);
}
// Setting running to false ensures that when they are shutdown we won't send signals to kill them
DeadThread->State.RunningEvents.Running = false;
}
// We now only have one thread
CTX->IdleWaitRefCount = 1;
void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->CleanupAfterFork(Thread);
}
void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation) {
@@ -147,8 +138,23 @@ namespace FEXCore::Context {
CTX->SyscallHandler = Handler;
}
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, [[maybe_unused]] uint32_t Leaf) {
return CTX->CPUID.RunFunction(Function);
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf) {
return CTX->CPUID.RunFunction(Function, Leaf);
}
void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::istream>(const std::string&)> CacheReader) {
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 AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name) {
return CTX->AddNamedRegion(Base, Length, Offset, Name);
}
void RemoveNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length) {
return CTX->RemoveNamedRegion(Base, Length);
}
namespace Debug {
@@ -163,10 +169,6 @@ namespace Debug {
return CTX->GetRuntimeStatsForThread(Thread);
}
FEXCore::Core::CPUState GetCPUState(FEXCore::Context::Context *CTX) {
return CTX->GetCPUState();
}
bool GetDebugDataForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::Core::DebugData *Data) {
return CTX->GetDebugDataForRIP(RIP, Data);
}
@@ -183,10 +185,6 @@ namespace Debug {
// void SetIRForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::IR::IntrusiveIRList *const ir) {
// CTX->SetIRForRIP(RIP, ir);
// }
FEXCore::Core::ThreadState *GetThreadState(FEXCore::Context::Context *CTX) {
return CTX->GetThreadState();
}
}
}
+87 -29
View File
@@ -6,13 +6,21 @@
#include "Interface/Core/InternalThreadState.h"
#include "Interface/Core/X86HelperGen.h"
#include "Interface/IR/PassManager.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Utils/Event.h>
#include <stdint.h>
#include <functional>
#include <istream>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <ostream>
#include <set>
#include <unordered_map>
namespace FEXCore {
class ThunkHandler;
@@ -21,7 +29,8 @@ class GdbServer;
class SiganlDelegator;
namespace CPU {
class JITCore;
class Arm64JITCore;
class X86JITCore;
}
namespace HLE {
class SyscallHandler;
@@ -30,6 +39,8 @@ class SyscallHandler;
namespace FEXCore::IR {
class RegisterAllocationPass;
class RegisterAllocationData;
class IRListView;
namespace Validation {
class IRValidation;
}
@@ -43,30 +54,42 @@ namespace FEXCore::Context {
struct Context {
friend class FEXCore::HLE::SyscallHandler;
friend class FEXCore::CPU::JITCore;
#ifdef JIT_ARM64
friend class FEXCore::CPU::Arm64JITCore;
#endif
#ifdef JIT_X86_64
friend class FEXCore::CPU::X86JITCore;
#endif
friend class FEXCore::IR::Validation::IRValidation;
struct {
bool Multiblock {false};
bool BreakOnFrontendFailure {true};
int64_t MaxInstPerBlock {-1LL};
uint64_t VirtualMemSize {1ULL << 36};
CoreRunningMode RunningMode {CoreRunningMode::MODE_RUN};
FEXCore::Config::ConfigCore Core {FEXCore::Config::CONFIG_INTERPRETER};
bool GdbServer {false};
std::string RootFSPath;
std::string ThunkLibsPath;
uint64_t VirtualMemSize{1ULL << 36};
bool Is64BitMode {true};
bool TSOEnabled {true};
bool SMCChecks {false};
bool ABILocalFlags {false};
bool ABINoPF {false};
std::string DumpIR;
// this is for internal use
bool ValidateIRarser { false };
FEX_CONFIG_OPT(Multiblock, MULTIBLOCK);
FEX_CONFIG_OPT(SingleStepConfig, SINGLESTEP);
FEX_CONFIG_OPT(GdbServer, GDBSERVER);
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
FEX_CONFIG_OPT(ABILocalFlags, ABILOCALFLAGS);
FEX_CONFIG_OPT(ABINoPF, ABINOPF);
FEX_CONFIG_OPT(AOTIRCapture, AOTIRCAPTURE);
FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD);
FEX_CONFIG_OPT(SMCChecks, SMCCHECKS);
FEX_CONFIG_OPT(Core, CORE);
FEX_CONFIG_OPT(MaxInstPerBlock, MAXINST);
FEX_CONFIG_OPT(RootFSPath, ROOTFS);
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
FEX_CONFIG_OPT(DumpIR, DUMPIR);
} Config;
using IntCallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
IntCallbackReturn InterpreterCallbackReturn;
FEXCore::HostFeatures HostFeatures;
std::mutex ThreadCreationMutex;
@@ -87,9 +110,30 @@ namespace FEXCore::Context {
std::unique_ptr<FEXCore::ThunkHandler> ThunkHandler;
CustomCPUFactoryType CustomCPUFactory;
CustomCPUFactoryType FallbackCPUFactory;
std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> CustomExitHandler;
struct AOTIRCacheEntry {
uint64_t start;
uint64_t len;
uint64_t crc;
IR::IRListView *IR;
IR::RegisterAllocationData *RAData;
};
std::function<std::unique_ptr<std::istream>(const std::string&)> AOTIRLoader;
std::unordered_map<std::string, std::map<uint64_t, AOTIRCacheEntry>> AOTIRCache;
struct AddrToFileEntry {
uint64_t Start;
uint64_t Len;
uint64_t Offset;
std::string fileid;
void *CachedFileEntry;
};
std::map<uint64_t, AddrToFileEntry> AddrToFile;
#ifdef BLOCKSTATS
std::unique_ptr<FEXCore::BlockSamplingData> BlockData;
#endif
@@ -122,24 +166,30 @@ namespace FEXCore::Context {
static void RemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
// Wrapper which takes CpuStateFrame instead of InternalThreadState
static void RemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
RemoveCodeEntry(Frame->Thread, GuestRIP);
}
// Debugger interface
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
uint64_t GetThreadCount() const;
FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(uint64_t Thread);
FEXCore::Core::CPUState GetCPUState();
bool GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data);
bool FindHostCodeForRIP(uint64_t RIP, uint8_t **Code);
// XXX:
// bool FindIRForRIP(uint64_t RIP, FEXCore::IR::IntrusiveIRList **ir);
// void SetIRForRIP(uint64_t RIP, FEXCore::IR::IntrusiveIRList *const ir);
FEXCore::Core::ThreadState *GetThreadState();
void LoadEntryList();
std::tuple<void *, FEXCore::Core::DebugData *> CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
uintptr_t CompileBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
uintptr_t CompileFallbackBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
std::tuple<FEXCore::IR::IRListView *, FEXCore::IR::RegisterAllocationData *, uint64_t, uint64_t, uint64_t, uint64_t> GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool, uint64_t, uint64_t> CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
uintptr_t CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
bool LoadAOTIRCache(std::istream &stream);
bool WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
// Used for thread creation from syscalls
void InitializeCompiler(FEXCore::Core::InternalThreadState* State, bool CompileThread);
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
@@ -148,27 +198,35 @@ namespace FEXCore::Context {
void CopyMemoryMapping(FEXCore::Core::InternalThreadState *ParentThread, FEXCore::Core::InternalThreadState *ChildThread);
void RunThread(FEXCore::Core::InternalThreadState *Thread);
void DestroyThread(FEXCore::Core::InternalThreadState *Thread);
void CleanupAfterFork(FEXCore::Core::InternalThreadState *ExceptForThread);
std::vector<FEXCore::Core::InternalThreadState*> *const GetThreads() { return &Threads; }
void AddNamedRegion(uintptr_t Base, uintptr_t Size, uintptr_t Offset, const std::string &filename);
void RemoveNamedRegion(uintptr_t Base, uintptr_t Size);
#if ENABLE_JITSYMBOLS
FEXCore::JITSymbols Symbols;
#endif
// Public for threading
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
protected:
void ClearCodeCache(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
void ClearCodeCache(FEXCore::Core::InternalThreadState *Thread, bool AlsoClearIRCache);
private:
void WaitForIdleWithTimeout();
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
void NotifyPause();
uintptr_t AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr);
void AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr, uint64_t Start, uint64_t Length);
FEXCore::CodeLoader *LocalLoader{};
// Entry Cache
bool GetFilenameHash(std::string const &Filename, std::string &Hash);
std::optional<std::string> GetFilenameHash(std::string const &Filename) const;
void AddThreadRIPsToEntryList(FEXCore::Core::InternalThreadState *Thread);
void SaveEntryList();
std::set<uint64_t> EntryList;
@@ -178,8 +236,8 @@ namespace FEXCore::Context {
std::unique_ptr<GdbServer> DebugServer;
bool StartPaused = false;
FEXCore::Config::Value<std::string> AppFilename{FEXCore::Config::CONFIG_APP_FILENAME, ""};
FEX_CONFIG_OPT(AppFilename, APP_FILENAME);
};
uint64_t HandleSyscall(FEXCore::HLE::SyscallHandler *Handler, FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args);
uint64_t HandleSyscall(FEXCore::HLE::SyscallHandler *Handler, FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args);
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
namespace FEXCore::ArchHelpers::Arm64 {
constexpr uint32_t CASPAL_MASK = 0xBF'E0'FC'00;
constexpr uint32_t CASPAL_INST = 0x08'60'FC'00;
constexpr uint32_t CASAL_MASK = 0x3F'E0'FC'00;
constexpr uint32_t CASAL_INST = 0x08'E0'FC'00;
constexpr uint32_t ATOMIC_MEM_MASK = 0x3B200C00;
constexpr uint32_t ATOMIC_MEM_INST = 0x38200000;
// Load ops are 4 bits
// Acquire and release bits are independent on the instruction
constexpr uint32_t ATOMIC_ADD_OP = 0b0000;
constexpr uint32_t ATOMIC_CLR_OP = 0b0001;
constexpr uint32_t ATOMIC_EOR_OP = 0b0010;
constexpr uint32_t ATOMIC_SET_OP = 0b0011;
constexpr uint32_t ATOMIC_SMAX_OP = 0b0100;
constexpr uint32_t ATOMIC_SMIN_OP = 0b0101;
constexpr uint32_t ATOMIC_UMAX_OP = 0b0110;
constexpr uint32_t ATOMIC_UMIN_OP = 0b0111;
constexpr uint32_t ATOMIC_SWAP_OP = 0b1000;
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr);
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr);
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr);
}
@@ -0,0 +1,220 @@
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Core/CoreState.h>
#include "aarch64/cpu-aarch64.h"
namespace FEXCore::CPU {
#define STATE x28
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
Arm64Emitter::Arm64Emitter(size_t size) : vixl::aarch64::Assembler(size, vixl::aarch64::PositionDependentCode) {
CPU.SetUp();
auto Features = vixl::CPUFeatures::InferFromOS();
SupportsAtomics = Features.Has(vixl::CPUFeatures::Feature::kAtomics);
// RCPC is bugged on Snapdragon 865
// Causes glibc cond16 test to immediately throw assert
// __pthread_mutex_cond_lock: Assertion `mutex->__data.__owner == 0'
SupportsRCPC = false; //Features.Has(vixl::CPUFeatures::Feature::kRCpc);
if (SupportsAtomics) {
// Hypervisor can hide this on the c630?
Features.Combine(vixl::CPUFeatures::Feature::kLORegions);
}
SetCPUFeatures(Features);
if (!SupportsAtomics) {
WARN_ONCE("Host CPU doesn't support atomics. Expect bad performance");
}
}
void Arm64Emitter::LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant) {
bool Is64Bit = Reg.IsX();
int Segments = Is64Bit ? 4 : 2;
if (Is64Bit && ((~Constant)>> 16) == 0) {
movn(Reg, (~Constant) & 0xFFFF);
return;
}
movz(Reg, (Constant) & 0xFFFF, 0);
for (int i = 1; i < Segments; ++i) {
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
if (Part) {
movk(Reg, Part, i * 16);
}
}
}
void Arm64Emitter::PushCalleeSavedRegisters() {
// We need to save pairs of registers
// We save r19-r30
MemOperand PairOffset(sp, -16, PreIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x19, x20},
{x21, x22},
{x23, x24},
{x25, x26},
{x27, x28},
{x29, x30},
}};
for (auto &RegPair : CalleeSaved) {
stp(RegPair.first, RegPair.second, PairOffset);
}
// Additionally we need to store the lower 64bits of v8-v15
// Here's a fun thing, we can use two ST4 instructions to store everything
// We just need a single sub to sp before that
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v8, v9, v10, v11},
{v12, v13, v14, v15},
}};
uint32_t VectorSaveSize = sizeof(uint64_t) * 8;
sub(sp, sp, VectorSaveSize);
// SP supporting move
// We just saved x19 so it is safe
add(x19, sp, 0);
MemOperand QuadOffset(x19, 32, PostIndex);
for (auto &RegQuad : FPRs) {
st4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
}
void Arm64Emitter::PopCalleeSavedRegisters() {
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v12, v13, v14, v15},
{v8, v9, v10, v11},
}};
MemOperand QuadOffset(sp, 32, PostIndex);
for (auto &RegQuad : FPRs) {
ld4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
MemOperand PairOffset(sp, 16, PostIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x29, x30},
{x27, x28},
{x25, x26},
{x23, x24},
{x21, x22},
{x19, x20},
}};
for (auto &RegPair : CalleeSaved) {
ldp(RegPair.first, RegPair.second, PairOffset);
}
}
void Arm64Emitter::SpillStaticRegs() {
for (size_t i = 0; i < SRA64.size(); i+=2) {
stp(SRA64[i], SRA64[i+1], MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i])));
}
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
stp(SRAFPR[i].Q(), SRAFPR[i+1].Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
}
}
void Arm64Emitter::FillStaticRegs() {
for (size_t i = 0; i < SRA64.size(); i+=2) {
ldp(SRA64[i], SRA64[i+1], MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i])));
}
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
ldp(SRAFPR[i].Q(), SRAFPR[i+1].Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
}
}
void Arm64Emitter::PushDynamicRegsAndLR() {
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
sub(sp, sp, SPOffset);
int i = 0;
for (auto RA : RAFPR)
{
str(RA.Q(), MemOperand(sp, i * 8));
i+=2;
}
#if 0 // All GPRs should be caller saved
for (auto RA : RA64)
{
str(RA, MemOperand(sp, i * 8));
i++;
}
#endif
str(lr, MemOperand(sp, i * 8));
}
void Arm64Emitter::PopDynamicRegsAndLR() {
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
int i = 0;
for (auto RA : RAFPR)
{
ldr(RA.Q(), MemOperand(sp, i * 8));
i+=2;
}
#if 0 // All GPRs should be caller saved
for (auto RA : RA64)
{
ldr(RA, MemOperand(sp, i * 8));
i++;
}
#endif
ldr(lr, MemOperand(sp, i * 8));
add(sp, sp, SPOffset);
}
void Arm64Emitter::ResetStack() {
if (SpillSlots == 0)
return;
if (IsImmAddSub(SpillSlots * 16)) {
add(sp, sp, SpillSlots * 16);
} else {
// Too big to fit in a 12bit immediate
LoadConstant(x0, SpillSlots * 16);
add(sp, sp, x0);
}
}
void Arm64Emitter::Align16B() {
uint64_t CurrentOffset = GetBuffer()->GetOffsetAddress<uint64_t>(GetCursorOffset());
for (uint64_t i = (16 - (CurrentOffset & 0xF)); i != 0; i -= 4) {
nop();
}
}
}
@@ -0,0 +1,74 @@
#pragma once
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
// All but x29 are caller saved
const std::array<aarch64::Register, 16> SRA64 = {
x4, x5, x6, x7, x8, x9, x10, x11,
x12, x18, x17, x16, x15, x14, x13, x29
};
// All are callee saved
const std::array<aarch64::Register, 9> RA64 = {
x20, x21, x22, x23, x24, x25, x26, x27,
x19
};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA64Pair = {{
{x20, x21},
{x22, x23},
{x24, x25},
{x26, x27},
}};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA32Pair = {{
{w20, w21},
{w22, w23},
{w24, w25},
{w26, w27},
}};
// All are caller saved
const std::array<aarch64::VRegister, 16> SRAFPR = {
v16, v17, v18, v19, v20, v21, v22, v23,
v24, v25, v26, v27, v28, v29, v30, v31
};
// v8..v15 = (lower 64bits) Callee saved
const std::array<aarch64::VRegister, 12> RAFPR = {
/*v0, v1, v2, v3,*/v4, v5, v6, v7, // v0 ~ v3 are used as temps
v8, v9, v10, v11, v12, v13, v14, v15
};
// This class contains common emitter utility functions that can
// be used by both Arm64 JIT and ARM64 Dispatcher
class Arm64Emitter : public vixl::aarch64::Assembler {
protected:
Arm64Emitter(size_t size);
vixl::aarch64::CPU CPU;
bool SupportsAtomics{};
bool SupportsRCPC{};
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
void SpillStaticRegs();
void FillStaticRegs();
void PushDynamicRegsAndLR();
void PopDynamicRegsAndLR();
void PushCalleeSavedRegisters();
void PopCalleeSavedRegisters();
void ResetStack();
void Align16B();
uint32_t SpillSlots{};
};
}
@@ -0,0 +1,26 @@
#include "Interface/Core/ArchHelpers/Arm64.h"
#include <FEXCore/Utils/LogManager.h>
namespace FEXCore::ArchHelpers::Arm64 {
#ifndef _M_ARM_64
// These are stub implementations that exist only to allow instantiating the arm64 jit
// on non arm platforms.
// Obvously such a configuration can't do the actual arm64-specific stuff
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) {
ERROR_AND_DIE("HandleCASPAL Not Implemented");
}
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr) {
ERROR_AND_DIE("HandleCASAL Not Implemented");
}
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) {
ERROR_AND_DIE("HandleAtomicMemOp Not Implemented");
}
#endif
}
@@ -0,0 +1,212 @@
#pragma once
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/UContext.h>
#include <signal.h>
#include <string.h>
#include <ucontext.h>
#include <stdint.h>
#include <type_traits>
namespace FEXCore::ArchHelpers::Context {
struct X86ContextBackup {
// Host State
// RIP and RSP is stored in GPRs here
uint64_t GPRs[23];
FEXCore::x86_64::_libc_fpstate FPRState;
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
static constexpr int RedZoneSize = 128;
};
struct ArmContextBackup {
// Host State
uint64_t GPRs[31];
uint64_t PrevSP;
uint64_t PrevPC;
uint64_t PState;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
// Arm64 doesn't have a red zone
static constexpr int RedZoneSize = 0;
};
static inline mcontext_t* GetMContext(void* ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
return &_context->uc_mcontext;
}
#ifdef _M_ARM_64
static inline uint64_t GetSp(void* ucontext) {
return GetMContext(ucontext)->sp;
}
static inline uint64_t GetPc(void* ucontext) {
return GetMContext(ucontext)->pc;
}
static inline void SetSp(void* ucontext, uint64_t val) {
GetMContext(ucontext)->sp = val;
}
static inline void SetPc(void* ucontext, uint64_t val) {
GetMContext(ucontext)->pc = val;
}
static inline uint64_t GetState(void* ucontext) {
return GetMContext(ucontext)->regs[28];
}
static inline void SetState(void* ucontext, uint64_t val) {
GetMContext(ucontext)->regs[28] = val;
}
static inline uint64_t GetArmReg(void* ucontext, uint32_t id) {
return GetMContext(ucontext)->regs[id];
}
static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) {
GetMContext(ucontext)->regs[id] = val;
}
constexpr uint32_t FPR_MAGIC = 0x46508001U;
struct HostCTXHeader {
uint32_t Magic;
uint32_t Size;
};
struct HostFPRState {
HostCTXHeader Head;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
};
using ContextBackup = ArmContextBackup;
template <typename T>
static inline void BackupContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, ArmContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
memcpy(&Backup->GPRs[0], &_mcontext->regs[0], 31 * sizeof(uint64_t));
Backup->PrevSP = ArchHelpers::Context::GetSp(ucontext);
Backup->PrevPC = ArchHelpers::Context::GetPc(ucontext);
Backup->PState = _mcontext->pstate;
// Host FPR state starts at _mcontext->reserved[0];
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
Backup->FPSR = HostState->FPSR;
Backup->FPCR = HostState->FPCR;
memcpy(&Backup->FPRs[0], &HostState->FPRs[0], 32 * sizeof(__uint128_t));
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
template <typename T>
static inline void RestoreContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, ArmContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
memcpy(&HostState->FPRs[0], &Backup->FPRs[0], 32 * sizeof(__uint128_t));
HostState->FPCR = Backup->FPCR;
HostState->FPSR = Backup->FPSR;
// Restore GPRs and other state
_mcontext->pstate = Backup->PState;
ArchHelpers::Context::SetPc(ucontext, Backup->PrevPC);
ArchHelpers::Context::SetSp(ucontext, Backup->PrevSP);
memcpy(&_mcontext->regs[0], &Backup->GPRs[0], 31 * sizeof(uint64_t));
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
#endif
#ifdef _M_X86_64
static inline uint64_t GetSp(void* ucontext) {
return GetMContext(ucontext)->gregs[REG_RSP];
}
static inline uint64_t GetPc(void* ucontext) {
return GetMContext(ucontext)->gregs[REG_RIP];
}
static inline void SetSp(void* ucontext, uint64_t val) {
GetMContext(ucontext)->gregs[REG_RSP] = val;
}
static inline void SetPc(void* ucontext, uint64_t val) {
GetMContext(ucontext)->gregs[REG_RIP] = val;
}
static inline uint64_t GetState(void* ucontext) {
return GetMContext(ucontext)->gregs[REG_R14];
}
static inline void SetState(void* ucontext, uint64_t val) {
GetMContext(ucontext)->gregs[REG_R14] = val;
}
static inline uint64_t GetArmReg(void* ucontext, uint32_t id) {
ERROR_AND_DIE("Not impelented for x86 host");
}
static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) {
ERROR_AND_DIE("Not impelented for x86 host");
}
using ContextBackup = X86ContextBackup;
template <typename T>
static inline void BackupContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, X86ContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
// Copy the GPRs
memcpy(&Backup->GPRs[0], &_mcontext->gregs[0], sizeof(X86ContextBackup::GPRs));
// Copy the FPRState
memcpy(&Backup->FPRState, _mcontext->fpregs, sizeof(X86ContextBackup::FPRState));
// XXX: Save 256bit and 512bit AVX register state
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
template <typename T>
static inline void RestoreContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, X86ContextBackup>::value) {
auto _mcontext = GetMContext(ucontext);
// Copy the GPRs
memcpy(&_mcontext->gregs[0], &Backup->GPRs[0], sizeof(X86ContextBackup::GPRs));
// Copy the FPRState
memcpy(_mcontext->fpregs, &Backup->FPRState, sizeof(X86ContextBackup::FPRState));
} else {
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
}
}
#endif
} // namespace FEXCore::ArchHelpers::Context
+151 -4
View File
@@ -1,11 +1,42 @@
/*
$info$
tags: opcodes|cpuid
desc: Handles presented capability bits for guest cpu
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUID.h"
#include "git_version.h"
#include <cstring>
#ifdef _M_X86_64
#include <cpuid.h>
#endif
namespace FEXCore {
//#define CPUID_AMD
#ifdef _M_ARM_64
static uint32_t GetCycleCounterFrequency() {
uint64_t Result{};
__asm("mrs %[Res], CNTFRQ_EL0"
: [Res] "=r" (Result));
return Result;
}
#else
static uint32_t GetCycleCounterFrequency() {
uint32_t eax, ebx, ecx, edx;
__cpuid(0, eax, ebx, ecx, edx);
if (eax >= 0x15) {
__cpuid(0x15, eax, ebx, ecx, edx);
if (eax && ebx && ecx) {
return ecx * ebx / eax;
}
}
return 0;
}
#endif
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_0h() {
FEXCore::CPUID::FunctionResults Res{};
@@ -109,6 +140,31 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h() {
return Res;
}
// 2: Cache and TLB information
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_02h() {
FEXCore::CPUID::FunctionResults Res{};
// returns default values from i7 model 1Ah
Res.eax = 0x1 | // Number of iterations needed for all descriptors
(0x5A << 8) |
(0x03 << 16) |
(0x55 << 24);
Res.ebx = 0xE4 |
(0xB2 << 8) |
(0xF0 << 16) |
(0 << 24);
Res.ecx = 0; // null descriptors
Res.edx = 0x2C |
(0x21 << 8) |
(0xCA << 16) |
(0x09 << 24);
return Res;
}
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_06h() {
FEXCore::CPUID::FunctionResults Res{};
Res.eax = (1 << 2); // Always running APIC
@@ -226,6 +282,18 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h() {
return Res;
}
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_15h() {
FEXCore::CPUID::FunctionResults Res{};
// TSC frequency = ECX * EBX / EAX
uint32_t FrequencyHz = GetCycleCounterFrequency();
if (FrequencyHz) {
Res.eax = 1;
Res.ebx = 1;
Res.ecx = FrequencyHz;
}
return Res;
}
// Highest extended function implemented
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0000h() {
FEXCore::CPUID::FunctionResults Res{};
@@ -327,8 +395,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0001h() {
}
constexpr char ProcessorBrand[48] = {
"FEX-"
GIT_SHORT_HASH
GIT_DESCRIBE_STRING
"\0"
};
@@ -351,10 +418,81 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0004h() {
return Res;
}
// L1 Cache and TLB identifiers
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0005h() {
FEXCore::CPUID::FunctionResults Res{};
// L1 TLB Information for 2MB and 4MB pages
Res.eax =
(64 << 0) | // Number of TLB instruction entries
(255 << 8) | // instruction TLB associativity type (full)
(64 << 16) | // Number of TLB data entries
(255 << 24); // data TLB associativity type (full)
// L1 TLB Information for 4KB pages
Res.ebx =
(64 << 0) | // Number of TLB instruction entries
(255 << 8) | // instruction TLB associativity type (full)
(64 << 16) | // Number of TLB data entries
(255 << 24); // data TLB associativity type (full)
// L1 data cache identifiers
Res.ecx =
(64 << 0) | // L1 data cache size line in bytes
(1 << 8) | // L1 data cachelines per tag
(8 << 16) | // L1 data cache associativity
(32 << 24); // L1 data cache size in KB
// L1 instruction cache identifiers
Res.edx =
(64 << 0) | // L1 instruction cache line size in bytes
(1 << 8) | // L1 instruction cachelines per tag
(4 << 16) | // L1 instruction cache associativity
(64 << 24); // L1 instruction cache size in KB
return Res;
}
// L2 Cache identifiers
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0006h() {
FEXCore::CPUID::FunctionResults Res{};
// L2 TLB Information for 2MB and 4MB pages
Res.eax =
(1024 << 0) | // Number of TLB instruction entries
(6 << 12) | // instruction TLB associativity type
(1536 << 16) | // Number of TLB data entries
(3 << 28); // data TLB associativity type
// L2 TLB Information for 4KB pages
Res.ebx =
(1024 << 0) | // Number of TLB instruction entries
(6 << 12) | // instruction TLB associativity type
(1536 << 16) | // Number of TLB data entries
(5 << 28); // data TLB associativity type
// L2 cache identifiers
Res.ecx =
(64 << 0) | // cacheline size
(1 << 8) | // cachelines per tag
(6 << 12) | // cache associativity
(512 << 16); // L2 cache size in KB
// L3 cache identifiers
Res.edx =
(64 << 0) | // cacheline size
(1 << 8) | // cachelines per tag
(6 << 12) | // cache associativity
(16 << 18); // L2 cache size in KB
return Res;
}
// Advanced power management
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0007h() {
FEXCore::CPUID::FunctionResults Res{};
Res.eax = (1 << 2); // APIC timer not affected by p-state
Res.edx =
(1 << 8); // Invariant TSC
return Res;
}
@@ -367,7 +505,7 @@ void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
CTX = ctx;
RegisterFunction(0, std::bind(&CPUIDEmu::Function_0h, this));
RegisterFunction(1, std::bind(&CPUIDEmu::Function_01h, this));
// 2: Cache and TLB information
RegisterFunction(2, std::bind(&CPUIDEmu::Function_02h, this));
// 3: Serial Number(previously), now reserved
// 4: Deterministic cache parameters for each level
// 5: Monitor/mwait
@@ -384,7 +522,11 @@ void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
// 0x12: Intel SGX capability enumeration
// 0x13: Reserved
// 0x14: Intel Processor trace
// 0x15: Timestamp counter information
#ifndef CPUID_AMD
// Timestamp counter information
// Doesn't exist on AMD hardware
RegisterFunction(0x15, std::bind(&CPUIDEmu::Function_15h, this));
#endif
// 0x16: Processor frequency information
// 0x17: SoC vendor attribute enumeration
@@ -399,7 +541,12 @@ void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
// Processor brand string continued
RegisterFunction(0x8000'0004, std::bind(&CPUIDEmu::Function_8000_0004h, this));
// 0x8000'0005: L1 Cache and TLB identifiers
#ifdef CPUID_AMD
// This is full reserved on Intel platforms
RegisterFunction(0x8000'0005, std::bind(&CPUIDEmu::Function_8000_0005h, this));
#endif
// 0x8000'0006: L2 Cache identifiers
RegisterFunction(0x8000'0006, std::bind(&CPUIDEmu::Function_8000_0006h, this));
// Advanced power management information
RegisterFunction(0x8000'0007, std::bind(&CPUIDEmu::Function_8000_0007h, this));
// 0x8000'0008: Virtual and physical address sizes
+11 -2
View File
@@ -3,6 +3,7 @@
#include <unordered_map>
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/Utils/LogManager.h>
namespace FEXCore {
namespace Context {
@@ -22,11 +23,15 @@ private:
public:
void Init(FEXCore::Context::Context *ctx);
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function) {
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, [[maybe_unused]] uint32_t Leaf) {
auto Handler = FunctionHandlers.find(Function);
if (Handler == FunctionHandlers.end())
if (Handler == FunctionHandlers.end()) {
#ifndef NDEBUG
LogMan::Msg::E("Unhandled CPU ID function, 0x%x", Function);
#endif
return Function_Reserved();
}
return Handler->second();
}
@@ -43,13 +48,17 @@ private:
// Functions
FEXCore::CPUID::FunctionResults Function_0h();
FEXCore::CPUID::FunctionResults Function_01h();
FEXCore::CPUID::FunctionResults Function_02h();
FEXCore::CPUID::FunctionResults Function_06h();
FEXCore::CPUID::FunctionResults Function_07h();
FEXCore::CPUID::FunctionResults Function_15h();
FEXCore::CPUID::FunctionResults Function_8000_0000h();
FEXCore::CPUID::FunctionResults Function_8000_0001h();
FEXCore::CPUID::FunctionResults Function_8000_0002h();
FEXCore::CPUID::FunctionResults Function_8000_0003h();
FEXCore::CPUID::FunctionResults Function_8000_0004h();
FEXCore::CPUID::FunctionResults Function_8000_0005h();
FEXCore::CPUID::FunctionResults Function_8000_0006h();
FEXCore::CPUID::FunctionResults Function_8000_0007h();
FEXCore::CPUID::FunctionResults Function_Reserved();
+28 -42
View File
@@ -1,10 +1,16 @@
#include "Interface/Context/Context.h"
#include "Interface/Core/BlockCache.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/CompileService.h"
#include "Interface/Core/InternalThreadState.h"
#include "Interface/Core/OpcodeDispatcher.h"
namespace FEXCore {
static void* ThreadHandler(void *Arg) {
FEXCore::CompileService *This = reinterpret_cast<FEXCore::CompileService*>(Arg);
This->ExecutionThread();
return nullptr;
}
CompileService::CompileService(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: CTX {ctx}
, ParentThread {Thread} {
@@ -16,9 +22,7 @@ namespace FEXCore {
CTX->InitializeCompiler(CompileThreadData.get(), true);
CompileThreadData->CPUBackend->CopyNecessaryDataForCompileThread(ParentThread->CPUBackend.get());
WorkerThread = std::thread([this]() {
ExecutionThread();
});
WorkerThread = FEXCore::Threads::Thread::Create(ThreadHandler, this);
}
void CompileService::Initialize() {
@@ -30,10 +34,10 @@ namespace FEXCore {
ShuttingDown = true;
// Kick the working thread
StartWork.NotifyAll();
WorkerThread.join();
WorkerThread->join(nullptr);
}
void CompileService::ClearCache(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
void CompileService::ClearCache(FEXCore::Core::InternalThreadState *Thread) {
// On cache clear we need to spin down the execution thread to ensure it isn't trying to give us more work items
if (CompileMutex.try_lock()) {
// We can only clear these things if we pulled the compile mutex
@@ -57,30 +61,17 @@ namespace FEXCore {
}
}
if (GuestRIP == 0) {
CompileThreadData->IRLists.clear();
}
else {
auto IR = CompileThreadData->IRLists.find(GuestRIP)->second.release();
CompileThreadData->IRLists.clear();
CompileThreadData->IRLists.try_emplace(GuestRIP, IR);
}
LogMan::Throw::A(CompileThreadData->LocalIRCache.size() == 0, "Compile service must never have LocalIRCache");
CompileMutex.unlock();
}
// Clear the inverse cache of what is calling us from the Context ClearCache routine
auto SelectedThread = Thread->IsCompileService ? ParentThread : Thread;
SelectedThread->BlockCache->ClearCache();
SelectedThread->LookupCache->ClearCache();
SelectedThread->CPUBackend->ClearCache();
SelectedThread->IntBackend->ClearCache();
}
void CompileService::RemoveCodeEntry(uint64_t GuestRIP) {
CompileThreadData->IRLists.erase(GuestRIP);
CompileThreadData->DebugData.erase(GuestRIP);
CompileThreadData->BlockCache->Erase(GuestRIP);
}
CompileService::WorkItem *CompileService::CompileCode(uint64_t RIP) {
// Tell the worker thread to compile code for us
@@ -105,7 +96,7 @@ namespace FEXCore {
// Set our thread name so we can see its relation
char ThreadName[16]{};
snprintf(ThreadName, 16, "%ld-CS", ParentThread->State.ThreadManager.TID.load());
snprintf(ThreadName, 16, "%ld-CS", ParentThread->ThreadManager.TID.load());
pthread_setname_np(pthread_self(), ThreadName);
while (true) {
@@ -132,33 +123,28 @@ namespace FEXCore {
// If we had a work item then work on it
if (Item) {
// Does the block cache already contain this RIP?
void *CompiledCode = reinterpret_cast<void*>(CompileThreadData->BlockCache->FindBlock(Item->RIP));
FEXCore::Core::DebugData *DebugData = nullptr;
// Make sure it's not in lookup cache by accident
LogMan::Throw::A(CompileThreadData->LookupCache->FindBlock(Item->RIP) == 0, "Compile Service must never have entries in the LookupCache");
if (!CompiledCode) {
// Code isn't in cache, compile now
// Set our thread state's RIP
CompileThreadData->State.State.rip = Item->RIP;
auto [Code, Data] = CTX->CompileCode(CompileThreadData.get(), Item->RIP);
CompiledCode = Code;
DebugData = Data;
}
// Code isn't in cache, compile now
// Set our thread state's RIP
CompileThreadData->CurrentFrame->State.rip = Item->RIP;
if (!CompiledCode) {
auto [CodePtr, IRList, DebugData, RAData, Generated, StartAddr, Length] = CTX->CompileCode(CompileThreadData.get(), Item->RIP);
LogMan::Throw::A(Generated == true, "Compile Service doesn't have IR Cache");
if (!CodePtr) {
// XXX: We currently have the expectation that compile service code will be significantly smaller than regular thread's code
ERROR_AND_DIE("Couldn't compile code for thread at RIP: 0x%lx", Item->RIP);
}
auto BlockMapPtr = CompileThreadData->BlockCache->AddBlockMapping(Item->RIP, CompiledCode);
if (BlockMapPtr == 0) {
// XXX: We currently have the expectation that compiler service block cache will be significantly underutilized compared to regular thread
ERROR_AND_DIE("Couldn't add code to block cache for thread at RIP: 0x%lx", Item->RIP);
}
Item->CodePtr = CompiledCode;
Item->IRList = CompileThreadData->IRLists.find(Item->RIP)->second.get();
Item->CodePtr = CodePtr;
Item->IRList = IRList;
Item->DebugData = DebugData;
Item->RAData = RAData;
Item->StartAddr = StartAddr;
Item->Length = Length;
GCArray.emplace_back(Item);
Item->ServiceWorkDone.NotifyAll();
+13 -5
View File
@@ -2,6 +2,7 @@
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Utils/Event.h>
#include <FEXCore/Utils/Threads.h>
#include <memory>
#include <thread>
@@ -16,6 +17,9 @@ namespace Core {
struct InternalThreadState;
}
namespace IR {
class RegisterAllocationData;
};
class CompileService final {
public:
CompileService(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
@@ -28,8 +32,11 @@ class CompileService final {
// Outgoing
void *CodePtr{};
FEXCore::IR::IRListView<true> *IRList{};
FEXCore::IR::IRListView *IRList{};
FEXCore::IR::RegisterAllocationData *RAData{};
FEXCore::Core::DebugData *DebugData{};
uint64_t StartAddr;
uint64_t Length;
// Communication
Event ServiceWorkDone{};
@@ -37,15 +44,16 @@ class CompileService final {
};
WorkItem *CompileCode(uint64_t RIP);
void ClearCache(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
void RemoveCodeEntry(uint64_t GuestRIP);
void ClearCache(FEXCore::Core::InternalThreadState *Thread);
// Public for threading
void ExecutionThread();
private:
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *ParentThread;
void ExecutionThread();
std::thread WorkerThread;
std::unique_ptr<FEXCore::Threads::Thread> WorkerThread;
std::unique_ptr<FEXCore::Core::InternalThreadState> CompileThreadData;
std::mutex QueueMutex{};
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,363 @@
#include "Interface/Core/ArchHelpers/MContext.h"
#include "Interface/Core/Dispatcher/Arm64Dispatcher.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE x28
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config)
: Dispatcher(ctx, Thread), Arm64Emitter(MAX_DISPATCHER_CODE_SIZE) {
SRAEnabled = config.StaticRegisterAssignment;
SetAllowAssembler(true);
auto Buffer = GetBuffer();
DispatchPtr = Buffer->GetOffsetAddress<CPUBackend::AsmDispatch>(GetCursorOffset());
// while (true) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// Ptr();
// }
uint64_t VirtualMemorySize = Thread->LookupCache->GetVirtualMemorySize();
Literal l_VirtualMemory {VirtualMemorySize};
Literal l_PagePtr {Thread->LookupCache->GetPagePointer()};
Literal l_L1Ptr {Thread->LookupCache->GetL1Pointer()};
Literal l_CTX {reinterpret_cast<uintptr_t>(CTX)};
Literal l_Sleep {reinterpret_cast<uint64_t>(SleepThread)};
Literal l_CompileBlock {GetCompileBlockPtr()};
Literal l_ExitFunctionLink {config.ExitFunctionLink};
Literal l_ExitFunctionLinkThis {config.ExitFunctionLinkThis};
// Push all the register we need to save
PushCalleeSavedRegisters();
// Push our memory base to the correct register
// Move our thread pointer to the correct register
// This is passed in to parameter 0 (x0)
mov(STATE, x0);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
add(x0, sp, 0);
str(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)));
AbsoluteLoopTopAddressFillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled) {
FillStaticRegs();
}
// We want to ensure that we are 16 byte aligned at the top of this loop
Align16B();
aarch64::Label FullLookup{};
aarch64::Label LoopTop{};
aarch64::Label ExitSpillSRA{};
aarch64::Label ThreadPauseHandler{};
bind(&LoopTop);
AbsoluteLoopTopAddress = GetLabelAddress<uint64_t>(&LoopTop);
// Load in our RIP
// Don't modify x2 since it contains our RIP once the block doesn't exist
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
auto RipReg = x2;
if (!config.ExecuteBlocksWithCall) {
// L1 Cache
ldr(x0, &l_L1Ptr);
and_(x3, RipReg, LookupCache::L1_ENTRIES_MASK);
add(x0, x0, Operand(x3, Shift::LSL, 4));
ldp(x1, x0, MemOperand(x0));
cmp(x0, RipReg);
b(&FullLookup, Condition::ne);
br(x1);
}
// L1C check failed, do a full lookup
bind(&FullLookup);
// This is the block cache lookup routine
// It matches what is going on it LookupCache.h::FindBlock
ldr(x0, &l_PagePtr);
// Mask the address by the virtual address size so we can check for aliases
if (__builtin_popcountl(VirtualMemorySize) == 1) {
and_(x3, RipReg, Thread->LookupCache->GetVirtualMemorySize() - 1);
}
else {
ldr(x3, &l_VirtualMemory);
and_(x3, RipReg, x3);
}
aarch64::Label NoBlock;
{
// Offset the address and add to our page pointer
lsr(x1, x3, 12);
// Load the pointer from the offset
ldr(x0, MemOperand(x0, x1, Shift::LSL, 3));
// If page pointer is zero then we have no block
cbz(x0, &NoBlock);
// Steal the page offset
and_(x1, x3, 0x0FFF);
// Shift the offset by the size of the block cache entry
add(x0, x0, Operand(x1, Shift::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry))));
// Load the guest address first to ensure it maps to the address we are currently at
// This fixes aliasing problems
ldr(x1, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode)));
cmp(x1, RipReg);
b(&NoBlock, Condition::ne);
// Now load the actual host block to execute if we can
ldr(x3, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode)));
cbz(x3, &NoBlock);
// If we've made it here then we have a real compiled block
{
// Jump to the block
if (!config.ExecuteBlocksWithCall) {
// update L1 cache
ldr(x0, &l_L1Ptr);
and_(x1, RipReg, LookupCache::L1_ENTRIES_MASK);
add(x0, x0, Operand(x1, Shift::LSL, 4));
stp(x3, x2, MemOperand(x0));
br(x3);
} else {
mov(x0, STATE);
blr(x3);
}
}
if (config.ExecuteBlocksWithCall) {
// Interpreter continues execution here
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
ldr(x0, &l_CTX);
ldr(w0, MemOperand(x0, offsetof(FEXCore::Context::Context, Config.RunningMode)));
// If the value == 0 then branch to the top
cbz(x0, &LoopTop);
// Else we need to pause now
b(&ThreadPauseHandler);
}
else {
// Unconditionally loop to the top
// We will only stop on error when compiling a block or signal
b(&LoopTop);
}
}
}
{
bind(&ExitSpillSRA);
ThreadStopHandlerAddressSpillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled)
SpillStaticRegs();
ThreadStopHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
PopCalleeSavedRegisters();
// Return from the function
// LR is set to the correct return location now
ret();
}
{
ExitFunctionLinkerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled)
SpillStaticRegs();
ldr(x0, &l_ExitFunctionLinkThis);
mov(x1, STATE);
mov(x2, lr);
ldr(x3, &l_ExitFunctionLink);
blr(x3);
if (SRAEnabled)
FillStaticRegs();
br(x0);
}
// Need to create the block
{
bind(&NoBlock);
ldr(x0, &l_CTX);
mov(x1, STATE);
ldr(x3, &l_CompileBlock);
if (SRAEnabled)
SpillStaticRegs();
// X2 contains our guest RIP
blr(x3); // { CTX, Frame, RIP}
if (SRAEnabled)
FillStaticRegs();
b(&LoopTop);
}
{
SignalHandlerReturnAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// Now to get back to our old location we need to do a fault dance
// We can't use SIGTRAP here since gdb catches it and never gives it to the application!
hlt(0);
}
{
ThreadPauseHandlerAddressSpillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
if (SRAEnabled)
SpillStaticRegs();
bind(&ThreadPauseHandler);
ThreadPauseHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// We are pausing, this means the frontend should be waiting for this thread to idle
// We will have faulted and jumped to this location at this point
// Call our sleep handler
ldr(x0, &l_CTX);
mov(x1, STATE);
ldr(x2, &l_Sleep);
blr(x2);
PauseReturnInstruction = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// Fault to start running again
hlt(0);
}
{
// The expectation here is that a thunked function needs to call back in to the JIT in a reentrant safe way
// To do this safely we need to do some state tracking and register saving
//
// eg:
// JIT Call->
// Thunk->
// Thunk callback->
//
// The thunk callback needs to execute JIT code and when it returns, it needs to safely return to the thunk rather than JIT space
// This is handled by pushing a return address trampoline to the stack so when the guest address returns it hits our custom thunk return
// - This will safely return us to the thunk
//
// On return to the thunk, the thunk can get whatever its return value is from the thread context depending on ABI handling on its end
// When the thunk itself returns, it'll do its regular return logic there
// void ReentrantCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
CallbackPtr = Buffer->GetOffsetAddress<CPUBackend::JITCallback>(GetCursorOffset());
// We expect the thunk to have previously pushed the registers it was using
PushCalleeSavedRegisters();
// First thing we need to move the thread state pointer back in to our register
mov(STATE, x0);
// Make sure to adjust the refcounter so we don't clear the cache now
LoadConstant(x0, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
ldr(w2, MemOperand(x0));
add(w2, w2, 1);
str(w2, MemOperand(x0));
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
LoadConstant(x0, CTX->X86CodeGen.CallbackReturn);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
sub(x2, x2, 16);
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
str(x0, MemOperand(x2));
// Store RIP to the context state
str(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
// load static regs
if (SRAEnabled)
FillStaticRegs();
// Now go back to the regular dispatcher loop
b(&LoopTop);
}
place(&l_VirtualMemory);
place(&l_PagePtr);
place(&l_L1Ptr);
place(&l_CTX);
place(&l_Sleep);
place(&l_CompileBlock);
place(&l_ExitFunctionLink);
place(&l_ExitFunctionLinkThis);
FinalizeCode();
Start = reinterpret_cast<uint64_t>(DispatchPtr);
End = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
vixl::aarch64::CPU::EnsureIAndDCacheCoherency(reinterpret_cast<void*>(DispatchPtr), End - reinterpret_cast<uint64_t>(DispatchPtr));
GetBuffer()->SetExecutable();
#if ENABLE_JITSYMBOLS
std::string Name = "Dispatch_" + std::to_string(::gettid());
CTX->Symbols.Register(reinterpret_cast<void*>(DispatchPtr), End - reinterpret_cast<uint64_t>(DispatchPtr), Name);
#endif
}
void Arm64Dispatcher::SpillSRA(void *ucontext) {
for(int i = 0; i < SRA64.size(); i++) {
ThreadState->CurrentFrame->State.gregs[i] = ArchHelpers::Context::GetArmReg(ucontext, SRA64[i].GetCode());
}
// TODO: Also recover FPRs, not sure where the neon context is
// This is usually not needed
/*
for(int i = 0; i < SRAFPR.size(); i++) {
State->State.State.xmm[i][0] = _mcontext.neon[SRAFPR[i].GetCode()];
State->State.State.xmm[i][0] = _mcontext.neon[SRAFPR[i].GetCode()];
}
*/
}
#ifdef _M_ARM_64
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
DispatcherConfig config;
config.ExecuteBlocksWithCall = true;
Dispatcher = new Arm64Dispatcher(ctx, Thread, config);
DispatchPtr = Dispatcher->DispatchPtr;
CallbackPtr = Dispatcher->CallbackPtr;
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = Dispatcher->ReturnPtr;
}
#endif
}
@@ -0,0 +1,18 @@
#pragma once
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "aarch64/assembler-aarch64.h"
namespace FEXCore::CPU {
class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
public:
Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config);
protected:
void SpillSRA(void *ucontext) override;
};
}
@@ -0,0 +1,343 @@
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Common/MathUtils.h"
#include "Interface/Core/ArchHelpers/MContext.h"
#include <FEXCore/Core/X86Enums.h>
namespace FEXCore::CPU {
void Dispatcher::SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame) {
auto Thread = Frame->Thread;
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
// Go to sleep
Thread->StartRunning.Wait();
Thread->RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
}
void Dispatcher::StoreThreadState(int Signal, void *ucontext) {
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
uint64_t OldSP = ArchHelpers::Context::GetSp(ucontext);
uintptr_t NewSP = OldSP;
size_t StackOffset = sizeof(ArchHelpers::Context::ContextBackup);
// We need to back up behind the host's red zone
// We do this on the guest side as well
// (does nothing on arm hosts)
NewSP -= ArchHelpers::Context::ContextBackup::RedZoneSize;
NewSP -= StackOffset;
NewSP = AlignDown(NewSP, 16);
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
ArchHelpers::Context::BackupContext(ucontext, Context);
// Retain the action pointer so we can see it when we return
Context->Signal = Signal;
// Save guest state
// We can't guarantee if registers are in context or host GPRs
// So we need to save everything
memcpy(&Context->GuestState, ThreadState->CurrentFrame, sizeof(FEXCore::Core::CPUState));
// Set the new SP
ArchHelpers::Context::SetSp(ucontext, NewSP);
SignalFrames.push(NewSP);
}
void Dispatcher::RestoreThreadState(void *ucontext) {
uint64_t OldSP = SignalFrames.top();
SignalFrames.pop();
uintptr_t NewSP = OldSP;
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
// First thing, reset the guest state
memcpy(ThreadState->CurrentFrame, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
ArchHelpers::Context::RestoreContext(ucontext, Context);
// Restore the previous signal state
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
}
bool Dispatcher::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
StoreThreadState(Signal, ucontext);
auto Frame = ThreadState->CurrentFrame;
// Ref count our faults
// We use this to track if it is safe to clear cache
++SignalHandlerRefCounter;
// Set the new PC
ArchHelpers::Context::SetPc(ucontext, AbsoluteLoopTopAddressFillSRA);
// Set our state register to point to our guest thread data
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
uint64_t OldGuestSP = Frame->State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
if (!(GuestStack->ss_flags & SS_DISABLE)) {
// If our guest is already inside of the alternative stack
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
if (OldGuestSP >= AltStackBase &&
OldGuestSP <= AltStackEnd) {
// We are already in the alt stack, the rest of the code will handle adjusting this
}
else {
NewGuestSP = AltStackEnd;
}
}
// Back up past the redzone, which is 128bytes
// Don't need this offset if we aren't going to be putting siginfo in to it
NewGuestSP -= 128;
if (GuestAction->sa_flags & SA_SIGINFO) {
if (SRAEnabled) {
if (!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
LogMan::Throw::A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
} else {
// We are in jit, SRA must be spilled
SpillSRA(ucontext);
}
}
// Setup ucontext a bit
if (CTX->Config.Is64BitMode) {
NewGuestSP -= sizeof(FEXCore::x86_64::ucontext_t);
uint64_t UContextLocation = NewGuestSP;
FEXCore::x86_64::ucontext_t *guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
// We have extended float information
guest_uctx->uc_flags |= FEXCore::x86_64::UC_FP_XSTATE;
// Pointer to where the fpreg memory is
guest_uctx->uc_mcontext.fpregs = &guest_uctx->__fpregs_mem;
#define COPY_REG(x) \
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x] = Frame->State.gregs[X86State::REG_##x];
COPY_REG(R8);
COPY_REG(R9);
COPY_REG(R10);
COPY_REG(R11);
COPY_REG(R12);
COPY_REG(R13);
COPY_REG(R14);
COPY_REG(R15);
COPY_REG(RDI);
COPY_REG(RSI);
COPY_REG(RBP);
COPY_REG(RBX);
COPY_REG(RDX);
COPY_REG(RAX);
COPY_REG(RCX);
COPY_REG(RSP);
#undef COPY_REG
// Copy float registers
memcpy(guest_uctx->__fpregs_mem._st, Frame->State.mm, sizeof(Frame->State.mm));
memcpy(guest_uctx->__fpregs_mem._xmm, Frame->State.xmm, sizeof(Frame->State.xmm));
// FCW store default
guest_uctx->__fpregs_mem.fcw = Frame->State.FCW;
// Reconstruct FSW
guest_uctx->__fpregs_mem.fsw =
(Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14);
// Copy over signal stack information
guest_uctx->uc_stack.ss_flags = GuestStack->ss_flags;
guest_uctx->uc_stack.ss_sp = GuestStack->ss_sp;
guest_uctx->uc_stack.ss_size = GuestStack->ss_size;
// XXX: siginfo_t(RSI)
Frame->State.gregs[X86State::REG_RSI] = 0x4142434445460000;
Frame->State.gregs[X86State::REG_RDX] = UContextLocation;
}
else {
// XXX: 32bit Support
NewGuestSP -= sizeof(FEXCore::x86::ucontext_t);
uint64_t UContextLocation = 0; // NewGuestSP;
NewGuestSP -= sizeof(FEXCore::x86::siginfo_t);
uint64_t SigInfoLocation = 0; // NewGuestSP;
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = UContextLocation;
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = SigInfoLocation;
}
Frame->State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
}
else {
Frame->State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
}
if (CTX->Config.Is64BitMode) {
Frame->State.gregs[X86State::REG_RDI] = Signal;
// Set up the new SP for stack handling
NewGuestSP -= 8;
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
Frame->State.gregs[X86State::REG_RSP] = NewGuestSP;
}
else {
NewGuestSP -= 4;
*(uint32_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
LogMan::Throw::A(CTX->X86CodeGen.SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
Frame->State.gregs[X86State::REG_RSP] = NewGuestSP;
}
return true;
}
bool Dispatcher::HandleSIGILL(int Signal, void *info, void *ucontext) {
if (ArchHelpers::Context::GetPc(ucontext) == SignalHandlerReturnAddress) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
return true;
}
if (ArchHelpers::Context::GetPc(ucontext) == PauseReturnInstruction) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
return true;
}
return false;
}
bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
FEXCore::Core::SignalEvent SignalReason = ThreadState->SignalReason.load();
auto Frame = ThreadState->CurrentFrame;
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
if (SRAEnabled && IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
// We are in jit, SRA must be spilled
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddressSpillSRA);
} else {
if (SRAEnabled) {
// We are in non-jit, SRA is already spilled
LogMan::Throw::A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
}
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddress);
}
// Set the new PC
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddress);
// Set our state register to point to our guest thread data
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
// Ref count our faults
// We use this to track if it is safe to clear cache
++SignalHandlerRefCounter;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
// Our thread is stopping
// We don't care about anything at this point
// Set the stack to our starting location when we entered the core and get out safely
ArchHelpers::Context::SetSp(ucontext, Frame->ReturningStackLocation);
// Our ref counting doesn't matter anymore
SignalHandlerRefCounter = 0;
// Set the new PC
if (SRAEnabled && IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
// We are in jit, SRA must be spilled
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddressSpillSRA);
} else {
if (SRAEnabled) {
// We are in non-jit, SRA is already spilled
LogMan::Throw::A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
}
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddress);
}
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
RestoreThreadState(ucontext);
// Ref count our faults
// We use this to track if it is safe to clear cache
--SignalHandlerRefCounter;
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
return false;
}
uint64_t Dispatcher::GetCompileBlockPtr() {
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast CompileBlockPtr;
CompileBlockPtr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
return CompileBlockPtr.Data;
}
void Dispatcher::RemoveCodeBuffer(uint8_t* start_to_remove) {
for (auto iter = CodeBuffers.begin(); iter != CodeBuffers.end(); ++iter) {
auto [start, end] = *iter;
if (start == reinterpret_cast<uint64_t>(start_to_remove)) {
CodeBuffers.erase(iter);
return;
}
}
}
bool Dispatcher::IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher) {
for (auto [start, end] : CodeBuffers) {
if (Address >= start && Address < end) {
return true;
}
}
if (IncludeDispatcher) {
return IsAddressInDispatcher(Address);
}
return false;
}
}
@@ -0,0 +1,84 @@
#pragma once
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Core/SignalDelegator.h>
#include "Interface/Context/Context.h"
#include <stack>
namespace FEXCore::CPU {
struct DispatcherConfig {
bool ExecuteBlocksWithCall = false;
uintptr_t ExitFunctionLink = 0;
uintptr_t ExitFunctionLinkThis = 0;
bool StaticRegisterAssignment = false;
};
class Dispatcher {
public:
CPUBackend::AsmDispatch DispatchPtr;
CPUBackend::JITCallback CallbackPtr;
FEXCore::Context::Context::IntCallbackReturn ReturnPtr;
/**
* @name Dispatch Helper functions
* @{ */
uint64_t ThreadStopHandlerAddress{};
uint64_t ThreadStopHandlerAddressSpillSRA{};
uint64_t AbsoluteLoopTopAddress{};
uint64_t AbsoluteLoopTopAddressFillSRA{};
uint64_t ThreadPauseHandlerAddress{};
uint64_t ThreadPauseHandlerAddressSpillSRA{};
uint64_t ExitFunctionLinkerAddress{};
uint64_t SignalHandlerReturnAddress{};
uint64_t PauseReturnInstruction{};
/** @} */
uint32_t SignalHandlerRefCounter{};
uint64_t Start{};
uint64_t End{};
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
bool HandleSIGILL(int Signal, void *info, void *ucontext);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
void RegisterCodeBuffer(uint8_t* start, size_t size) {
CodeBuffers.emplace_back(reinterpret_cast<uint64_t>(start),
reinterpret_cast<uint64_t>(start + size));
}
void RemoveCodeBuffer(uint8_t* start);
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
bool IsAddressInDispatcher(uint64_t Address) {
return Address >= Start && Address < End;
}
protected:
Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: CTX {ctx}
, ThreadState {Thread} {}
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
std::stack<uint64_t> SignalFrames;
bool SRAEnabled = false;
virtual void SpillSRA(void *ucontext) {}
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *ThreadState;
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame);
static uint64_t GetCompileBlockPtr();
private:
std::vector<std::tuple<uint64_t, uint64_t>> CodeBuffers; // Start, End
};
}
@@ -0,0 +1,325 @@
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
namespace FEXCore::CPU {
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE r14
X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config)
: Dispatcher(ctx, Thread)
, Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE) {
using namespace Xbyak;
using namespace Xbyak::util;
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
// Temp registers
// rax, rcx, rdx, rsi, r8, r9,
// r10, r11
//
// Callee Saved
// rbx, rbp, r12, r13, r14, r15
//
// 1St Argument: rdi <ThreadState>
// XMM:
// All temp
// while (true) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Bunch of exit state stuff
// x86-64 ABI has the stack aligned when /call/ happens
// Which means the destination has a misaligned stack at that point
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
mov(STATE, rdi);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
mov(qword [rdi + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)], rsp);
Label LoopTop;
Label FullLookup;
Label NoBlock;
Label ExitBlock;
Label ThreadPauseHandler;
L(LoopTop);
AbsoluteLoopTopAddressFillSRA = AbsoluteLoopTopAddress = getCurr<uint64_t>();
{
// Load our RIP
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
if (!config.ExecuteBlocksWithCall)
{
// L1 Cache
mov(r13, Thread->LookupCache->GetL1Pointer());
mov(rax, rdx);
and_(rax, LookupCache::L1_ENTRIES_MASK);
shl(rax, 4);
cmp(qword[r13 + rax + 8], rdx);
jne(FullLookup);
jmp(qword[r13 + rax + 0]);
}
L(FullLookup);
mov(r13, Thread->LookupCache->GetPagePointer());
// Full lookup
mov(rax, rdx);
mov(rbx, Thread->LookupCache->GetVirtualMemorySize() - 1);
and_(rax, rbx);
shr(rax, 12);
// Load page pointer
mov(rdi, qword [r13 + rax * 8]);
cmp(rdi, 0);
je(NoBlock);
mov (rax, rdx);
and_(rax, 0x0FFF);
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
// check for aliasing
mov(rcx, qword [rdi + rax + 8]);
cmp(rcx, rdx);
jne(NoBlock);
// Load the block pointer
mov(rax, qword [rdi + rax]);
cmp(rax, 0);
je(NoBlock);
// Update L1
if (config.ExecuteBlocksWithCall) {
mov(r13, Thread->LookupCache->GetL1Pointer());
mov(rcx, rdx);
and_(rcx, LookupCache::L1_ENTRIES_MASK);
shl(rcx, 1);
mov(qword[r13 + rcx*8 + 8], rdx);
mov(qword[r13 + rcx*8 + 0], rax);
}
// Real block if we made it here
if (!config.ExecuteBlocksWithCall) {
jmp(rax);
} else {
mov(rdi, STATE);
call(rax);
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
// If the value == 0 then branch to the top
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
je(LoopTop);
// Else we need to pause now
jmp(ThreadPauseHandler);
ud2();
}
else {
jmp(LoopTop);
}
}
}
{
L(ExitBlock);
ThreadStopHandlerAddress = getCurr<uint64_t>();
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
// Block creation
{
L(NoBlock);
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
// {rdi, rsi, rdx}
mov(rdi, reinterpret_cast<uint64_t>(CTX));
mov(rsi, STATE);
mov(rax, Ptr.Data);
call(rax);
// rdx already contains RIP here
jmp(LoopTop);
}
{
ExitFunctionLinkerAddress = getCurr<uint64_t>();
// {rdi, rsi, rdx}
mov(rdi, config.ExitFunctionLinkThis);
mov(rsi, STATE);
mov(rdx, rax); // rax is set at the block end
mov(rax, config.ExitFunctionLink);
call(rax);
jmp(rax);
}
{
// Pause handler
ThreadPauseHandlerAddress = getCurr<uint64_t>();
L(ThreadPauseHandler);
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
mov(rsi, STATE);
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
call(rax);
// XXX: Unsupported atm
PauseReturnInstruction = getCurr<uint64_t>();
ud2();
}
{
CallbackPtr = getCurr<CPUBackend::JITCallback>();
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
// First thing we need to move the thread state pointer back in to our register
mov(STATE, rdi);
// XXX: XMM?
// Make sure to adjust the refcounter so we don't clear the cache now
mov(rax, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
add(dword [rax], 1);
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
mov(rax, CTX->X86CodeGen.CallbackReturn);
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
sub(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])], 16);
mov(rbx, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])]);
mov(qword [rbx], rax);
// Store RIP to the context state
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.rip)], rsi);
// Back to the loop top now
jmp(LoopTop);
}
{
// Signal return handler
SignalHandlerReturnAddress = getCurr<uint64_t>();
ud2();
}
{
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// rdi = thread
// rsi = rsp
mov(rsp, rsi);
// Now jump back to the thunk
// XXX: XMM?
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
ready();
Start = reinterpret_cast<uint64_t>(getCode());
End = Start + getSize();
#if ENABLE_JITSYMBOLS
std::string Name = "Dispatch_" + std::to_string(::gettid());
CTX->Symbols.Register(Start, End-Start, Name);
#endif
}
X86Dispatcher::~X86Dispatcher() {
}
#ifdef _M_X86_64
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
DispatcherConfig config;
config.ExecuteBlocksWithCall = true;
Dispatcher = new X86Dispatcher(ctx, Thread, config);
DispatchPtr = Dispatcher->DispatchPtr;
CallbackPtr = Dispatcher->CallbackPtr;
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = Dispatcher->ReturnPtr;
}
#endif
}
@@ -0,0 +1,17 @@
#pragma once
#include "Interface/Core/Dispatcher/Dispatcher.h"
#define XBYAK64
#include <xbyak/xbyak.h>
namespace FEXCore::CPU {
class X86Dispatcher final : public Dispatcher, public Xbyak::CodeGenerator {
public:
X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config);
virtual ~X86Dispatcher() override;
};
}
+106 -79
View File
@@ -1,3 +1,10 @@
/*
$info$
tags: frontend|x86-meta-blocks
desc: Extracts instruction & block meta info, frontend multiblock logic
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Frontend.h"
#include "Interface/Core/InternalThreadState.h"
@@ -129,26 +136,17 @@ uint8_t Decoder::PeekByte(uint8_t Offset) {
}
uint64_t Decoder::ReadData(uint8_t Size) {
uint64_t Res{};
#define READ_DATA(x, y) \
case x: { \
y const *Data = reinterpret_cast<y const*>(&InstStream[InstructionSize]); \
Res = *Data; \
} \
break
switch (Size) {
case 0: return 0;
READ_DATA(1, uint8_t);
READ_DATA(2, uint16_t);
case 3: memcpy(&Res, &InstStream[InstructionSize], Size);
READ_DATA(4, uint32_t);
READ_DATA(8, uint64_t);
default:
LogMan::Msg::A("Unknown data size to read");
return 0;
if (Size == 0) {
return 0;
}
#undef READ_DATA
if (Size > sizeof(uint64_t)) {
LogMan::Msg::A("Unknown data size to read");
return 0;
}
uint64_t Res = 0;
std::memcpy(&Res, &InstStream[InstructionSize], Size);
#ifndef NDEBUG
for(size_t i = 0; i < Size; ++i) {
@@ -157,6 +155,7 @@ uint64_t Decoder::ReadData(uint8_t Size) {
#else
SkipBytes(Size);
#endif
return Res;
}
@@ -329,9 +328,21 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
DecodeInst->TableInfo = Info;
// XXX: Once we support 32bit x86 then this will be necessary to support
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_LEGACY_PREFIX, "Legacy Prefix");
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_UNKNOWN, "Invalid or Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_INVALID, "Invalid or Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
if (Info->Type == FEXCore::X86Tables::TYPE_LEGACY_PREFIX) {
LogMan::Msg::D("Legacy Prefix");
return false;
}
if (Info->Type == FEXCore::X86Tables::TYPE_UNKNOWN) {
LogMan::Msg::D("Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
return false;
}
if (Info->Type == FEXCore::X86Tables::TYPE_INVALID) {
LogMan::Msg::D("Invalid or Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
return false;
}
LogMan::Throw::A(!(Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 && Info->Type <= FEXCore::X86Tables::TYPE_GROUP_P),
"Group Ops should have been decoded before this!");
@@ -565,9 +576,22 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
DecodeInst->TableInfo = Info;
// XXX: Once we support 32bit x86 then this will be necessary to support
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_LEGACY_PREFIX, "Legacy Prefix");
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_UNKNOWN, "Invalid or Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_INVALID, "Invalid or Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
if (Info->Type == FEXCore::X86Tables::TYPE_LEGACY_PREFIX) {
LogMan::Msg::D("Legacy Prefix");
return false;
}
if (Info->Type == FEXCore::X86Tables::TYPE_UNKNOWN) {
LogMan::Msg::D("Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
return false;
}
if (Info->Type == FEXCore::X86Tables::TYPE_INVALID) {
LogMan::Msg::D("Invalid or Unknown instruction: %s 0x%04x 0x%lx", Info->Name, Op, DecodeInst->PC);
return false;
}
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX, "REX PREFIX should have been decoded before this!");
if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
Info->Type <= FEXCore::X86Tables::TYPE_GROUP_11) {
@@ -686,36 +710,12 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
return NormalOp(LocalInfo, Op);
}
else if (Info->Type == FEXCore::X86Tables::TYPE_GROUP_EVEX) {
uint8_t P1 = ReadByte();
uint8_t P2 = ReadByte();
uint8_t P3 = ReadByte();
/* uint8_t P1 = */ ReadByte();
/* uint8_t P2 = */ ReadByte();
/* uint8_t P3 = */ ReadByte();
uint8_t EVEXOp = ReadByte();
return NormalOp(&EVEXTableOps[EVEXOp], EVEXOp);
}
else if (Info->Type == FEXCore::X86Tables::TYPE_REX_PREFIX) {
LogMan::Throw::A(CTX->Config.Is64BitMode, "Got REX prefix in 32bit mode");
DecodeInst->Flags |= DecodeFlags::FLAG_REX_PREFIX;
// Widening displacement
if (Op & 0b1000) {
DecodeInst->Flags |= DecodeFlags::FLAG_REX_WIDENING;
DecodeFlags::PushOpAddr(&DecodeInst->Flags, DecodeFlags::FLAG_WIDENING_SIZE_LAST);
}
// XGPR_B bit set
if (Op & 0b0001)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_B;
// XGPR_X bit set
if (Op & 0b0010)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_X;
// XGPR_R bit set
if (Op & 0b0100)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_R;
return false;
}
return NormalOp(Info, Op);
}
@@ -723,13 +723,12 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
bool Decoder::DecodeInstruction(uint64_t PC) {
InstructionSize = 0;
Instruction.fill(0);
bool InstructionDecoded = false;
DecodeInst = &DecodedBuffer[DecodedSize];
memset(DecodeInst, 0, sizeof(DecodedInst));
DecodeInst->PC = PC;
while (!InstructionDecoded) {
for(;;) {
uint8_t Op = ReadByte();
switch (Op) {
case 0x0F: {// Escape Op
@@ -753,9 +752,7 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
// Take a peek at the op just past the displacement
uint8_t LocalOp = ReadByte();
if (NormalOpHeader(&FEXCore::X86Tables::DDDNowOps[LocalOp], LocalOp)) {
InstructionDecoded = true;
}
return NormalOpHeader(&FEXCore::X86Tables::DDDNowOps[LocalOp], LocalOp);
break;
}
case 0x38: { // F38 Table!
@@ -770,9 +767,7 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
Prefix = PF_38_66;
uint16_t LocalOp = (Prefix << 8) | ReadByte();
if (NormalOpHeader(&FEXCore::X86Tables::H0F38TableOps[LocalOp], LocalOp)) {
InstructionDecoded = true;
}
return NormalOpHeader(&FEXCore::X86Tables::H0F38TableOps[LocalOp], LocalOp);
break;
}
case 0x3A: { // F3A Table!
@@ -788,9 +783,7 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
Prefix |= PF_3A_REX;
uint16_t LocalOp = (Prefix << 8) | ReadByte();
if (NormalOpHeader(&FEXCore::X86Tables::H0F3ATableOps[LocalOp], LocalOp)) {
InstructionDecoded = true;
}
return NormalOpHeader(&FEXCore::X86Tables::H0F3ATableOps[LocalOp], LocalOp);
break;
}
default: // Two byte table!
@@ -805,37 +798,29 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
bool NoOverlay66 = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY66) != 0;
if (NoOverlay) { // This section of the table ignores prefix extention
if (NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp)) {
InstructionDecoded = true;
}
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0xF3) { // REP
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_REP_PREFIX;
if (NormalOpHeader(&FEXCore::X86Tables::RepModOps[EscapeOp], EscapeOp)) {
InstructionDecoded = true;
}
return NormalOpHeader(&FEXCore::X86Tables::RepModOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0xF2) { // REPNE
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_REPNE_PREFIX;
if (NormalOpHeader(&FEXCore::X86Tables::RepNEModOps[EscapeOp], EscapeOp)) {
InstructionDecoded = true;
}
return NormalOpHeader(&FEXCore::X86Tables::RepNEModOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0x66 && !NoOverlay66) { // Operand Size
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_OPERAND_SIZE;
DecodeFlags::PopOpAddrIf(&DecodeInst->Flags, DecodeFlags::FLAG_OPERAND_SIZE_LAST);
if (NormalOpHeader(&FEXCore::X86Tables::OpSizeModOps[EscapeOp], EscapeOp)) {
InstructionDecoded = true;
}
return NormalOpHeader(&FEXCore::X86Tables::OpSizeModOps[EscapeOp], EscapeOp);
}
else if (NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp)) {
InstructionDecoded = true;
else {
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
}
break;
}
@@ -891,9 +876,33 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
DecodeInst->Flags |= DecodeFlags::FLAG_GS_PREFIX;
break;
default: { // Default base table
if (NormalOpHeader(&FEXCore::X86Tables::BaseOps[Op], Op)) {
InstructionDecoded = true;
auto Info = &FEXCore::X86Tables::BaseOps[Op];
if (Info->Type == FEXCore::X86Tables::TYPE_REX_PREFIX) {
LogMan::Throw::A(CTX->Config.Is64BitMode, "Got REX prefix in 32bit mode");
DecodeInst->Flags |= DecodeFlags::FLAG_REX_PREFIX;
// Widening displacement
if (Op & 0b1000) {
DecodeInst->Flags |= DecodeFlags::FLAG_REX_WIDENING;
DecodeFlags::PushOpAddr(&DecodeInst->Flags, DecodeFlags::FLAG_WIDENING_SIZE_LAST);
}
// XGPR_B bit set
if (Op & 0b0001)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_B;
// XGPR_X bit set
if (Op & 0b0010)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_X;
// XGPR_R bit set
if (Op & 0b0100)
DecodeInst->Flags |= DecodeFlags::FLAG_REX_XGPR_R;
} else {
return NormalOpHeader(Info, Op);
}
break;
}
}
@@ -909,6 +918,7 @@ void Decoder::BranchTargetInMultiblockRange() {
// If the RIP setting is conditional AND within our symbol range then it can be considered for multiblock
uint64_t TargetRIP = 0;
uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4;
bool Conditional = true;
switch (DecodeInst->OP) {
@@ -936,6 +946,11 @@ void Decoder::BranchTargetInMultiblockRange() {
break;
}
if (GPRSize == 4) {
// If we are running a 32bit guest then wrap around addresses that go above 32bit
TargetRIP &= 0xFFFFFFFFU;
}
// If the target RIP is within the symbol ranges then we are golden
if (TargetRIP >= SymbolMinAddress && TargetRIP < SymbolMaxAddress) {
// Update our conditional branch ranges before we return
@@ -982,6 +997,9 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
SymbolMinAddress = EntryPoint;
}
DecodedMinAddress = EntryPoint;
DecodedMaxAddress = EntryPoint;
// Entry is a jump target
BlocksToDecode.emplace(PC);
@@ -1002,11 +1020,20 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
while (1) {
ErrorDuringDecoding = !DecodeInstruction(RIPToDecode + PCOffset);
if (ErrorDuringDecoding) {
LogMan::Msg::D("Couldn't Decode something at 0x%lx, Started at 0x%lx", PC + PCOffset, PC);
LogMan::Throw::A(Blocks.size() != 1, "Decode Error in entry block");
CurrentBlockDecoding.HasInvalidInstruction = true;
if (ErrorDuringDecoding && Blocks.size() != 1) {
ErrorDuringDecoding = false;
}
break;
}
DecodedMinAddress = std::min(DecodedMinAddress, RIPToDecode + PCOffset);
DecodedMaxAddress = std::max(DecodedMaxAddress, RIPToDecode + PCOffset + DecodeInst->InstSize);
++TotalInstructions;
++BlockNumberOfInstructions;
++DecodedSize;
+4
View File
@@ -20,6 +20,7 @@ public:
uint64_t Entry{};
uint64_t NumInstructions{};
FEXCore::X86Tables::DecodedInst *DecodedInstructions;
bool HasInvalidInstruction{};
};
Decoder(FEXCore::Context::Context *ctx);
@@ -29,6 +30,9 @@ public:
return &Blocks;
}
uint64_t DecodedMinAddress {};
uint64_t DecodedMaxAddress {~0ULL};
private:
FEXCore::Context::Context *CTX;
+22 -10
View File
@@ -1,3 +1,10 @@
/*
$info$
tags: glue|gdbserver
desc: Provides a gdb interface to the guest state
$end_info$
*/
#include <cstdlib>
#include <cstdio>
#include <iomanip>
@@ -232,17 +239,17 @@ std::string GdbServer::readRegs() {
bool Found = false;
for (auto &Thread : *Threads) {
if (Thread->State.ThreadManager.GetTID() != CurrentDebuggingThread) {
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
continue;
}
state = Thread->State.State;
memcpy(&state, Thread->CurrentFrame, sizeof(state));
Found = true;
break;
}
if (!Found) {
// If set to an invalid thread then just get the parent thread ID
state = CTX->GetCPUState();
memcpy(&state, CTX->ParentThread->CurrentFrame, sizeof(state));
}
// Encode the GDB context definition
@@ -284,17 +291,17 @@ GdbServer::HandledPacketType GdbServer::readReg(std::string& packet) {
bool Found = false;
for (auto &Thread : *Threads) {
if (Thread->State.ThreadManager.GetTID() != CurrentDebuggingThread) {
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
continue;
}
state = Thread->State.State;
memcpy(&state, Thread->CurrentFrame, sizeof(state));
Found = true;
break;
}
if (!Found) {
// If set to an invalid thread then just get the parent thread ID
state = CTX->GetCPUState();
memcpy(&state, CTX->ParentThread->CurrentFrame, sizeof(state));
}
@@ -525,7 +532,7 @@ GdbServer::HandledPacketType GdbServer::handleXfer(std::string &packet) {
ss << "<threads>\n";
for (size_t i = 0; i < Threads->size(); ++i) {
auto Thread = Threads->at(i);
ss << "\t<thread id=\"" << std::hex << Thread->State.ThreadManager.GetTID() << "\" core=\"" << i << "\" name=\"" << getThreadName(Thread->State.ThreadManager.GetTID()) << "\">\n";
ss << "\t<thread id=\"" << std::hex << Thread->ThreadManager.GetTID() << "\" core=\"" << i << "\" name=\"" << getThreadName(Thread->ThreadManager.GetTID()) << "\">\n";
ss << "\t</thread>\n";
}
@@ -653,7 +660,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
ss << "m";
for (size_t i = 0; i < Threads->size(); ++i) {
auto Thread = Threads->at(i);
ss << std::hex << Thread->State.ThreadManager.TID << ",";
ss << std::hex << Thread->ThreadManager.TID << ",";
}
return {ss.str(), HandledPacketType::TYPE_ACK};
}
@@ -672,7 +679,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
if (match("qC")) {
// Returns the current Thread ID
std::ostringstream ss;
ss << "m" << std::hex << CTX->ParentThread->State.ThreadManager.TID;
ss << "m" << std::hex << CTX->ParentThread->ThreadManager.TID;
return {ss.str(), HandledPacketType::TYPE_ACK};
}
if (match("QStartNoAckMode")) {
@@ -952,9 +959,14 @@ void GdbServer::GdbServerLoop() {
}
}
}
static void* ThreadHandler(void *Arg) {
FEXCore::GdbServer *This = reinterpret_cast<FEXCore::GdbServer*>(Arg);
This->GdbServerLoop();
return nullptr;
}
void GdbServer::StartThread() {
gdbServerThread = std::thread(&GdbServer::GdbServerLoop, this);
gdbServerThread = FEXCore::Threads::Thread::Create(ThreadHandler, this);
}
std::unique_ptr<std::iostream> GdbServer::OpenSocket() {
+15 -4
View File
@@ -1,23 +1,34 @@
/*
$info$
tags: glue|gdbserver
$end_info$
*/
#pragma once
#include <mutex>
#include <thread>
#include "Interface/Context/Context.h"
#include "Common/NetStream.h"
#include <FEXCore/Utils/Threads.h>
#include <mutex>
namespace FEXCore {
class GdbServer {
public:
GdbServer(FEXCore::Context::Context *ctx);
// Public for threading
void GdbServerLoop();
private:
void Break(int signal);
std::unique_ptr<std::iostream> OpenSocket();
void StartThread();
void GdbServerLoop();
std::string ReadPacket(std::iostream &stream);
void SendPacket(std::ostream &stream, std::string packet);
@@ -50,14 +61,14 @@ private:
HandledPacketType readReg(std::string& packet);
FEXCore::Context::Context *CTX;
std::thread gdbServerThread;
std::unique_ptr<FEXCore::Threads::Thread> gdbServerThread;
std::unique_ptr<std::iostream> CommsStream;
std::mutex sendMutex;
bool SettingNoAckMode{false};
bool NoAckMode{false};
std::string ThreadString{};
uint32_t CurrentDebuggingThread{};
FEXCore::Config::Value<std::string> Filename{FEXCore::Config::CONFIG_APP_FILENAME, ""};
FEX_CONFIG_OPT(Filename, APP_FILENAME);
};
}
@@ -1,559 +0,0 @@
#include "Common/MathUtils.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
#include "aarch64/assembler-aarch64.h"
namespace FEXCore::CPU {
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
// Go to sleep
Thread->StartRunning.Wait();
Thread->State.RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
}
using namespace vixl;
using namespace vixl::aarch64;
#define STATE x28
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
class DispatchGenerator : public vixl::aarch64::Assembler {
public:
DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
CPUBackend::AsmDispatch DispatchPtr;
CPUBackend::JITCallback CallbackPtr;
uint64_t ThreadStopHandlerAddress;
uint64_t AbsoluteLoopTopAddress;
uint64_t ThreadPauseHandlerAddress;
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *State;
private:
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
void PushCalleeSavedRegisters();
void PopCalleeSavedRegisters();
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
std::stack<uint64_t> SignalFrames;
};
void DispatchGenerator::PushCalleeSavedRegisters() {
// We need to save pairs of registers
// We save r19-r30
MemOperand PairOffset(sp, -16, PreIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x19, x20},
{x21, x22},
{x23, x24},
{x25, x26},
{x27, x28},
{x29, x30},
}};
for (auto &RegPair : CalleeSaved) {
stp(RegPair.first, RegPair.second, PairOffset);
}
// Additionally we need to store the lower 64bits of v8-v15
// Here's a fun thing, we can use two ST4 instructions to store everything
// We just need a single sub to sp before that
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v8, v9, v10, v11},
{v12, v13, v14, v15},
}};
uint32_t VectorSaveSize = sizeof(uint64_t) * 8;
sub(sp, sp, VectorSaveSize);
// SP supporting move
// We just saved x19 so it is safe
add(x19, sp, 0);
MemOperand QuadOffset(x19, 32, PostIndex);
for (auto &RegQuad : FPRs) {
st4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
}
void DispatchGenerator::PopCalleeSavedRegisters() {
const std::array<
std::tuple<vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister,
vixl::aarch64::VRegister>, 2> FPRs = {{
{v12, v13, v14, v15},
{v8, v9, v10, v11},
}};
MemOperand QuadOffset(sp, 32, PostIndex);
for (auto &RegQuad : FPRs) {
ld4(std::get<0>(RegQuad).D(),
std::get<1>(RegQuad).D(),
std::get<2>(RegQuad).D(),
std::get<3>(RegQuad).D(),
0,
QuadOffset);
}
MemOperand PairOffset(sp, 16, PostIndex);
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
{x29, x30},
{x27, x28},
{x25, x26},
{x23, x24},
{x21, x22},
{x19, x20},
}};
for (auto &RegPair : CalleeSaved) {
ldp(RegPair.first, RegPair.second, PairOffset);
}
}
void DispatchGenerator::LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant) {
bool Is64Bit = Reg.IsX();
int Segments = Is64Bit ? 4 : 2;
movz(Reg, (Constant) & 0xFFFF, 0);
for (int i = 1; i < Segments; ++i) {
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
if (Part) {
movk(Reg, Part, i * 16);
}
}
}
DispatchGenerator::DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: vixl::aarch64::Assembler(MAX_DISPATCHER_CODE_SIZE, vixl::aarch64::PositionDependentCode)
, CTX {ctx}
, State {Thread} {
SetAllowAssembler(true);
auto Buffer = GetBuffer();
DispatchPtr = Buffer->GetOffsetAddress<CPUBackend::AsmDispatch>(GetCursorOffset());
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Push all the register we need to save
PushCalleeSavedRegisters();
// Push our memory base to the correct register
// Move our thread pointer to the correct register
// This is passed in to parameter 0 (x0)
mov(STATE, x0);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
add(x0, sp, 0);
str(x0, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)));
Label Exit;
Label LoopTop;
Label NoBlock;
Label ThreadPauseHandler;
bind(&LoopTop);
AbsoluteLoopTopAddress = GetLabelAddress<uint64_t>(&LoopTop);
// Load in our RIP
// Don't modify x2 since it contains our RIP once the block doesn't exist
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, State.rip)));
auto RipReg = x2;
// Mask the address by the virtual address size so we can check for aliases
LoadConstant(x3, Thread->BlockCache->GetVirtualMemorySize() - 1);
and_(x3, RipReg, x3);
{
// This is the block cache lookup routine
// It matches what is going on it BlockCache.h::FindBlock
LoadConstant(x0, Thread->BlockCache->GetPagePointer());
// Offset the address and add to our page pointer
lsr(x1, x3, 12);
// Load the pointer from the offset
ldr(x0, MemOperand(x0, x1, Shift::LSL, 3));
// If page pointer is zero then we have no block
cbz(x0, &NoBlock);
// Steal the page offset
and_(x1, x3, 0x0FFF);
// Shift the offset by the size of the block cache entry
add(x0, x0, Operand(x1, Shift::LSL, (int)log2(sizeof(FEXCore::BlockCache::BlockCacheEntry))));
// Load the guest address first to ensure it maps to the address we are currently at
// This fixes aliasing problems
ldr(x1, MemOperand(x0, offsetof(FEXCore::BlockCache::BlockCacheEntry, GuestCode)));
cmp(x1, RipReg);
b(&NoBlock, Condition::ne);
// Now load the actual host block to execute if we can
ldr(x1, MemOperand(x0, offsetof(FEXCore::BlockCache::BlockCacheEntry, HostCode)));
cbz(x1, &NoBlock);
// If we've made it here then we have a real compiled block
{
mov(x0, STATE);
blr(x1);
}
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, CTX)));
ldr(w0, MemOperand(x0, offsetof(FEXCore::Context::Context, Config.RunningMode)));
// If the value == 0 then branch to the top
cbz(x0, &LoopTop);
// Else we need to pause now
b(&ThreadPauseHandler);
}
else {
// Unconditionally loop to the top
// We will only stop on error when compiling a block or signal
b(&LoopTop);
}
}
{
bind(&Exit);
ThreadStopHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
PopCalleeSavedRegisters();
// Return from the function
// LR is set to the correct return location now
ret();
}
// Need to create the block
{
bind(&NoBlock);
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, CTX)));
mov(x1, STATE);
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
LoadConstant(x3, Ptr.Data);
// X2 contains our guest RIP
blr(x3); // { CTX, ThreadState, RIP}
b(&LoopTop);
}
{
bind(&ThreadPauseHandler);
ThreadPauseHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
// We are pausing, this means the frontend should be waiting for this thread to idle
// We will have faulted and jumped to this location at this point
// Call our sleep handler
LoadConstant(x0, reinterpret_cast<uintptr_t>(CTX));
mov(x1, STATE);
LoadConstant(x2, reinterpret_cast<uint64_t>(SleepThread));
blr(x2);
// XXX: Unsupported atm
//PauseReturnInstruction = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
//// Fault to start running again
//hlt(0);
}
{
CallbackPtr = Buffer->GetOffsetAddress<CPUBackend::JITCallback>(GetCursorOffset());
// We expect the thunk to have previously pushed the registers it was using
PushCalleeSavedRegisters();
// First thing we need to move the thread state pointer back in to our register
mov(STATE, x0);
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
LoadConstant(x0, CTX->X86CodeGen.CallbackReturn);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
sub(x2, x2, 16);
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
str(x0, MemOperand(x2));
// Store RIP to the context state
str(x1, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.rip)));
// Now go back to the regular dispatcher loop
b(&LoopTop);
}
FinalizeCode();
uint64_t CodeEnd = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
vixl::aarch64::CPU::EnsureIAndDCacheCoherency(reinterpret_cast<void*>(DispatchPtr), CodeEnd - reinterpret_cast<uint64_t>(DispatchPtr));
GetBuffer()->SetExecutable();
}
struct HostCTXHeader {
uint32_t Magic;
uint32_t Size;
};
constexpr uint32_t FPR_MAGIC = 0x46508001U;
struct HostFPRState {
HostCTXHeader Head;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
};
struct ContextBackup {
// Host State
uint64_t GPRs[31];
uint64_t PrevSP;
uint64_t PrevPC;
uint64_t PState;
uint32_t FPSR;
uint32_t FPCR;
__uint128_t FPRs[32];
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
};
void DispatchGenerator::StoreThreadState(int Signal, void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
uint64_t OldSP = _mcontext->sp;
uintptr_t NewSP = OldSP;
size_t StackOffset = sizeof(ContextBackup);
NewSP -= StackOffset;
NewSP = AlignDown(NewSP, 16);
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
memcpy(&Context->GPRs[0], &_mcontext->regs[0], 31 * sizeof(uint64_t));
Context->PrevSP = _mcontext->sp;
Context->PrevPC = _mcontext->pc;
Context->PState = _mcontext->pstate;
// Host FPR state starts at _mcontext->reserved[0];
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
Context->FPSR = HostState->FPSR;
Context->FPCR = HostState->FPCR;
memcpy(&Context->FPRs[0], &HostState->FPRs[0], 32 * sizeof(__uint128_t));
// Retain the action pointer so we can see it when we return
Context->Signal = Signal;
// Save guest state
// We can't guarantee if registers are in context or host GPRs
// So we need to save everything
memcpy(&Context->GuestState, &State->State, sizeof(FEXCore::Core::CPUState));
// Set the new SP
_mcontext->sp = NewSP;
}
void DispatchGenerator::RestoreThreadState(void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
uint64_t OldSP = _mcontext->sp;
uintptr_t NewSP = OldSP;
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
// First thing, reset the guest state
memcpy(&State->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
memcpy(&HostState->FPRs[0], &Context->FPRs[0], 32 * sizeof(__uint128_t));
Context->FPCR = HostState->FPCR;
Context->FPSR = HostState->FPSR;
// Restore GPRs and other state
_mcontext->pstate = Context->PState;
_mcontext->pc = Context->PrevPC;
_mcontext->sp = Context->PrevSP;
memcpy(&_mcontext->regs[0], &Context->GPRs[0], 31 * sizeof(uint64_t));
// Restore the previous signal state
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
}
bool DispatchGenerator::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->pc = AbsoluteLoopTopAddress;
// Set x28 (which is our state register) to point to our guest thread data
_mcontext->regs[28 /* STATE */] = reinterpret_cast<uint64_t>(State);
State->State.State.gregs[X86State::REG_RDI] = Signal;
uint64_t OldGuestSP = State->State.State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
if (!(GuestStack->ss_flags & SS_DISABLE)) {
// If our guest is already inside of the alternative stack
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
if (OldGuestSP >= AltStackBase &&
OldGuestSP <= AltStackEnd) {
// We are already in the alt stack, the rest of the code will handle adjusting this
}
else {
NewGuestSP = AltStackEnd;
}
}
// Back up past the redzone, which is 128bytes
// Don't need this offset if we aren't going to be putting siginfo in to it
NewGuestSP -= 128;
if (GuestAction->sa_flags & SA_SIGINFO) {
// XXX: siginfo_t(RSI), ucontext (RDX)
State->State.State.gregs[X86State::REG_RSI] = 0;
State->State.State.gregs[X86State::REG_RDX] = 0;
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
}
else {
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
}
// Set up the new SP for stack handling
NewGuestSP -= 8;
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
State->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
return true;
}
bool DispatchGenerator::HandleSignalPause(int Signal, void *info, void *ucontext) {
FEXCore::Core::SignalEvent SignalReason = State->SignalReason.load();
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->pc = ThreadPauseHandlerAddress;
// Set our state register to point to our guest thread data
_mcontext->regs[28 /* STATE */] = reinterpret_cast<uint64_t>(State);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
RestoreThreadState(ucontext);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Our thread is stopping
// We don't care about anything at this point
// Set the stack to our starting location when we entered the JIT and get out safely
_mcontext->sp = State->State.ReturningStackLocation;
// Set the new PC
_mcontext->pc = ThreadStopHandlerAddress;
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
return false;
}
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
Generator = new DispatchGenerator(ctx, Thread);
DispatchPtr = Generator->DispatchPtr;
CallbackPtr = Generator->CallbackPtr;
}
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
DispatchGenerator *Gen = Generator;
return Gen->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
}
bool InterpreterCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
DispatchGenerator *Gen = Generator;
return Gen->HandleSignalPause(Signal, info, ucontext);
}
void InterpreterCore::DeleteAsmDispatch() {
delete Generator;
}
}
@@ -1,14 +1,16 @@
#pragma once
#include "Interface/Core/BlockCache.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/InternalThreadState.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
namespace FEXCore::CPU {
class DispatchGenerator;
class X86DispatchGenerator;
class Arm64DispatchGenerator;
#define DESTMAP_AS_MAP 0
#if DESTMAP_AS_MAP
@@ -22,27 +24,21 @@ public:
explicit InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
~InterpreterCore() override;
std::string GetName() override { return "Interpreter"; }
void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData) override;
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
bool NeedsOpDispatch() override { return true; }
void ExecuteCode(FEXCore::Core::InternalThreadState *Thread);
void CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
void DeleteAsmDispatch();
using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
CallbackReturn ReturnPtr;
bool HandleSIGBUS(int Signal, void *info, void *ucontext);
private:
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *State;
uint32_t AllocateTmpSpace(size_t Size);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
template<typename Res>
Res GetDest(void* SSAData, IR::OrderedNodeWrapper Op);
@@ -50,7 +46,7 @@ private:
template<typename Res>
Res GetSrc(void* SSAData, IR::OrderedNodeWrapper Src);
DispatchGenerator *Generator{};
Dispatcher *Dispatcher{};
};
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,42 @@
namespace FEXCore::Core {
struct InternalThreadState;
}
namespace FEXCore::IR {
class IRListView;
}
namespace FEXCore::Core{
struct DebugData;
}
namespace FEXCore::CPU {
enum FallbackABI {
FABI_UNKNOWN,
FABI_VOID_U16,
FABI_F80_F32,
FABI_F80_F64,
FABI_F80_I16,
FABI_F80_I32,
FABI_F32_F80,
FABI_F64_F80,
FABI_I16_F80,
FABI_I32_F80,
FABI_I64_F80,
FABI_I64_F80_F80,
FABI_F80_F80,
FABI_F80_F80_F80,
};
struct FallbackInfo {
FallbackABI ABI;
void *fn;
};
class InterpreterOps {
public:
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData);
static bool GetFallbackHandler(IR::IROp_Header *IROp, FallbackInfo *Info);
};
};
@@ -1,467 +0,0 @@
#include "Common/MathUtils.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
#include <xbyak/xbyak.h>
namespace FEXCore::CPU {
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE r14
class DispatchGenerator : public Xbyak::CodeGenerator {
public:
DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
CPUBackend::AsmDispatch DispatchPtr;
CPUBackend::JITCallback CallbackPtr;
InterpreterCore::CallbackReturn ReturnPtr;
uint64_t ThreadStopHandlerAddress;
uint64_t AbsoluteLoopTopAddress;
uint64_t ThreadPauseHandlerAddress;
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *State;
private:
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
std::stack<uint64_t> SignalFrames;
};
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
// Go to sleep
Thread->StartRunning.Wait();
Thread->State.RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
}
DispatchGenerator::DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE)
, CTX {ctx}
, State {Thread} {
using namespace Xbyak;
using namespace Xbyak::util;
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Bunch of exit state stuff
// x86-64 ABI has the stack aligned when /call/ happens
// Which means the destination has a misaligned stack at that point
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
mov(STATE, rdi);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
mov(qword [rdi + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)], rsp);
Label LoopTop;
Label NoBlock;
Label ExitBlock;
Label ThreadPauseHandler;
L(LoopTop);
AbsoluteLoopTopAddress = getCurr<uint64_t>();
{
mov(r13, Thread->BlockCache->GetPagePointer());
// Load our RIP
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
mov(rax, rdx);
mov(rbx, Thread->BlockCache->GetVirtualMemorySize() - 1);
and_(rax, rbx);
shr(rax, 12);
// Load page pointer
mov(rdi, qword [r13 + rax * 8]);
cmp(rdi, 0);
je(NoBlock);
mov (rax, rdx);
and_(rax, 0x0FFF);
shl(rax, (int)log2(sizeof(FEXCore::BlockCache::BlockCacheEntry)));
// check for aliasing
mov(rcx, qword [rdi + rax + 8]);
cmp(rcx, rdx);
jne(NoBlock);
// Load the block pointer
mov(rax, qword [rdi + rax]);
cmp(rax, 0);
je(NoBlock);
// Real block if we made it here
mov(rdi, STATE);
call(rax);
if (CTX->GetGdbServerStatus()) {
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
// If the value == 0 then branch to the top
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
je(LoopTop);
// Else we need to pause now
jmp(ThreadPauseHandler);
ud2();
}
else {
jmp(LoopTop);
}
}
{
L(ExitBlock);
ThreadStopHandlerAddress = getCurr<uint64_t>();
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
// Block creation
{
L(NoBlock);
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
// {rdi, rsi, rdx}
mov(rdi, reinterpret_cast<uint64_t>(CTX));
mov(rsi, STATE);
mov(rax, Ptr.Data);
call(rax);
// rdx already contains RIP here
jmp(LoopTop);
}
{
// Pause handler
ThreadPauseHandlerAddress = getCurr<uint64_t>();
L(ThreadPauseHandler);
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
mov(rsi, STATE);
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
call(rax);
// XXX: Unsupported atm
// uint64_t PauseReturnInstruction = getCurr<uint64_t>();
// ud2();
}
{
CallbackPtr = getCurr<CPUBackend::JITCallback>();
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
// First thing we need to move the thread state pointer back in to our register
mov(STATE, rdi);
// XXX: XMM?
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
mov(rax, CTX->X86CodeGen.CallbackReturn);
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
sub(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 16);
mov(rbx, qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])]);
mov(qword [rbx], rax);
// Store RIP to the context state
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], rsi);
// Back to the loop top now
jmp(LoopTop);
}
{
ReturnPtr = getCurr<InterpreterCore::CallbackReturn>();
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// rdi = thread
// rsi = rsp
mov(rsp, rsi);
// Now jump back to the thunk
// XXX: XMM?
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
ready();
}
struct ContextBackup {
uint64_t StoredCookie;
// Host State
// RIP and RSP is stored in GPRs here
uint64_t GPRs[NGREG];
_libc_fpstate FPRState;
// Guest state
int Signal;
FEXCore::Core::CPUState GuestState;
};
void DispatchGenerator::StoreThreadState(int Signal, void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
uint64_t OldSP = _mcontext->gregs[REG_RSP];
uintptr_t NewSP = OldSP;
size_t StackOffset = sizeof(ContextBackup);
// We need to back up behind the host's red zone
// We do this on the guest side as well
NewSP -= 128;
NewSP -= StackOffset;
NewSP = AlignDown(NewSP, 16);
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
Context->StoredCookie = 0x4142434445464748ULL;
// Copy the GPRs
memcpy(&Context->GPRs[0], &_mcontext->gregs[0], NGREG * sizeof(_mcontext->gregs[0]));
// Copy the FPRState
memcpy(&Context->FPRState, _mcontext->fpregs, sizeof(_libc_fpstate));
// XXX: Save 256bit and 512bit AVX register state
// Retain the action pointer so we can see it when we return
Context->Signal = Signal;
// Save guest state
// We can't guarantee if registers are in context or host GPRs
// So we need to save everything
memcpy(&Context->GuestState, &State->State, sizeof(FEXCore::Core::CPUState));
// Set the new SP
_mcontext->gregs[REG_RSP] = NewSP;
SignalFrames.push(NewSP);
}
void DispatchGenerator::RestoreThreadState(void *ucontext) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
uint64_t OldSP = SignalFrames.top();
SignalFrames.pop();
uintptr_t NewSP = OldSP;
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
if (Context->StoredCookie != 0x4142434445464748ULL) {
LogMan::Msg::D("COOKIE WAS NOT CORRECT!\n");
exit(-1);
}
// First thing, reset the guest state
memcpy(&State->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
// Copy the GPRs
memcpy(&_mcontext->gregs[0], &Context->GPRs[0], NGREG * sizeof(_mcontext->gregs[0]));
// Copy the FPRState
memcpy(_mcontext->fpregs, &Context->FPRState, sizeof(_libc_fpstate));
// Restore the previous signal state
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
}
bool DispatchGenerator::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->gregs[REG_RIP] = AbsoluteLoopTopAddress;
// Set our state register to point to our guest thread data
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(State);
uint64_t OldGuestSP = State->State.State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
if (!(GuestStack->ss_flags & SS_DISABLE)) {
// If our guest is already inside of the alternative stack
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
if (OldGuestSP >= AltStackBase &&
OldGuestSP <= AltStackEnd) {
// We are already in the alt stack, the rest of the code will handle adjusting this
}
else {
NewGuestSP = AltStackEnd;
}
}
// Back up past the redzone, which is 128bytes
// Don't need this offset if we aren't going to be putting siginfo in to it
NewGuestSP -= 128;
State->State.State.gregs[X86State::REG_RDI] = Signal;
if (GuestAction->sa_flags & SA_SIGINFO) {
// XXX: siginfo_t(RSI), ucontext (RDX)
State->State.State.gregs[X86State::REG_RSI] = 0;
State->State.State.gregs[X86State::REG_RDX] = 0;
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
}
else {
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
}
// Set up the new SP for stack handling
NewGuestSP -= 8;
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
State->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
return true;
}
bool DispatchGenerator::HandleSignalPause(int Signal, void *info, void *ucontext) {
FEXCore::Core::SignalEvent SignalReason = State->SignalReason.load();
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Store our thread state so we can come back to this
StoreThreadState(Signal, ucontext);
// Set the new PC
_mcontext->gregs[REG_RIP] = ThreadPauseHandlerAddress;
// Set our state register to point to our guest thread data
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(State);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
ucontext_t* _context = (ucontext_t*)ucontext;
mcontext_t* _mcontext = &_context->uc_mcontext;
// Our thread is stopping
// We don't care about anything at this point
// Set the stack to our starting location when we entered the core and get out safely
_mcontext->gregs[REG_RSP] = State->State.ReturningStackLocation;
// Set the new PC
_mcontext->gregs[REG_RIP] = ThreadStopHandlerAddress;
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
RestoreThreadState(ucontext);
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
return true;
}
return false;
}
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
Generator = new DispatchGenerator(ctx, Thread);
DispatchPtr = Generator->DispatchPtr;
CallbackPtr = Generator->CallbackPtr;
ReturnPtr = Generator->ReturnPtr;
}
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
DispatchGenerator *Gen = Generator;
return Gen->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
}
bool InterpreterCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
DispatchGenerator *Gen = Generator;
return Gen->HandleSignalPause(Signal, info, ucontext);
}
void InterpreterCore::DeleteAsmDispatch() {
delete Generator;
}
}
+27 -30
View File
@@ -1,3 +1,8 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -29,7 +34,7 @@ static int64_t LREM(int64_t SrcHigh, int64_t SrcLow, int64_t Divisor) {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(TruncElementPair) {
auto Op = IROp->C<IR::IROp_TruncElementPair>();
@@ -51,10 +56,22 @@ DEF_OP(Constant) {
LoadConstant(Dst, Op->Constant);
}
DEF_OP(EntrypointOffset) {
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
auto Constant = IR->GetHeader()->Entry + Op->Offset;
auto Dst = GetReg<RA_64>(Node);
LoadConstant(Dst, Constant);
}
DEF_OP(InlineConstant) {
//nop
}
DEF_OP(InlineEntrypointOffset) {
//nop
}
DEF_OP(CycleCounter) {
#ifdef DEBUG_CYCLES
movz(GetReg<RA_64>(Node), 0);
@@ -386,7 +403,6 @@ DEF_OP(Xor) {
DEF_OP(Lshl) {
auto Op = IROp->C<IR::IROp_Lshl>();
uint8_t OpSize = IROp->Size;
uint64_t Const;
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
lsl(GRS(Node), GRS(Op->Header.Args[0].ID()), (unsigned int)Const);
@@ -397,7 +413,6 @@ DEF_OP(Lshl) {
DEF_OP(Lshr) {
auto Op = IROp->C<IR::IROp_Lshr>();
uint8_t OpSize = IROp->Size;
uint64_t Const;
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
lsr(GRS(Node), GRS(Op->Header.Args[0].ID()), (unsigned int)Const);
@@ -512,14 +527,10 @@ DEF_OP(LDiv) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LDIV);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LDIV));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Result is now in x0
// Fix the stack and any values that were stepped on
@@ -559,14 +570,10 @@ DEF_OP(LUDiv) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LUDIV);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LUDIV));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Result is now in x0
// Fix the stack and any values that were stepped on
@@ -616,14 +623,10 @@ DEF_OP(LRem) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LREM);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LREM));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Result is now in x0
// Fix the stack and any values that were stepped on
@@ -670,14 +673,11 @@ DEF_OP(LURem) {
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
#if _M_X86_64
CallRuntime(LUREM);
#else
LoadConstant(x3, reinterpret_cast<uint64_t>(LUREM));
SpillStaticRegs();
blr(x3);
FillStaticRegs();
#endif
// Fix the stack and any values that were stepped on
PopDynamicRegsAndLR();
@@ -908,8 +908,8 @@ Condition MapSelectCC(IR::CondClassType Cond) {
case FEXCore::IR::COND_FGT: return Condition::hi;
case FEXCore::IR::COND_FU: return Condition::vs;
case FEXCore::IR::COND_FNU: return Condition::vc;
case FEXCore::IR::COND_VS:;
case FEXCore::IR::COND_VC:;
case FEXCore::IR::COND_VS:
case FEXCore::IR::COND_VC:
case FEXCore::IR::COND_MI:
case FEXCore::IR::COND_PL:
default:
@@ -933,7 +933,7 @@ DEF_OP(Select) {
} else {
LogMan::Msg::A("Select: Expected GPR or FPR");
}
auto cc = MapSelectCC(Op->Cond);
uint64_t const_true, const_false;
@@ -1010,7 +1010,7 @@ DEF_OP(FCmp) {
fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
}
auto Dst = GetReg<RA_64>(Node);
bool set = false;
if (Op->Flags & (1 << IR::FCMP_FLAG_EQ)) {
@@ -1043,17 +1043,15 @@ DEF_OP(FCmp) {
}
}
DEF_OP(F80Cmp) {
LogMan::Msg::D("Unimplemented");
}
#undef DEF_OP
void JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
REGISTER_OP(CONSTANT, Constant);
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
REGISTER_OP(INLINECONSTANT, InlineConstant);
REGISTER_OP(INLINEENTRYPOINTOFFSET, InlineEntrypointOffset);
REGISTER_OP(CYCLECOUNTER, CycleCounter);
REGISTER_OP(ADD, Add);
REGISTER_OP(SUB, Sub);
@@ -1095,7 +1093,6 @@ void JITCore::RegisterALUHandlers() {
REGISTER_OP(FLOAT_TOGPR_U, Float_ToGPR_U);
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
REGISTER_OP(FCMP, FCmp);
REGISTER_OP(F80CMP, F80Cmp);
#undef REGISTER_OP
}
@@ -1,9 +1,15 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(CASPair) {
auto Op = IROp->C<IR::IROp_CASPair>();
uint8_t OpSize = IROp->Size;
@@ -865,8 +871,8 @@ DEF_OP(AtomicFetchXor) {
}
#undef DEF_OP
void JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
@@ -1,13 +1,20 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include "Interface/Core/InternalThreadState.h"
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <Interface/HLE/Thunks/Thunks.h>
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GuestCallDirect) {
LogMan::Msg::D("Unimplemented");
}
@@ -22,9 +29,7 @@ DEF_OP(GuestReturn) {
DEF_OP(SignalReturn) {
// First we must reset the stack
if (SpillSlots) {
add(sp, sp, SpillSlots * 16);
}
ResetStack();
// Now branch to our signal return helper
// This can't be a direct branch since the code needs to live at a constant location
@@ -36,11 +41,9 @@ DEF_OP(CallbackReturn) {
// spill back to CTX
SpillStaticRegs();
// First we must reset the stack
if (SpillSlots) {
add(sp, sp, SpillSlots * 16);
}
ResetStack();
// We can now lower the ref counter again
LoadConstant(x0, reinterpret_cast<uint64_t>(ThreadSharedData.SignalHandlerRefCounterPtr));
@@ -49,9 +52,9 @@ DEF_OP(CallbackReturn) {
str(w2, MemOperand(x0));
// We need to adjust an additional 8 bytes to get back to the original "misaligned" RSP state
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
add(x2, x2, 8);
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
PopCalleeSavedRegisters();
@@ -64,15 +67,13 @@ DEF_OP(ExitFunction) {
Label FullLookup;
if (SpillSlots) {
add(sp, sp, SpillSlots * 16);
}
ResetStack();
aarch64::Register RipReg;
uint64_t NewRIP;
if (IsInlineConstant(Op->NewRIP, &NewRIP)) {
Literal l_BranchHost{ExitFunctionLinkerAddress};
if (IsInlineConstant(Op->NewRIP, &NewRIP) || IsInlineEntrypointOffset(Op->NewRIP, &NewRIP)) {
Literal l_BranchHost{Dispatcher->ExitFunctionLinkerAddress};
Literal l_BranchGuest{NewRIP};
ldr(x0, &l_BranchHost);
@@ -82,11 +83,11 @@ DEF_OP(ExitFunction) {
place(&l_BranchGuest);
} else {
RipReg = GetReg<RA_64>(Op->Header.Args[0].ID());
// L1 Cache
LoadConstant(x0, State->BlockCache->GetL1Pointer());
and_(x3, RipReg, BlockCache::L1_ENTRIES_MASK);
// L1 Cache
LoadConstant(x0, ThreadState->LookupCache->GetL1Pointer());
and_(x3, RipReg, LookupCache::L1_ENTRIES_MASK);
add(x0, x0, Operand(x3, Shift::LSL, 4));
ldp(x1, x0, MemOperand(x0));
@@ -95,8 +96,8 @@ DEF_OP(ExitFunction) {
br(x1);
bind(&FullLookup);
LoadConstant(TMP1, AbsoluteLoopTopAddress);
str(RipReg, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, State.rip)));
LoadConstant(TMP1, Dispatcher->AbsoluteLoopTopAddress);
str(RipReg, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
br(TMP1);
}
}
@@ -136,8 +137,8 @@ Condition MapBranchCC(IR::CondClassType Cond) {
case FEXCore::IR::COND_FGT: return Condition::hi;
case FEXCore::IR::COND_FU: return Condition::vs;
case FEXCore::IR::COND_FNU: return Condition::vc;
case FEXCore::IR::COND_VS:;
case FEXCore::IR::COND_VC:;
case FEXCore::IR::COND_VS:
case FEXCore::IR::COND_VC:
case FEXCore::IR::COND_MI:
case FEXCore::IR::COND_PL:
default:
@@ -187,7 +188,7 @@ DEF_OP(CondJump) {
b(TrueTargetLabel, MapBranchCC(Op->Cond));
}
if (FalseIter == JumpTargets.end()) {
FalseTargetLabel = &JumpTargets.try_emplace(Op->FalseBlock.ID()).first->second;
}
@@ -222,7 +223,7 @@ DEF_OP(Syscall) {
blr(x3);
add(sp, sp, SPOffset);
// Result is now in x0
// Fix the stack and any values that were stepped on
FillStaticRegs();
@@ -244,30 +245,35 @@ DEF_OP(Thunk) {
mov(x0, GetReg<RA_64>(Op->Header.Args[0].ID()));
#if _M_X86_64
ERROR_AND_DIE("JIT: OP_THUNK not supported with arm simulator")
#else
LoadConstant(x2, Op->ThunkFnPtr);
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
LoadConstant(x2, (uintptr_t)thunkFn);
blr(x2);
#endif
PopDynamicRegsAndLR();
FillStaticRegs(); // load from ctx after ra64 refill
}
DEF_OP(ValidateCode) {
auto Op = IROp->C<IR::IROp_ValidateCode>();
uint8_t *NewCode = (uint8_t *)Op->CodePtr;
uint8_t *OldCode = (uint8_t *)&Op->CodeOriginal;
uint8_t *OldCode = (uint8_t *)&Op->CodeOriginalLow;
int len = Op->CodeLength;
int idx = 0;
LoadConstant(GetReg<RA_64>(Node), 0);
LoadConstant(x0, Op->CodePtr);
LoadConstant(x0, IR->GetHeader()->Entry + Op->Offset);
LoadConstant(x1, 1);
while (len >= 8)
{
ldr(x2, MemOperand(x0, idx));
LoadConstant(x3, *(uint32_t *)(OldCode + idx));
cmp(x2, x3);
csel(GetReg<RA_64>(Node), GetReg<RA_64>(Node), x1, Condition::eq);
len -= 8;
idx += 8;
}
while (len >= 4)
{
ldr(w2, MemOperand(x0, idx));
@@ -298,17 +304,16 @@ DEF_OP(ValidateCode) {
}
DEF_OP(RemoveCodeEntry) {
auto Op = IROp->C<IR::IROp_RemoveCodeEntry>();
// Arguments are passed as follows:
// X0: Thread
// X1: RIP
PushDynamicRegsAndLR();
mov(x0, STATE);
LoadConstant(x1, Op->RIP);
LoadConstant(x2, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntry));
LoadConstant(x1, IR->GetHeader()->Entry);
LoadConstant(x2, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntryFromJit));
SpillStaticRegs();
blr(x2);
FillStaticRegs();
@@ -319,15 +324,17 @@ DEF_OP(RemoveCodeEntry) {
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
PushDynamicRegsAndLR();
// x0 = CPUID Handler
// x1 = CPUID Function
// x2 = CPUID Leaf
LoadConstant(x0, reinterpret_cast<uint64_t>(&CTX->CPUID));
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
mov(x2, GetReg<RA_64>(Op->Header.Args[1].ID()));
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t);
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t, uint32_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
@@ -350,8 +357,8 @@ DEF_OP(CPUID) {
}
#undef DEF_OP
void JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(GUESTCALLDIRECT, GuestCallDirect);
REGISTER_OP(GUESTCALLINDIRECT, GuestCallIndirect);
REGISTER_OP(GUESTRETURN, GuestReturn);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VInsGPR) {
auto Op = IROp->C<IR::IROp_VInsGPR>();
mov(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
@@ -193,8 +199,8 @@ DEF_OP(Vector_FToF) {
}
#undef DEF_OP
void JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(AESImc) {
auto Op = IROp->C<IR::IROp_VAESImc>();
@@ -83,8 +89,8 @@ DEF_OP(AESKeyGenAssist) {
}
#undef DEF_OP
void JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
@@ -1,18 +1,24 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GetHostFlag) {
auto Op = IROp->C<IR::IROp_GetHostFlag>();
ubfx(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), Op->Flag, 1);
}
#undef DEF_OP
void JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
#undef REGISTER_OP
}
File diff suppressed because it is too large. Load diff
+23 -95
View File
@@ -1,9 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#pragma once
#include "Interface/Core/BlockCache.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
#include "aarch64/assembler-aarch64.h"
@@ -29,56 +36,18 @@ namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
// All but x29 are caller saved
const std::array<aarch64::Register, 16> SRA64 = {
x4, x5, x6, x7, x8, x9, x10, x11,
x12, x18, x17, x16, x15, x14, x13, x29
};
// All are callee saved
const std::array<aarch64::Register, 9> RA64 = {
x20, x21, x22, x23, x24, x25, x26, x27,
x19
};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA64Pair = {{
{x20, x21},
{x22, x23},
{x24, x25},
{x26, x27},
}};
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA32Pair = {{
{w20, w21},
{w22, w23},
{w24, w25},
{w26, w27},
}};
// All are caller saved
const std::array<aarch64::VRegister, 16> SRAFPR = {
v16, v17, v18, v19, v20, v21, v22, v23,
v24, v25, v26, v27, v28, v29, v30, v31
};
// v8..v15 = (lower 64bits) Callee saved
const std::array<aarch64::VRegister, 12> RAFPR = {
/*v0, v1, v2, v3,*/v4, v5, v6, v7, // v0 ~ v3 are used as temps
v8, v9, v10, v11, v12, v13, v14, v15
};
class JITCore final : public CPUBackend, public vixl::aarch64::Assembler {
class Arm64JITCore final : public CPUBackend, public Arm64Emitter {
public:
struct CodeBuffer {
uint8_t *Ptr;
size_t Size;
};
explicit JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
explicit Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
~JITCore() override;
~Arm64JITCore() override;
std::string GetName() override { return "JIT"; }
void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData) override;
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
@@ -86,21 +55,19 @@ public:
void ClearCache() override;
bool HandleSIGILL(int Signal, void *info, void *ucontext);
bool HandleSIGBUS(int Signal, void *info, void *ucontext);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
static CodeBuffer AllocateNewCodeBuffer(size_t Size);
CodeBuffer AllocateNewCodeBuffer(size_t Size);
void CopyNecessaryDataForCompileThread(CPUBackend *Original) override;
private:
Dispatcher *Dispatcher;
Label *PendingTargetLabel;
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *State;
FEXCore::IR::IRListView<true> const *IR;
FEXCore::Core::InternalThreadState *ThreadState;
FEXCore::IR::IRListView const *IR;
std::map<IR::OrderedNodeWrapper::NodeOffsetType, aarch64::Label> JumpTargets;
@@ -152,6 +119,7 @@ private:
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);
struct LiveRange {
uint32_t Begin;
@@ -161,9 +129,6 @@ private:
#if DEBUG
vixl::aarch64::Decoder Decoder;
#endif
vixl::aarch64::CPU CPU;
bool SupportsAtomics{};
bool SupportsRCPC{};
void EmplaceNewCodeBuffer(CodeBuffer Buffer) {
CurrentCodeBuffer = &CodeBuffers.emplace_back(Buffer);
@@ -180,8 +145,6 @@ private:
// For code safety we can't delete code buffers until outside of all signals
std::vector<CodeBuffer> CodeBuffers{};
// This is the codebuffer that our dispatcher lives in
CodeBuffer DispatcherCodeBuffer{};
// This is the current code buffer that we are tracking
CodeBuffer *CurrentCodeBuffer{};
@@ -189,42 +152,13 @@ private:
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 128;
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096 * 2;
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
#if DEBUG
vixl::aarch64::Disassembler Disasm;
#endif
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
void CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread);
void PushCalleeSavedRegisters();
void PopCalleeSavedRegisters();
static uint64_t ExitFunctionLink(JITCore *core, FEXCore::Core::InternalThreadState *Thread, uint64_t *record);
/**
* @name Dispatch Helper functions
* @{ */
uint64_t AbsoluteLoopTopAddressFillSRA{};
uint64_t AbsoluteLoopTopAddress{};
uint64_t ThreadPauseHandlerAddressSpillSRA{};
uint64_t ExitFunctionLinkerAddress{};
uint64_t ThreadPauseHandlerAddress{};
uint64_t ThreadStopHandlerAddressSpillSRA{};
uint64_t ThreadStopHandlerAddress{};
uint64_t PauseReturnInstruction{};
uint32_t SignalHandlerRefCounter{};
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
/** @} */
static uint64_t ExitFunctionLink(Arm64JITCore *core, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record);
struct CompilerSharedData {
uint64_t InterpreterFallbackHelperAddress{};
uint64_t SignalReturnInstruction{};
uint32_t *SignalHandlerRefCounterPtr{};
@@ -232,16 +166,9 @@ private:
CompilerSharedData ThreadSharedData;
IR::RegisterAllocationPass *RAPass;
IR::RegisterAllocationData *RAData;
uint32_t SpillSlots{};
void SpillStaticRegs();
void FillStaticRegs();
void PushDynamicRegsAndLR();
void PopDynamicRegsAndLR();
using OpHandler = void (JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
using OpHandler = void (Arm64JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
std::array<OpHandler, FEXCore::IR::IROps::OP_LAST + 1> OpHandlers {};
void RegisterALUHandlers();
void RegisterAtomicHandlers();
@@ -264,7 +191,9 @@ private:
///< ALU Ops
DEF_OP(TruncElementPair);
DEF_OP(Constant);
DEF_OP(EntrypointOffset);
DEF_OP(InlineConstant);
DEF_OP(InlineEntrypointOffset);
DEF_OP(CycleCounter);
DEF_OP(Add);
DEF_OP(Sub);
@@ -308,7 +237,6 @@ private:
DEF_OP(Float_ToGPR_U);
DEF_OP(Float_ToGPR_S);
DEF_OP(FCmp);
DEF_OP(F80Cmp);
///< Atomic ops
DEF_OP(CASPair);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(LoadContext) {
auto Op = IROp->C<IR::IROp_LoadContext>();
@@ -95,16 +101,15 @@ DEF_OP(StoreContext) {
DEF_OP(LoadRegister) {
auto Op = IROp->C<IR::IROp_LoadRegister>();
uint8_t OpSize = IROp->Size;
if (Op->Class == IR::GPRClass) {
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.gregs[0])) / 8;
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.gregs[0])) / 8;
auto regOffs = Op->Offset & 7;
LogMan::Throw::A(regId < SRA64.size(), "out of range regId");
auto reg = SRA64[regId];
switch(Op->Header.Size) {
case 1:
LogMan::Throw::A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
@@ -129,7 +134,7 @@ DEF_OP(LoadRegister) {
break;
}
} else if (Op->Class == IR::FPRClass) {
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.xmm[0][0])) / 16;
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0])) / 16;
auto regOffs = Op->Offset & 15;
LogMan::Throw::A(regId < SRAFPR.size(), "out of range regId");
@@ -181,8 +186,6 @@ DEF_OP(LoadRegister) {
DEF_OP(StoreRegister) {
auto Op = IROp->C<IR::IROp_StoreRegister>();
uint8_t OpSize = IROp->Size;
if (Op->Class == IR::GPRClass) {
auto regId = Op->Offset / 8 - 1;
@@ -191,7 +194,7 @@ DEF_OP(StoreRegister) {
LogMan::Throw::A(regId < SRA64.size(), "out of range regId");
auto reg = SRA64[regId];
switch(Op->Header.Size) {
case 1:
LogMan::Throw::A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
@@ -202,7 +205,7 @@ DEF_OP(StoreRegister) {
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 16);
break;
case 4:
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 32);
@@ -215,7 +218,7 @@ DEF_OP(StoreRegister) {
break;
}
} else if (Op->Class == IR::FPRClass) {
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.xmm[0][0])) / 16;
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0])) / 16;
auto regOffs = Op->Offset & 15;
LogMan::Throw::A(regId < SRAFPR.size(), "regId out of range");
@@ -530,7 +533,7 @@ DEF_OP(StoreFlag) {
strb(GetReg<RA_64>(Op->Header.Args[0].ID()), MemOperand(STATE, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag));
}
MemOperand JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
MemOperand Arm64JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
if (Offset.IsInvalid()) {
return MemOperand(Base);
} else {
@@ -551,6 +554,7 @@ MemOperand JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Bas
}
}
}
__builtin_unreachable();
}
DEF_OP(LoadMem) {
@@ -792,8 +796,8 @@ DEF_OP(VStoreMemElement) {
}
#undef DEF_OP
void JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(LOADCONTEXT, LoadContext);
REGISTER_OP(STORECONTEXT, StoreContext);
REGISTER_OP(LOADREGISTER, LoadRegister);
+13 -10
View File
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
@@ -32,20 +38,18 @@ DEF_OP(Break) {
case 4: { // HLT
// Time to quit
// Set our stack to the starting stack location
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)));
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)));
add(sp, TMP1, 0);
// Now we need to jump to the thread stop handler
LoadConstant(TMP1, ThreadStopHandlerAddressSpillSRA);
LoadConstant(TMP1, Dispatcher->ThreadStopHandlerAddressSpillSRA);
br(TMP1);
break;
}
case 6: { // INT3
if (SpillSlots) {
add(sp, sp, SpillSlots * 16);
}
ResetStack();
LoadConstant(TMP1, ThreadPauseHandlerAddressSpillSRA);
LoadConstant(TMP1, Dispatcher->ThreadPauseHandlerAddressSpillSRA);
br(TMP1);
break;
}
@@ -54,7 +58,6 @@ DEF_OP(Break) {
}
DEF_OP(GetRoundingMode) {
auto Op = IROp->C<IR::IROp_GetRoundingMode>();
auto Dst = GetReg<RA_64>(Node);
mrs(Dst, FPCR);
lsr(Dst, Dst, 22);
@@ -113,8 +116,8 @@ DEF_OP(SetRoundingMode) {
}
#undef DEF_OP
void JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(ExtractElementPair) {
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
switch (Op->Header.Size) {
@@ -29,18 +35,21 @@ DEF_OP(CreateElementPair) {
std::pair<aarch64::Register, aarch64::Register> Dst;
aarch64::Register RegFirst;
aarch64::Register RegSecond;
aarch64::Register RegTmp;
switch (Op->Header.Size) {
case 4: {
Dst = GetSrcPair<RA_32>(Node);
RegFirst = GetReg<RA_32>(Op->Header.Args[0].ID());
RegSecond = GetReg<RA_32>(Op->Header.Args[1].ID());
RegTmp = w0;
break;
}
case 8: {
Dst = GetSrcPair<RA_64>(Node);
RegFirst = GetReg<RA_64>(Op->Header.Args[0].ID());
RegSecond = GetReg<RA_64>(Op->Header.Args[1].ID());
RegTmp = x0;
break;
}
default: LogMan::Msg::A("Unknown Size"); break;
@@ -53,7 +62,9 @@ DEF_OP(CreateElementPair) {
mov(Dst.second, RegSecond);
mov(Dst.first, RegFirst);
} else {
LogMan::Msg::A("Unhandled CreateElementPair");
mov(RegTmp, RegFirst);
mov(Dst.second, RegSecond);
mov(Dst.first, RegTmp);
}
}
@@ -63,8 +74,8 @@ DEF_OP(Mov) {
}
#undef DEF_OP
void JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
REGISTER_OP(MOV, Mov);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|arm64
$end_info$
*/
#include "Interface/Core/JIT/Arm64/JITClass.h"
namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VectorZero) {
uint8_t OpSize = IROp->Size;
switch (OpSize) {
@@ -909,7 +915,17 @@ DEF_OP(VZip2) {
}
DEF_OP(VBSL) {
LogMan::Msg::A("Unimplemented");
auto Op = IROp->C<IR::IROp_VBSL>();
if (IROp->Size == 16) {
mov(VTMP1.V16B(), GetSrc(Op->Header.Args[0].ID()).V16B());
bsl(VTMP1.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), GetSrc(Op->Header.Args[2].ID()).V16B());
mov(GetDst(Node).V16B(), VTMP1.V16B());
}
else {
mov(VTMP1.V8B(), GetSrc(Op->Header.Args[0].ID()).V8B());
bsl(VTMP1.V8B(), GetSrc(Op->Header.Args[1].ID()).V8B(), GetSrc(Op->Header.Args[2].ID()).V8B());
mov(GetDst(Node).V8B(), VTMP1.V8B());
}
}
DEF_OP(VCMPEQ) {
@@ -1509,7 +1525,7 @@ DEF_OP(VSShrS) {
DEF_OP(VInsElement) {
auto Op = IROp->C<IR::IROp_VInsElement>();
auto reg = GetSrc(Op->Header.Args[0].ID());
if (GetDst(Node).GetCode() != reg.GetCode()) {
@@ -1544,7 +1560,7 @@ DEF_OP(VInsElement) {
DEF_OP(VInsScalarElement) {
auto Op = IROp->C<IR::IROp_VInsScalarElement>();
auto reg = GetSrc(Op->Header.Args[0].ID());
if (GetDst(Node).GetCode() != reg.GetCode()) {
@@ -2102,8 +2118,8 @@ DEF_OP(VTBL1) {
}
#undef DEF_OP
void JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void Arm64JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(CREATEVECTOR2, CreateVector2);
+2 -1
View File
@@ -11,5 +11,6 @@ struct InternalThreadState;
namespace FEXCore::CPU {
class CPUBackend;
FEXCore::CPU::CPUBackend *CreateJITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
FEXCore::CPU::CPUBackend *CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
FEXCore::CPU::CPUBackend *CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
}
+45 -26
View File
@@ -1,8 +1,14 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(TruncElementPair) {
auto Op = IROp->C<IR::IROp_TruncElementPair>();
@@ -23,17 +29,28 @@ DEF_OP(Constant) {
mov(GetDst<RA_64>(Node), Op->Constant);
}
DEF_OP(EntrypointOffset) {
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
auto Constant = IR->GetHeader()->Entry + Op->Offset;
mov(GetDst<RA_64>(Node), Constant);
}
DEF_OP(InlineConstant) {
//nop
}
DEF_OP(InlineEntrypointOffset) {
//nop
}
DEF_OP(CycleCounter) {
#ifdef DEBUG_CYCLES
mov (GetDst<RA_64>(Node), 0);
#else
rdtsc();
shl(rdx, 32);
or(rax, rdx);
or_(rax, rdx);
mov (GetDst<RA_64>(Node), rax);
#endif
}
@@ -373,9 +390,9 @@ DEF_OP(Or) {
uint64_t Const;
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
or (rax, Const);
or_(rax, Const);
} else {
or (rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
or_(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
}
mov(Dst, rax);
}
@@ -386,9 +403,9 @@ DEF_OP(And) {
mov(rax, GetSrc<RA_64>(Op->Header.Args[0].ID()));
uint64_t Const;
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
and (rax, Const);
and_(rax, Const);
} else {
and(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
and_(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
}
mov(Dst, rax);
}
@@ -399,9 +416,9 @@ DEF_OP(Xor) {
mov(rax, GetSrc<RA_64>(Op->Header.Args[0].ID()));
uint64_t Const;
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
xor(rax, Const);
xor_(rax, Const);
} else {
xor(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
xor_(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
}
mov(Dst, rax);
}
@@ -428,7 +445,7 @@ DEF_OP(Lshl) {
};
} else {
mov(rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
and(rcx, Mask);
and_(rcx, Mask);
switch (OpSize) {
case 4:
@@ -476,7 +493,7 @@ DEF_OP(Lshr) {
} else {
mov (rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
and(rcx, Mask);
and_(rcx, Mask);
switch (OpSize) {
case 1:
@@ -534,7 +551,7 @@ DEF_OP(Ashr) {
} else {
mov (rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
and(rcx, Mask);
and_(rcx, Mask);
switch (OpSize) {
case 1:
movsx(rax, GetSrc<RA_8>(Op->Header.Args[0].ID()));
@@ -583,7 +600,7 @@ DEF_OP(Ror) {
}
} else {
mov (rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
and(rcx, Mask);
and_(rcx, Mask);
switch (OpSize) {
case 4: {
mov(eax, GetSrc<RA_32>(Op->Header.Args[0].ID()));
@@ -790,10 +807,10 @@ DEF_OP(FindLSB) {
bsf(rcx, GetSrc<RA_64>(Op->Header.Args[0].ID()));
mov(rax, 0x40);
cmovz(rcx, rax);
xor(rax, rax);
xor_(rax, rax);
cmp(GetSrc<RA_64>(Op->Header.Args[0].ID()), 1);
sbb(rax, rax);
or(rax, rcx);
or_(rax, rcx);
mov (GetDst<RA_64>(Node), rax);
}
@@ -870,7 +887,7 @@ DEF_OP(CountLeadingZeroes) {
Label Skip;
je(Skip);
bsr(ax, GetSrc<RA_16>(Op->Header.Args[0].ID()));
xor(ax, 0xF);
xor_(ax, 0xF);
movzx(eax, ax);
L(Skip);
mov(GetDst<RA_32>(Node), eax);
@@ -882,7 +899,7 @@ DEF_OP(CountLeadingZeroes) {
Label Skip;
je(Skip);
bsr(eax, GetSrc<RA_32>(Op->Header.Args[0].ID()));
xor(eax, 0x1F);
xor_(eax, 0x1F);
L(Skip);
mov(GetDst<RA_32>(Node), eax);
break;
@@ -893,7 +910,7 @@ DEF_OP(CountLeadingZeroes) {
Label Skip;
je(Skip);
bsr(rax, GetSrc<RA_64>(Op->Header.Args[0].ID()));
xor(rax, 0x3F);
xor_(rax, 0x3F);
L(Skip);
mov(GetDst<RA_64>(Node), rax);
break;
@@ -939,15 +956,15 @@ DEF_OP(Bfi) {
mov(Dst, GetSrc<RA_64>(Op->Header.Args[0].ID()));
mov(TMP2, DestMask);
and(Dst, TMP2);
and_(Dst, TMP2);
mov(TMP2, SourceMask);
and(TMP1, TMP2);
and_(TMP1, TMP2);
shl(TMP1, Op->lsb);
or_(Dst, TMP1);
if (OpSize != 8) {
mov(rcx, uint64_t((1ULL << (OpSize * 8)) - 1));
and(Dst, rcx);
and_(Dst, rcx);
}
}
@@ -987,7 +1004,7 @@ DEF_OP(Bfe) {
if (Op->Width != 64) {
mov(rcx, uint64_t((1ULL << Op->Width) - 1));
and(Dst, rcx);
and_(Dst, rcx);
}
}
@@ -1136,32 +1153,34 @@ DEF_OP(FCmp) {
mov(rcx, 0);
setb(cl);
shl(rcx, IR::FCMP_FLAG_LT);
or(rdx, rcx);
or_(rdx, rcx);
}
if (Op->Flags & (1 << IR::FCMP_FLAG_UNORDERED)) {
sahf();
mov(rcx, 0);
setp(cl);
shl(rcx, IR::FCMP_FLAG_UNORDERED);
or(rdx, rcx);
or_(rdx, rcx);
}
if (Op->Flags & (1 << IR::FCMP_FLAG_EQ)) {
sahf();
mov(rcx, 0);
setz(cl);
shl(rcx, IR::FCMP_FLAG_EQ);
or(rdx, rcx);
or_(rdx, rcx);
}
mov (GetDst<RA_64>(Node), rdx);
}
#undef DEF_OP
void JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
REGISTER_OP(CONSTANT, Constant);
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
REGISTER_OP(INLINECONSTANT, InlineConstant);
REGISTER_OP(INLINEENTRYPOINTOFFSET, InlineEntrypointOffset);
REGISTER_OP(CYCLECOUNTER, CycleCounter);
REGISTER_OP(ADD, Add);
REGISTER_OP(SUB, Sub);
@@ -1,8 +1,14 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(CASPair) {
auto Op = IROp->C<IR::IROp_CAS>();
uint8_t OpSize = IROp->Size;
@@ -155,16 +161,16 @@ DEF_OP(AtomicAnd) {
lock();
switch (Op->Size) {
case 1:
and(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
and_(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
break;
case 2:
and(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
and_(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
break;
case 4:
and(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
and_(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
break;
case 8:
and(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
and_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
}
@@ -177,16 +183,16 @@ DEF_OP(AtomicOr) {
lock();
switch (Op->Size) {
case 1:
or(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
or_(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
break;
case 2:
or(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
or_(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
break;
case 4:
or(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
or_(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
break;
case 8:
or(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
or_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
}
@@ -199,16 +205,16 @@ DEF_OP(AtomicXor) {
lock();
switch (Op->Size) {
case 1:
xor(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
xor_(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
break;
case 2:
xor(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
xor_(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
break;
case 4:
xor(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
xor_(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
break;
case 8:
xor(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
xor_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
break;
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
}
@@ -329,7 +335,7 @@ DEF_OP(AtomicFetchAnd) {
L(Loop);
mov(TMP2.cvt8(), TMP1.cvt8());
mov(TMP3.cvt8(), TMP1.cvt8());
and(TMP2.cvt8(), GetSrc<RA_8>(Op->Header.Args[1].ID()));
and_(TMP2.cvt8(), GetSrc<RA_8>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(byte [MemReg], TMP2.cvt8());
@@ -345,7 +351,7 @@ DEF_OP(AtomicFetchAnd) {
L(Loop);
mov(TMP2.cvt16(), TMP1.cvt16());
mov(TMP3.cvt16(), TMP1.cvt16());
and(TMP2.cvt16(), GetSrc<RA_16>(Op->Header.Args[1].ID()));
and_(TMP2.cvt16(), GetSrc<RA_16>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(word [MemReg], TMP2.cvt16());
@@ -362,7 +368,7 @@ DEF_OP(AtomicFetchAnd) {
L(Loop);
mov(TMP2.cvt32(), TMP1.cvt32());
mov(TMP3.cvt32(), TMP1.cvt32());
and(TMP2.cvt32(), GetSrc<RA_32>(Op->Header.Args[1].ID()));
and_(TMP2.cvt32(), GetSrc<RA_32>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(dword [MemReg], TMP2.cvt32());
@@ -379,7 +385,7 @@ DEF_OP(AtomicFetchAnd) {
L(Loop);
mov(TMP2.cvt64(), TMP1.cvt64());
mov(TMP3.cvt64(), TMP1.cvt64());
and(TMP2.cvt64(), GetSrc<RA_64>(Op->Header.Args[1].ID()));
and_(TMP2.cvt64(), GetSrc<RA_64>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(qword [MemReg], TMP2.cvt64());
@@ -406,7 +412,7 @@ DEF_OP(AtomicFetchOr) {
L(Loop);
mov(TMP2.cvt8(), TMP1.cvt8());
mov(TMP3.cvt8(), TMP1.cvt8());
or(TMP2.cvt8(), GetSrc<RA_8>(Op->Header.Args[1].ID()));
or_(TMP2.cvt8(), GetSrc<RA_8>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(byte [MemReg], TMP2.cvt8());
@@ -422,7 +428,7 @@ DEF_OP(AtomicFetchOr) {
L(Loop);
mov(TMP2.cvt16(), TMP1.cvt16());
mov(TMP3.cvt16(), TMP1.cvt16());
or(TMP2.cvt16(), GetSrc<RA_16>(Op->Header.Args[1].ID()));
or_(TMP2.cvt16(), GetSrc<RA_16>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(word [MemReg], TMP2.cvt16());
@@ -439,7 +445,7 @@ DEF_OP(AtomicFetchOr) {
L(Loop);
mov(TMP2.cvt32(), TMP1.cvt32());
mov(TMP3.cvt32(), TMP1.cvt32());
or(TMP2.cvt32(), GetSrc<RA_32>(Op->Header.Args[1].ID()));
or_(TMP2.cvt32(), GetSrc<RA_32>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(dword [MemReg], TMP2.cvt32());
@@ -456,7 +462,7 @@ DEF_OP(AtomicFetchOr) {
L(Loop);
mov(TMP2.cvt64(), TMP1.cvt64());
mov(TMP3.cvt64(), TMP1.cvt64());
or(TMP2.cvt64(), GetSrc<RA_64>(Op->Header.Args[1].ID()));
or_(TMP2.cvt64(), GetSrc<RA_64>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(qword [MemReg], TMP2.cvt64());
@@ -483,7 +489,7 @@ DEF_OP(AtomicFetchXor) {
L(Loop);
mov(TMP2.cvt8(), TMP1.cvt8());
mov(TMP3.cvt8(), TMP1.cvt8());
xor(TMP2.cvt8(), GetSrc<RA_8>(Op->Header.Args[1].ID()));
xor_(TMP2.cvt8(), GetSrc<RA_8>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(byte [MemReg], TMP2.cvt8());
@@ -499,7 +505,7 @@ DEF_OP(AtomicFetchXor) {
L(Loop);
mov(TMP2.cvt16(), TMP1.cvt16());
mov(TMP3.cvt16(), TMP1.cvt16());
xor(TMP2.cvt16(), GetSrc<RA_16>(Op->Header.Args[1].ID()));
xor_(TMP2.cvt16(), GetSrc<RA_16>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(word [MemReg], TMP2.cvt16());
@@ -516,7 +522,7 @@ DEF_OP(AtomicFetchXor) {
L(Loop);
mov(TMP2.cvt32(), TMP1.cvt32());
mov(TMP3.cvt32(), TMP1.cvt32());
xor(TMP2.cvt32(), GetSrc<RA_32>(Op->Header.Args[1].ID()));
xor_(TMP2.cvt32(), GetSrc<RA_32>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(dword [MemReg], TMP2.cvt32());
@@ -533,7 +539,7 @@ DEF_OP(AtomicFetchXor) {
L(Loop);
mov(TMP2.cvt64(), TMP1.cvt64());
mov(TMP3.cvt64(), TMP1.cvt64());
xor(TMP2.cvt64(), GetSrc<RA_64>(Op->Header.Args[1].ID()));
xor_(TMP2.cvt64(), GetSrc<RA_64>(Op->Header.Args[1].ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(qword [MemReg], TMP2.cvt64());
@@ -548,8 +554,8 @@ DEF_OP(AtomicFetchXor) {
}
#undef DEF_OP
void JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
@@ -1,11 +1,18 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <Interface/HLE/Thunks/Thunks.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GuestCallDirect) {
LogMan::Msg::D("Unimplemented");
}
@@ -39,7 +46,7 @@ DEF_OP(CallbackReturn) {
sub(dword [rax], 1);
// We need to adjust an additional 8 bytes to get back to the original "misaligned" RSP state
add(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 8);
add(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])], 8);
// Now jump back to the thunk
// XXX: XMM?
@@ -66,7 +73,7 @@ DEF_OP(ExitFunction) {
uint64_t NewRIP;
if (IsInlineConstant(Op->NewRIP, &NewRIP)) {
if (IsInlineConstant(Op->NewRIP, &NewRIP) || IsInlineEntrypointOffset(Op->NewRIP, &NewRIP)) {
Label l_BranchHost;
Label l_BranchGuest;
@@ -74,28 +81,28 @@ DEF_OP(ExitFunction) {
jmp(qword[rax]);
L(l_BranchHost);
dq(ExitFunctionLinkerAddress);
dq(Dispatcher->ExitFunctionLinkerAddress);
L(l_BranchGuest);
dq(NewRIP);
} else {
Xbyak::Reg RipReg = GetSrc<RA_64>(Op->NewRIP.ID());
// L1 Cache
mov(rcx, ThreadState->BlockCache->GetL1Pointer());
mov(rcx, ThreadState->LookupCache->GetL1Pointer());
mov(rax, RipReg);
and_(rax, BlockCache::L1_ENTRIES_MASK);
and_(rax, LookupCache::L1_ENTRIES_MASK);
shl(rax, 4);
Xbyak::RegExp LookupBase = rcx + rax;
cmp(qword[LookupBase + 8], RipReg);
jne(FullLookup);
jmp(qword[LookupBase + 0]);
L(FullLookup);
mov(rax, AbsoluteLoopTopAddress);
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], RipReg);
mov(rax, Dispatcher->AbsoluteLoopTopAddress);
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.rip)], RipReg);
jmp(rax);
}
@@ -227,7 +234,9 @@ DEF_OP(Thunk) {
mov(rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
mov(rax, reinterpret_cast<uintptr_t>(Op->ThunkFnPtr));
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
mov(rax, reinterpret_cast<uintptr_t>(thunkFn));
call(rax);
if (NumPush & 1)
@@ -239,12 +248,12 @@ DEF_OP(Thunk) {
DEF_OP(ValidateCode) {
auto Op = IROp->C<IR::IROp_ValidateCode>();
uint8_t* OldCode = (uint8_t*)&Op->CodeOriginal;
uint8_t* OldCode = (uint8_t*)&Op->CodeOriginalLow;
int len = Op->CodeLength;
int idx = 0;
xor_(GetDst<RA_64>(Node), GetDst<RA_64>(Node));
mov(rax, Op->CodePtr);
mov(rax, IR->GetHeader()->Entry + Op->Offset);
mov(rbx, 1);
while (len >= 4) {
cmp(dword[rax + idx], *(uint32_t*)(OldCode + idx));
@@ -268,8 +277,6 @@ DEF_OP(ValidateCode) {
}
DEF_OP(RemoveCodeEntry) {
auto Op = IROp->C<IR::IROp_RemoveCodeEntry>();
auto NumPush = RA64.size();
for (auto &Reg : RA64)
@@ -279,11 +286,11 @@ DEF_OP(RemoveCodeEntry) {
sub(rsp, 8); // Align
mov(rdi, STATE);
mov(rax, Op->RIP); // imm64 move
mov(rax, IR->GetHeader()->Entry); // imm64 move
mov(rsi, rax);
mov(rax, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntry));
mov(rax, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntryFromJit));
call(rax);
if (NumPush & 1)
@@ -296,7 +303,7 @@ DEF_OP(RemoveCodeEntry) {
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t Function);
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t Function, uint32_t Leaf);
union {
ClassPtrType ClassPtr;
uint64_t Raw;
@@ -313,6 +320,7 @@ DEF_OP(CPUID) {
// Result: RAX, RDX. 4xi32
mov (rsi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
mov (rdx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
mov (rdi, reinterpret_cast<uint64_t>(&CTX->CPUID));
auto NumPush = RA64.size();
@@ -338,8 +346,8 @@ DEF_OP(CPUID) {
}
#undef DEF_OP
void JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(GUESTCALLDIRECT, GuestCallDirect);
REGISTER_OP(GUESTCALLINDIRECT, GuestCallIndirect);
REGISTER_OP(GUESTRETURN, GuestReturn);
@@ -1,9 +1,15 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VInsGPR) {
auto Op = IROp->C<IR::IROp_VInsGPR>();
movapd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
@@ -171,8 +177,8 @@ DEF_OP(Vector_FToF) {
}
#undef DEF_OP
void JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
@@ -1,8 +1,14 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(AESImc) {
auto Op = IROp->C<IR::IROp_VAESImc>();
@@ -35,8 +41,8 @@ DEF_OP(AESKeyGenAssist) {
}
#undef DEF_OP
void JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
@@ -1,21 +1,27 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(GetHostFlag) {
auto Op = IROp->C<IR::IROp_GetHostFlag>();
mov(rax, GetSrc<RA_64>(Op->Header.Args[0].ID()));
shr(rax, Op->Flag);
and(rax, 1);
and_(rax, 1);
mov(GetDst<RA_64>(Node), rax);
}
#undef DEF_OP
void JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
#undef REGISTER_OP
}
File diff suppressed because it is too large. Load diff
@@ -1 +0,0 @@
+31 -38
View File
@@ -1,11 +1,18 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#pragma once
#include "Interface/Core/BlockCache.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/BlockSamplingData.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/JIT/x86_64/JIT.h"
#include "Common/MathUtils.h"
#define XBYAK64
#include <xbyak/xbyak.h>
#include <xbyak/xbyak_util.h>
@@ -14,6 +21,7 @@ using namespace Xbyak;
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include <tuple>
@@ -50,15 +58,15 @@ namespace FEXCore::CPU {
using namespace Xbyak::util;
const std::array<Xbyak::Reg, 9> RA64 = { rsi, r8, r9, r10, r11, rbp, r12, r13, r15 };
const std::array<std::pair<Xbyak::Reg, Xbyak::Reg>, 4> RA64Pair = {{ {rsi, r8}, {r9, r10}, {r11, rbp}, {r12, r13} }};
const std::array<Xbyak::Reg, 11> RAXMM = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10 };
const std::array<Xbyak::Xmm, 11> RAXMM_x = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10 };
const std::array<Xbyak::Reg, 11> RAXMM = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
const std::array<Xbyak::Xmm, 11> RAXMM_x = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
class JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
class X86JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
public:
explicit JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
~JITCore() override;
explicit X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
~X86JITCore() override;
std::string GetName() override { return "JIT"; }
void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData) override;
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
@@ -68,17 +76,14 @@ public:
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 256;
bool HandleSIGILL(int Signal, void *info, void *ucontext);
bool HandleSignalPause(int Signal, void *info, void *ucontext);
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
void CopyNecessaryDataForCompileThread(CPUBackend *Original) override;
private:
Label* PendingTargetLabel{};
FEXCore::Context::Context *CTX;
FEXCore::Core::InternalThreadState *ThreadState;
FEXCore::IR::IRListView<true> const *IR;
FEXCore::IR::IRListView const *IR;
FEXCore::CPU::Dispatcher *Dispatcher;
std::unordered_map<IR::OrderedNodeWrapper::NodeOffsetType, Label> JumpTargets;
Xbyak::util::Cpu Features{};
@@ -106,7 +111,7 @@ private:
constexpr static uint8_t RA_64 = 3;
constexpr static uint8_t RA_XMM = 4;
uint32_t GetPhys(uint32_t Node);
IR::PhysicalRegister GetPhys(uint32_t Node);
bool IsFPR(uint32_t Node);
bool IsGPR(uint32_t Node);
@@ -125,21 +130,20 @@ private:
Xbyak::RegExp GenerateModRM(Xbyak::Reg 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);
void CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread);
IR::RegisterAllocationPass *RAPass;
FEXCore::IR::RegisterAllocationData *RAData;
#ifdef BLOCKSTATS
bool GetSamplingData {true};
#endif
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096 * 1;
void EmplaceNewCodeBuffer(CodeBuffer Buffer) {
CurrentCodeBuffer = &CodeBuffers.emplace_back(Buffer);
}
static uint64_t ExitFunctionLink(JITCore* code, FEXCore::Core::InternalThreadState *Thread, uint64_t *record);
static uint64_t ExitFunctionLink(X86JITCore* code, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record);
// This is the initial code buffer that we will fall back to
// In a program without signals and code clearing, we will typically
@@ -150,23 +154,10 @@ private:
// For code safety we can't delete code buffers until outside of all signals
std::vector<CodeBuffer> CodeBuffers{};
// This is the codebuffer that our dispatcher lives in
CodeBuffer DispatcherCodeBuffer{};
// This is the current code buffer that we are tracking
CodeBuffer *CurrentCodeBuffer{};
uint64_t AbsoluteLoopTopAddress{};
uint64_t ExitFunctionLinkerAddress{};
uint64_t ThreadStopHandlerAddress{};
uint64_t ThreadPauseHandlerAddress{};
uint64_t PauseReturnInstruction{};
uint32_t SignalHandlerRefCounter{};
struct CompilerSharedData {
void *InterpreterFallbackHelperAddress;
uint64_t SignalHandlerReturnAddress{};
uint32_t *SignalHandlerRefCounterPtr{};
@@ -174,17 +165,14 @@ private:
CompilerSharedData ThreadSharedData;
void StoreThreadState(int Signal, void *ucontext);
void RestoreThreadState(void *ucontext);
std::stack<uint64_t> SignalFrames;
uint32_t SpillSlots{};
using SetCC = void (JITCore::*)(const Operand& op);
using CMovCC = void (JITCore::*)(const Reg& reg, const Operand& op);
using JCC = void (JITCore::*)(const Label& label, LabelType type);
using SetCC = void (X86JITCore::*)(const Operand& op);
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
using JCC = void (X86JITCore::*)(const Label& label, LabelType type);
std::tuple<SetCC, CMovCC, JCC> GetCC(IR::CondClassType cond);
using OpHandler = void (JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
using OpHandler = void (X86JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
std::array<OpHandler, FEXCore::IR::IROps::OP_LAST + 1> OpHandlers {};
void RegisterALUHandlers();
void RegisterAtomicHandlers();
@@ -196,6 +184,9 @@ private:
void RegisterMoveHandlers();
void RegisterVectorHandlers();
void RegisterEncryptionHandlers();
void PushRegs();
void PopRegs();
#define DEF_OP(x) void Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
///< Unhandled handler
@@ -207,7 +198,9 @@ private:
///< ALU Ops
DEF_OP(TruncElementPair);
DEF_OP(Constant);
DEF_OP(EntrypointOffset);
DEF_OP(InlineConstant);
DEF_OP(InlineEntrypointOffset);
DEF_OP(CycleCounter);
DEF_OP(Add);
DEF_OP(Sub);
@@ -1,3 +1,9 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -5,7 +11,7 @@
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(LoadContext) {
auto Op = IROp->C<IR::IROp_LoadContext>();
@@ -419,7 +425,7 @@ DEF_OP(StoreFlag) {
mov(byte [STATE + (offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag)], al);
}
Xbyak::RegExp JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
Xbyak::RegExp X86JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
if (Offset.IsInvalid()) {
return Base;
} else {
@@ -568,8 +574,8 @@ DEF_OP(VStoreMemElement) {
}
#undef DEF_OP
void JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(LOADCONTEXT, LoadContext);
REGISTER_OP(STORECONTEXT, StoreContext);
REGISTER_OP(LOADREGISTER, Unhandled); // SRA specific, not supported on this backend
+17 -11
View File
@@ -1,3 +1,9 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
@@ -6,7 +12,7 @@ static void PrintValue(uint64_t Value) {
LogMan::Msg::D("Value: 0x%lx", Value);
}
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
@@ -34,10 +40,10 @@ DEF_OP(Break) {
case 4: { // HLT
// Time to quit
// Set our stack to the starting stack location
mov(rsp, qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)]);
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
// Now we need to jump to the thread stop handler
mov(TMP1, ThreadStopHandlerAddress);
mov(TMP1, Dispatcher->ThreadStopHandlerAddress);
jmp(TMP1);
break;
}
@@ -48,18 +54,18 @@ DEF_OP(Break) {
if (SpillSlots) {
add(rsp, SpillSlots * 16);
}
// This jump target needs to be a constant offset here
mov(TMP1, ThreadPauseHandlerAddress);
mov(TMP1, Dispatcher->ThreadPauseHandlerAddress);
jmp(TMP1);
}
else {
// If we don't have a gdb server attached then....crash?
// Treat this case like HLT
mov(rsp, qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)]);
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
// Now we need to jump to the thread stop handler
mov(TMP1, ThreadStopHandlerAddress);
mov(TMP1, Dispatcher->ThreadStopHandlerAddress);
jmp(TMP1);
}
break;
@@ -89,10 +95,10 @@ DEF_OP(SetRoundingMode) {
mov(TMP1.cvt32(), dword [rsp]);
// Insert the new rounding mode
and(TMP1.cvt32(), ~(0b111 << 13));
and_(TMP1.cvt32(), ~(0b111 << 13));
mov(TMP2.cvt32(), Src);
shl(TMP2.cvt32(), 13);
or(TMP1.cvt32(), TMP2.cvt32());
or_(TMP1.cvt32(), TMP2.cvt32());
// Store it to mxcsr
// Only loads from memory
@@ -125,8 +131,8 @@ DEF_OP(Print) {
}
#undef DEF_OP
void JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
@@ -1,9 +1,15 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(ExtractElementPair) {
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
switch (Op->Header.Size) {
@@ -28,18 +34,21 @@ DEF_OP(CreateElementPair) {
std::pair<Xbyak::Reg, Xbyak::Reg> Dst;
Xbyak::Reg RegFirst;
Xbyak::Reg RegSecond;
Xbyak::Reg RegTmp;
switch (Op->Header.Size) {
case 4: {
Dst = GetSrcPair<RA_32>(Node);
RegFirst = GetSrc<RA_32>(Op->Header.Args[0].ID());
RegSecond = GetSrc<RA_32>(Op->Header.Args[1].ID());
RegTmp = eax;
break;
}
case 8: {
Dst = GetSrcPair<RA_64>(Node);
RegFirst = GetSrc<RA_64>(Op->Header.Args[0].ID());
RegSecond = GetSrc<RA_64>(Op->Header.Args[1].ID());
RegTmp = rax;
break;
}
default: LogMan::Msg::A("Unknown Size"); break;
@@ -52,7 +61,9 @@ DEF_OP(CreateElementPair) {
mov(Dst.second, RegSecond);
mov(Dst.first, RegFirst);
} else {
LogMan::Msg::A("Unhandled CreateElementPair");
mov(RegTmp, RegFirst);
mov(Dst.second, RegSecond);
mov(Dst.first, RegTmp);
}
}
@@ -62,8 +73,8 @@ DEF_OP(Mov) {
}
#undef DEF_OP
void JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
REGISTER_OP(MOV, Mov);
@@ -1,10 +1,16 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
namespace FEXCore::CPU {
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
DEF_OP(VectorZero) {
auto Dst = GetDst(Node);
vpxor(Dst, Dst, Dst);
@@ -262,17 +268,17 @@ DEF_OP(VAddP) {
vpaddw(GetDst(Node), xmm15, xmm14);
switch (Op->Header.ElementSize) {
case 1:
vpunpcklbw(xmm11, xmm15, xmm14);
vpunpcklbw(xmm0, xmm15, xmm14);
vpunpckhbw(xmm12, xmm15, xmm14);
vpunpcklbw(xmm15, xmm11, xmm12);
vpunpckhbw(xmm14, xmm11, xmm12);
vpunpcklbw(xmm15, xmm0, xmm12);
vpunpckhbw(xmm14, xmm0, xmm12);
vpunpcklbw(xmm11, xmm15, xmm14);
vpunpcklbw(xmm0, xmm15, xmm14);
vpunpckhbw(xmm12, xmm15, xmm14);
vpunpcklbw(xmm15, xmm11, xmm12);
vpunpckhbw(xmm14, xmm11, xmm12);
vpunpcklbw(xmm15, xmm0, xmm12);
vpunpckhbw(xmm14, xmm0, xmm12);
vpaddb(GetDst(Node), xmm15, xmm14);
break;
@@ -291,17 +297,17 @@ DEF_OP(VAddP) {
movdqu(xmm15, GetSrc(Op->Header.Args[0].ID()));
movdqu(xmm14, GetSrc(Op->Header.Args[1].ID()));
vpunpcklbw(xmm11, xmm15, xmm14);
vpunpcklbw(xmm0, xmm15, xmm14);
vpunpckhbw(xmm12, xmm15, xmm14);
vpunpcklbw(xmm15, xmm11, xmm12);
vpunpckhbw(xmm14, xmm11, xmm12);
vpunpcklbw(xmm15, xmm0, xmm12);
vpunpckhbw(xmm14, xmm0, xmm12);
vpunpcklbw(xmm11, xmm15, xmm14);
vpunpcklbw(xmm0, xmm15, xmm14);
vpunpckhbw(xmm12, xmm15, xmm14);
vpunpcklbw(xmm15, xmm11, xmm12);
vpunpckhbw(xmm14, xmm11, xmm12);
vpunpcklbw(xmm15, xmm0, xmm12);
vpunpckhbw(xmm14, xmm0, xmm12);
vpaddb(GetDst(Node), xmm15, xmm14);
break;
@@ -327,7 +333,7 @@ DEF_OP(VAddV) {
switch (Op->Header.ElementSize) {
case 2: {
for (int i = Elements; i > 1; i >>= 1) {
phaddw(Dest, Src);
vphaddw(Dest, Src, Dest);
Src = Dest;
}
pextrw(eax, Dest, 0);
@@ -336,7 +342,7 @@ DEF_OP(VAddV) {
}
case 4: {
for (int i = Elements; i > 1; i >>= 1) {
phaddd(Dest, Src);
vphaddd(Dest, Src, Dest);
Src = Dest;
}
pextrd(eax, Dest, 0);
@@ -912,7 +918,10 @@ DEF_OP(VZip2) {
}
DEF_OP(VBSL) {
LogMan::Msg::A("Unimplemented");
auto Op = IROp->C<IR::IROp_VBSL>();
vpand(xmm0, GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
vpandn(xmm12, GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[2].ID()));
vpor(GetDst(Node), xmm0, xmm12);
}
DEF_OP(VCMPEQ) {
@@ -1866,8 +1875,8 @@ DEF_OP(VTBL1) {
}
#undef DEF_OP
void JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
void X86JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(CREATEVECTOR2, CreateVector2);
@@ -1,10 +1,17 @@
/*
$info$
tags: glue|block-database
desc: Stores information about blocks, and provides C++ implementations to lookup the blocks
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Core.h"
#include "Interface/Core/BlockCache.h"
#include "Interface/Core/LookupCache.h"
#include <sys/mman.h>
namespace FEXCore {
BlockCache::BlockCache(FEXCore::Context::Context *CTX)
LookupCache::LookupCache(FEXCore::Context::Context *CTX)
: ctx {CTX} {
// Block cache ends up looking like this
@@ -36,13 +43,13 @@ BlockCache::BlockCache(FEXCore::Context::Context *CTX)
VirtualMemSize = ctx->Config.VirtualMemSize;
}
BlockCache::~BlockCache() {
LookupCache::~LookupCache() {
munmap(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8);
munmap(reinterpret_cast<void*>(PageMemory), CODE_SIZE);
munmap(reinterpret_cast<void*>(L1Pointer), L1_SIZE);
}
void BlockCache::HintUsedRange(uint64_t Address, uint64_t Size) {
void LookupCache::HintUsedRange(uint64_t Address, uint64_t Size) {
// Tell the kernel we will definitely need [Address, Address+Size) mapped for the page pointer
// Page Pointer is allocated per page, so shift by page size
Address >>= 12;
@@ -50,14 +57,22 @@ void BlockCache::HintUsedRange(uint64_t Address, uint64_t Size) {
madvise(reinterpret_cast<void*>(PagePointer + Address), Size, MADV_WILLNEED);
}
void BlockCache::ClearCache() {
void LookupCache::ClearL2Cache() {
// Clear out the page memory
madvise(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8, MADV_DONTNEED);
madvise(reinterpret_cast<void*>(PageMemory), CODE_SIZE, MADV_DONTNEED);
madvise(reinterpret_cast<void*>(L1Pointer), L1_SIZE, MADV_DONTNEED);
AllocateOffset = 0;
}
void LookupCache::ClearCache() {
// Clear L1
madvise(reinterpret_cast<void*>(L1Pointer), L1_SIZE, MADV_DONTNEED);
// Clear L2
ClearL2Cache();
// All code is gone, remove links
BlockLinks.clear();
// All code is gone, clear the block list
BlockList.clear();
}
}
@@ -2,23 +2,50 @@
#include "Interface/Context/Context.h"
#include <FEXCore/Utils/LogManager.h>
#include <map>
namespace FEXCore {
class BlockCache {
class LookupCache {
public:
struct BlockCacheEntry {
struct LookupCacheEntry {
uintptr_t HostCode;
uintptr_t GuestCode;
};
BlockCache(FEXCore::Context::Context *CTX);
~BlockCache();
LookupCache(FEXCore::Context::Context *CTX);
~LookupCache();
using BlockCacheIter = uintptr_t;
using LookupCacheIter = uintptr_t;
uintptr_t End() { return 0; }
uintptr_t FindBlock(uint64_t Address) {
return FindCodePointerForAddress(Address);
auto HostCode = FindCodePointerForAddress(Address);
if (HostCode) {
return HostCode;
} else {
auto HostCode = BlockList.find(Address);
if (HostCode != BlockList.end()) {
CacheBlockMapping(Address, HostCode->second);
return HostCode->second;
} else {
return 0;
}
}
}
std::map<uint64_t, std::vector<uint64_t>> CodePages;
void AddBlockMapping(uint64_t Address, void *HostCode, uint64_t Start, uint64_t Length) {
auto InsertPoint = BlockList.emplace(Address, (uintptr_t)HostCode);
LogMan::Throw::A(InsertPoint.second == true, "Dupplicate block mapping added");
for (auto CurrentPage = Start >> 12, EndPage = (Start + Length) >> 12; CurrentPage <= EndPage; CurrentPage++) {
CodePages[CurrentPage].push_back(Address);
}
// no need to update L1 or L2, they will get updated on first lookup
}
void Erase(uint64_t Address) {
@@ -30,8 +57,11 @@ public:
it->second();
}
// Remove from BlockList
BlockList.erase(Address);
// Do L1
auto &L1Entry = reinterpret_cast<BlockCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
auto &L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
if (L1Entry.GuestCode == Address) {
L1Entry.GuestCode = L1Entry.HostCode = 0;
}
@@ -49,14 +79,32 @@ public:
}
// Page exists, just set the offset to zero
auto BlockPointers = reinterpret_cast<BlockCacheEntry*>(LocalPagePointer);
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
BlockPointers[PageOffset].GuestCode = 0;
BlockPointers[PageOffset].HostCode = 0;
}
uintptr_t AddBlockMapping(uint64_t Address, void *Ptr) {
void AddBlockLink(uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker) {
BlockLinks.insert({{GuestDestination, HostLink}, delinker});
}
void ClearCache();
void ClearL2Cache();
void HintUsedRange(uint64_t Address, uint64_t Size);
uintptr_t GetL1Pointer() { return L1Pointer; }
uintptr_t GetPagePointer() { return PagePointer; }
uintptr_t GetVirtualMemorySize() const { return VirtualMemSize; }
constexpr static size_t L1_ENTRIES = 1 * 1024 * 1024; // Must be a power of 2
constexpr static size_t L1_ENTRIES_MASK = L1_ENTRIES - 1;
private:
void CacheBlockMapping(uint64_t Address, uintptr_t HostCode) {
// Do L1
auto &L1Entry = reinterpret_cast<BlockCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
auto &L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
if (L1Entry.GuestCode == Address) {
L1Entry.GuestCode = L1Entry.HostCode = 0;
}
@@ -74,40 +122,23 @@ public:
// Allocate one now if we can
uintptr_t NewPageBacking = AllocateBackingForPage();
if (!NewPageBacking) {
// Couldn't allocate, return so the frontend can recover from this
return 0;
// Couldn't allocate, clear L2 and retry
ClearL2Cache();
CacheBlockMapping(Address, HostCode);
return;
}
Pointers[Address] = NewPageBacking;
LocalPagePointer = NewPageBacking;
}
// Add the new pointer to the page block
auto BlockPointers = reinterpret_cast<BlockCacheEntry*>(LocalPagePointer);
uintptr_t CastPtr = reinterpret_cast<uintptr_t>(Ptr);
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
// This silently replaces existing mappings
BlockPointers[PageOffset].GuestCode = FullAddress;
BlockPointers[PageOffset].HostCode = CastPtr;
return CastPtr;
BlockPointers[PageOffset].HostCode = HostCode;
}
void AddBlockLink(uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker) {
BlockLinks.insert({{GuestDestination, HostLink}, delinker});
}
void ClearCache();
void HintUsedRange(uint64_t Address, uint64_t Size);
uintptr_t GetL1Pointer() { return L1Pointer; }
uintptr_t GetPagePointer() { return PagePointer; }
uintptr_t GetVirtualMemorySize() const { return VirtualMemSize; }
constexpr static size_t L1_ENTRIES = 1 * 1024 * 1024; // Must be a power of 2
constexpr static size_t L1_ENTRIES_MASK = L1_ENTRIES - 1;
private:
uintptr_t AllocateBackingForPage() {
uintptr_t NewBase = AllocateOffset;
uintptr_t NewEnd = AllocateOffset + SIZE_PER_PAGE;
@@ -125,7 +156,7 @@ private:
uintptr_t FindCodePointerForAddress(uint64_t Address) {
// Do L1
auto &L1Entry = reinterpret_cast<BlockCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
auto &L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
if (L1Entry.GuestCode == Address) {
return L1Entry.HostCode;
}
@@ -143,7 +174,7 @@ private:
}
// Find there pointer for the address in the blocks
auto BlockPointers = reinterpret_cast<BlockCacheEntry*>(LocalPagePointer);
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
if (BlockPointers[PageOffset].GuestCode == FullAddress)
{
@@ -172,11 +203,13 @@ private:
}
};
std::map<BlockLinkTag, std::function<void()>> BlockLinks;
std::map<uint64_t, uint64_t> BlockList;
constexpr static size_t CODE_SIZE = 128 * 1024 * 1024;
constexpr static size_t SIZE_PER_PAGE = 4096 * sizeof(BlockCacheEntry);
constexpr static size_t L1_SIZE = L1_ENTRIES * sizeof(BlockCacheEntry);
constexpr static size_t SIZE_PER_PAGE = 4096 * sizeof(LookupCacheEntry);
constexpr static size_t L1_SIZE = L1_ENTRIES * sizeof(LookupCacheEntry);
size_t AllocateOffset {};
File diff suppressed because it is too large. Load diff
+8 -2
View File
@@ -88,7 +88,8 @@ public:
uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4;
// If we don't have a jump target to a new block then we have to leave
// Set the RIP to the next instruction and leave
_ExitFunction(_Constant(GPRSize * 8, NextRIP));
auto RelocatedNextRIP = _EntrypointOffset(NextRIP - Current_Header->Entry, GPRSize);
_ExitFunction(RelocatedNextRIP);
}
else if (it != JumpTargets.end()) {
_Jump(it->second.BlockEntry);
@@ -349,6 +350,8 @@ public:
void FST(OpcodeArgs);
void FST(OpcodeArgs);
template<bool Truncate>
void FIST(OpcodeArgs);
enum class OpResult {
@@ -381,6 +384,7 @@ public:
void X87TAN(OpcodeArgs);
void X87ATAN(OpcodeArgs);
void X87LDENV(OpcodeArgs);
void X87FLDCW(OpcodeArgs);
void X87FNSTENV(OpcodeArgs);
void X87FSTCW(OpcodeArgs);
void X87LDSW(OpcodeArgs);
@@ -471,14 +475,16 @@ public:
OrderedNode *GetPackedRFLAG(bool Lower8);
void SetMultiblock(bool _Multiblock) { Multiblock = _Multiblock; }
bool GetMultiblock() { return Multiblock; }
bool HandledLock = false;
private:
bool DecodeFailure{false};
FEXCore::IR::IROp_IRHeader *Current_Header{};
OrderedNode *Current_HeaderNode{};
OrderedNode *AppendSegmentOffset(OrderedNode *Value, uint32_t Flags, uint32_t DefaultPrefix = 0, bool Override = false);
OrderedNode *GetDynamicPC(FEXCore::X86Tables::DecodedOp const& Op, int64_t Offset = 0);
OrderedNode *LoadSource(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp const& Op, FEXCore::X86Tables::DecodedOperand const& Operand, uint32_t Flags, int8_t Align, bool LoadData = true, bool ForceLoad = false);
OrderedNode *LoadSource_WithOpSize(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp const& Op, FEXCore::X86Tables::DecodedOperand const& Operand, uint8_t OpSize, uint32_t Flags, int8_t Align, bool LoadData = true, bool ForceLoad = false);
void StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, FEXCore::X86Tables::DecodedOperand const& Operand, OrderedNode *const Src, uint8_t OpSize, int8_t Align);
+48 -2
View File
@@ -1,13 +1,23 @@
/*
$info$
tags: glue|x86-guest-code
desc: Guest-side assembly helpers used by the backends
$end_info$
*/
#include "Interface/Core/X86HelperGen.h"
#include <cstring>
#include <stdlib.h>
#include <vector>
#include <sys/mman.h>
namespace FEXCore {
constexpr size_t CODE_SIZE = 0x1000;
X86GeneratedCode::X86GeneratedCode() {
// Allocate a page for our emulated guest
CodePtr = malloc(0x1000);
CodePtr = AllocateGuestCodeSpace(CODE_SIZE);
SignalReturn = reinterpret_cast<uint64_t>(CodePtr);
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr) + 2;
@@ -21,7 +31,43 @@ X86GeneratedCode::X86GeneratedCode() {
}
X86GeneratedCode::~X86GeneratedCode() {
free(CodePtr);
munmap(CodePtr, CODE_SIZE);
}
void* X86GeneratedCode::AllocateGuestCodeSpace(size_t Size) {
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
if (Is64BitMode()) {
// 64bit mode can have its sigret handler anywhere
return mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
// First 64bit page
constexpr uintptr_t LOCATION_MAX = 0x1'0000'0000;
// 32bit mode
// We need to have the sigret handler in the lower 32bits of memory space
// Scan top down and try to allocate a location
for (size_t Location = 0xFFFF'E000; Location != 0x0; Location -= 0x1000) {
void *Ptr = mmap(reinterpret_cast<void*>(Location), Size, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (Ptr != MAP_FAILED &&
reinterpret_cast<uintptr_t>(Ptr) >= LOCATION_MAX) {
// Failed to map in the lower 32bits
// Try again
// Can happen in the case that host kernel ignores MAP_FIXED_NOREPLACE
munmap(Ptr, Size);
continue;
}
if (Ptr != MAP_FAILED) {
return Ptr;
}
}
// Can't do anything about this
// Here's hoping the application doesn't use signals
return MAP_FAILED;
}
}
+9
View File
@@ -1,4 +1,12 @@
/*
$info$
tags: glue|x86-guest-code
$end_info$
*/
#pragma once
#include <FEXCore/Config/Config.h>
#include <stdint.h>
namespace FEXCore {
@@ -12,5 +20,6 @@ public:
private:
void *CodePtr{};
void* AllocateGuestCodeSpace(size_t Size);
};
}
+7 -18
View File
@@ -1,3 +1,10 @@
/*
$info$
meta: frontend|x86-tables ~ Metadata that drives the frontend x86/64 decoding
tags: frontend|x86-tables
$end_info$
*/
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Core/Context.h>
@@ -100,24 +107,6 @@ void InitializeInfoTables(Context::OperatingMode Mode) {
InitializeEVEXTables();
#ifndef NDEBUG
auto CheckTable = [&UnknownOp](auto& FinalTable) {
for (size_t i = 0; i < FinalTable.size(); ++i) {
auto const &Op = FinalTable.at(i);
if (Op == UnknownOp) {
LogMan::Msg::A("Unknown Op: 0x%lx", i);
}
}
};
// CheckTable(BaseOps);
// CheckTable(SecondBaseOps);
// CheckTable(RepModOps);
// CheckTable(RepNEModOps);
// CheckTable(OpSizeModOps);
// CheckTable(X87Ops);
X86InstDebugInfo::InstallDebugInfo();
LogMan::Msg::D("X86Tables had %ld total insts, and %ld labeled as understood", Total, NumInsts);
#endif
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
#include <FEXCore/Core/Context.h>
@@ -279,13 +285,13 @@ void InitializeBaseTables(Context::OperatingMode Mode) {
{0xEA, 1, X86InstInfo{"JMPF", TYPE_INST, FLAGS_NONE, 0, nullptr}},
};
GenerateTable(BaseOps, BaseOpTable, sizeof(BaseOpTable) / sizeof(BaseOpTable[0]));
GenerateTable(BaseOps, BaseOpTable, std::size(BaseOpTable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(BaseOps, BaseOpTable_64, sizeof(BaseOpTable_64) / sizeof(BaseOpTable_64[0]));
GenerateTable(BaseOps, BaseOpTable_64, std::size(BaseOpTable_64));
}
else {
GenerateTable(BaseOps, BaseOpTable_32, sizeof(BaseOpTable_32) / sizeof(BaseOpTable_32[0]));
GenerateTable(BaseOps, BaseOpTable_32, std::size(BaseOpTable_32));
}
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -38,6 +44,6 @@ void InitializeDDDTables() {
{0xB7, 1, X86InstInfo{"PMULHRW", TYPE_3DNOW_INST, FLAGS_MODRM, 0, nullptr}},
};
GenerateTable(DDDNowOps, DDDNowOpTable, sizeof(DDDNowOpTable) / sizeof(DDDNowOpTable[0]));
GenerateTable(DDDNowOps, DDDNowOpTable, std::size(DDDNowOpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -20,6 +26,6 @@ void InitializeEVEXTables() {
{0xE7, 1, X86InstInfo{"VMOVNTDQ", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
};
GenerateTable(EVEXTableOps, EVEXTable, sizeof(EVEXTable) / sizeof(EVEXTable[0]));
GenerateTable(EVEXTableOps, EVEXTable, std::size(EVEXTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -54,7 +60,7 @@ void InitializeH0F38Tables() {
{OPD(PF_38_66, 0x25), 1, X86InstInfo{"PMOVSXDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x28), 1, X86InstInfo{"PMULDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x29), 1, X86InstInfo{"PCMPEQQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x2A), 1, X86InstInfo{"MOVNTDQA", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x2A), 1, X86InstInfo{"MOVNTDQA", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(PF_38_66, 0x2B), 1, X86InstInfo{"PACKUSDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(PF_38_66, 0x30), 1, X86InstInfo{"PMOVZXBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
@@ -92,6 +98,6 @@ void InitializeH0F38Tables() {
};
#undef OPD
GenerateTable(H0F38TableOps, H0F38Table, sizeof(H0F38Table) / sizeof(H0F38Table[0]));
GenerateTable(H0F38TableOps, H0F38Table, std::size(H0F38Table));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -48,10 +54,10 @@ void InitializeH0F3ATables(Context::OperatingMode Mode) {
#undef OPD
GenerateTable(H0F3ATableOps, H0F3ATable, sizeof(H0F3ATable) / sizeof(H0F3ATable[0]));
GenerateTable(H0F3ATableOps, H0F3ATable, std::size(H0F3ATable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(H0F3ATableOps, H0F3ATable_64, sizeof(H0F3ATable_64) / sizeof(H0F3ATable_64[0]));
GenerateTable(H0F3ATableOps, H0F3ATable_64, std::size(H0F3ATable_64));
}
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -125,9 +131,12 @@ void InitializePrimaryGroupTables(Context::OperatingMode Mode) {
// GROUP 11
{OPD(TYPE_GROUP_11, OpToIndex(0xC6), 0), 1, X86InstInfo{"MOV", TYPE_INST, GenFlagsSameSize(SIZE_8BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SRC_SEXT, 1, nullptr}},
{OPD(TYPE_GROUP_11, OpToIndex(0xC6), 1), 6, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_11, OpToIndex(0xC6), 1), 5, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_11, OpToIndex(0xC6), 7), 1, X86InstInfo{"XABORT", TYPE_INST, FLAGS_MODRM, 1, nullptr}},
{OPD(TYPE_GROUP_11, OpToIndex(0xC7), 0), 1, X86InstInfo{"MOV", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SRC_SEXT | FLAGS_DISPLACE_SIZE_DIV_2, 4, nullptr}},
{OPD(TYPE_GROUP_11, OpToIndex(0xC7), 1), 6, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_11, OpToIndex(0xC7), 1), 5, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_11, OpToIndex(0xC7), 7), 1, X86InstInfo{"XBEGIN", TYPE_INST, FLAGS_MODRM | FLAGS_SRC_SEXT | FLAGS_SETS_RIP | FLAGS_DISPLACE_SIZE_DIV_2, 4, nullptr}},
};
const U16U8InfoStruct PrimaryGroupOpTable_64[] = {
@@ -149,12 +158,12 @@ void InitializePrimaryGroupTables(Context::OperatingMode Mode) {
#undef OPD
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable, sizeof(PrimaryGroupOpTable) / sizeof(PrimaryGroupOpTable[0]));
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable, std::size(PrimaryGroupOpTable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_64, sizeof(PrimaryGroupOpTable_64) / sizeof(PrimaryGroupOpTable_64[0]));
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_64, std::size(PrimaryGroupOpTable_64));
}
else {
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_32, sizeof(PrimaryGroupOpTable_32) / sizeof(PrimaryGroupOpTable_32[0]));
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_32, std::size(PrimaryGroupOpTable_32));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -477,7 +483,7 @@ void InitializeSecondaryGroupTables() {
};
#undef OPD
GenerateTable(SecondInstGroupOps, SecondaryExtensionOpTable, sizeof(SecondaryExtensionOpTable) / sizeof(SecondaryExtensionOpTable[0]));
GenerateTable(SecondInstGroupOps, SecondaryExtensionOpTable, std::size(SecondaryExtensionOpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -21,8 +27,8 @@ void InitializeSecondaryModRMTables() {
{((1 << 3) | 2), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{((1 << 3) | 3), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{((1 << 3) | 4), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{((1 << 3) | 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{((1 << 3) | 6), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{((1 << 3) | 5), 1, X86InstInfo{"XEND", TYPE_INST, FLAGS_NONE, 0, nullptr}},
{((1 << 3) | 6), 1, X86InstInfo{"XTEST", TYPE_INST, FLAGS_NONE, 0, nullptr}},
{((1 << 3) | 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
// REG /3
@@ -46,6 +52,6 @@ void InitializeSecondaryModRMTables() {
{((3 << 3) | 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
};
GenerateTable(SecondModRMTableOps, SecondaryModRMExtensionOpTable, sizeof(SecondaryModRMExtensionOpTable) / sizeof(SecondaryModRMExtensionOpTable[0]));
GenerateTable(SecondModRMTableOps, SecondaryModRMExtensionOpTable, std::size(SecondaryModRMExtensionOpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -571,18 +577,18 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
{0xFF, 1, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}},
};
GenerateTable(SecondBaseOps, TwoByteOpTable, sizeof(TwoByteOpTable) / sizeof(TwoByteOpTable[0]));
GenerateTable(SecondBaseOps, TwoByteOpTable, std::size(TwoByteOpTable));
if (Mode == Context::MODE_64BIT) {
GenerateTable(SecondBaseOps, TwoByteOpTable_64, sizeof(TwoByteOpTable_64) / sizeof(TwoByteOpTable_64[0]));
GenerateTable(SecondBaseOps, TwoByteOpTable_64, std::size(TwoByteOpTable_64));
}
else {
GenerateTable(SecondBaseOps, TwoByteOpTable_32, sizeof(TwoByteOpTable_32) / sizeof(TwoByteOpTable_32[0]));
GenerateTable(SecondBaseOps, TwoByteOpTable_32, std::size(TwoByteOpTable_32));
}
GenerateTableWithCopy(RepModOps, RepModOpTable, sizeof(RepModOpTable) / sizeof(RepModOpTable[0]), SecondBaseOps);
GenerateTableWithCopy(RepNEModOps, RepNEModOpTable, sizeof(RepNEModOpTable) / sizeof(RepNEModOpTable[0]), SecondBaseOps);
GenerateTableWithCopy(OpSizeModOps, OpSizeModOpTable, sizeof(OpSizeModOpTable) / sizeof(OpSizeModOpTable[0]), SecondBaseOps);
GenerateTableWithCopy(RepModOps, RepModOpTable, std::size(RepModOpTable), SecondBaseOps);
GenerateTableWithCopy(RepNEModOps, RepNEModOpTable, std::size(RepNEModOpTable), SecondBaseOps);
GenerateTableWithCopy(OpSizeModOps, OpSizeModOpTable, std::size(OpSizeModOpTable), SecondBaseOps);
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -500,7 +506,7 @@ void InitializeVEXTables() {
};
#undef OPD
GenerateTable(VEXTableOps, VEXTable, sizeof(VEXTable) / sizeof(VEXTable[0]));
GenerateTable(VEXTableGroupOps, VEXGroupTable, sizeof(VEXGroupTable) / sizeof(VEXGroupTable[0]));
GenerateTable(VEXTableOps, VEXTable, std::size(VEXTable));
GenerateTable(VEXTableGroupOps, VEXGroupTable, std::size(VEXGroupTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#pragma once
#include <FEXCore/Debug/X86Tables.h>
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -254,6 +260,6 @@ void InitializeX87Tables() {
#undef OPD
#undef OPDReg
GenerateX87Table(X87Ops, X87OpTable, sizeof(X87OpTable) / sizeof(X87OpTable[0]));
GenerateX87Table(X87Ops, X87OpTable, std::size(X87OpTable));
}
}
@@ -1,3 +1,9 @@
/*
$info$
tags: frontend|x86-tables
$end_info$
*/
#include "Interface/Core/X86Tables/X86Tables.h"
namespace FEXCore::X86Tables {
@@ -119,7 +125,7 @@ void InitializeXOPTables() {
};
#undef OPD
GenerateTable(XOPTableOps, XOPTable, sizeof(XOPTable) / sizeof(XOPTable[0]));
GenerateTable(XOPTableGroupOps, XOPGroupTable, sizeof(XOPGroupTable) / sizeof(XOPGroupTable[0]));
GenerateTable(XOPTableOps, XOPTable, std::size(XOPTable));
GenerateTable(XOPTableGroupOps, XOPGroupTable, std::size(XOPGroupTable));
}
}
+25 -14
View File
@@ -1,3 +1,10 @@
/*
$info$
meta: glue|thunks ~ FEXCore side of thunks: Registration, Lookup
tags: glue|thunks
$end_info$
*/
#include <FEXCore/Utils/LogManager.h>
#include "Thunks.h"
@@ -7,6 +14,7 @@
#include <string>
#include <map>
#include <array>
#include <Interface/Context/Context.h>
#include "Interface/Core/InternalThreadState.h"
#include "FEXCore/Core/X86Enums.h"
@@ -22,23 +30,26 @@ static thread_local FEXCore::Core::InternalThreadState *Thread;
namespace FEXCore {
struct ExportEntry { const char* Name; ThunkedFunction* Fn; };
struct ExportEntry { uint8_t *sha256; ThunkedFunction* Fn; };
class ThunkHandler_impl final: public ThunkHandler {
std::shared_mutex ThunksMutex;
std::map<std::string, ThunkedFunction*> Thunks = {
{ "fex:loadlib", &LoadLib}
std::map<IR::SHA256Sum, ThunkedFunction*> Thunks = {
{
// sha256(fex:loadlib)
{ 0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80},
&LoadLib
}
};
/*
Set arg0/1 to arg regs, use CTX::HandleCallback to handle the callback
*/
static void CallCallback(void *callback, void *arg0, void* arg1) {
Thread->State.State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0;
Thread->State.State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1;
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0;
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1;
Thread->CTX->HandleCallback((uintptr_t)callback);
}
@@ -52,10 +63,10 @@ namespace FEXCore {
auto Name = Args->Name;
auto CallbackThunks = Args->CallbackThunks;
auto SOName = CTX->Config.ThunkLibsPath + "/" + (const char*)Name + "-host.so";
auto SOName = CTX->Config.ThunkHostLibsPath() + "/" + (const char*)Name + "-host.so";
LogMan::Msg::D("Load lib: %s -> %s", Name, SOName.c_str());
auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW);
if (!Handle) {
@@ -73,7 +84,7 @@ namespace FEXCore {
LogMan::Msg::E("Load lib: failed to find export %s", InitSym.c_str());
return;
}
auto Exports = InitFN((void*)&CallCallback, CallbackThunks);
auto That = reinterpret_cast<ThunkHandler_impl*>(CTX->ThunkHandler.get());
@@ -82,8 +93,8 @@ namespace FEXCore {
std::unique_lock lk(That->ThunksMutex);
int i;
for (i = 0; Exports[i].Name; i++) {
That->Thunks[Exports[i].Name] = Exports[i].Fn;
for (i = 0; Exports[i].sha256; i++) {
That->Thunks[*reinterpret_cast<IR::SHA256Sum*>(Exports[i].sha256)] = Exports[i].Fn;
}
LogMan::Msg::D("Loaded %d syms", i);
@@ -92,11 +103,11 @@ namespace FEXCore {
public:
ThunkedFunction* LookupThunk(const char *Name) {
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) {
std::shared_lock lk(ThunksMutex);
auto it = Thunks.find(Name);
auto it = Thunks.find(sha256);
if (it != Thunks.end()) {
return it->second;
+8 -1
View File
@@ -1,4 +1,11 @@
/*
$info$
tags: glue|thunks
$end_info$
*/
#pragma once
#include <FEXCore/IR/IR.h>
namespace FEXCore::Core {
struct InternalThreadState;
@@ -9,7 +16,7 @@ namespace FEXCore {
class ThunkHandler {
public:
virtual ThunkedFunction* LookupThunk(const char *name) = 0;
virtual ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) = 0;
virtual void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) = 0;
virtual ~ThunkHandler() { }
+52 -12
View File
@@ -28,7 +28,9 @@
"static constexpr FEXCore::IR::RegisterClassType FPRFixedClass {3}",
"static constexpr FEXCore::IR::RegisterClassType GPRPairClass {4}",
"static constexpr FEXCore::IR::RegisterClassType ComplexClass {5}",
"static constexpr FEXCore::IR::RegisterClassType InvalidClass {~0U}",
"static constexpr FEXCore::IR::RegisterClassType InvalidClass {7}",
"",
"static constexpr uint8_t InvalidReg {31}",
"",
"static const FEXCore::IR::TypeDefinition i8 {TypeDefinition::Create(1, 0)}",
"static const FEXCore::IR::TypeDefinition i16 {TypeDefinition::Create(2, 0)}",
@@ -78,8 +80,7 @@
],
"Args": [
"uint64_t", "Entry",
"uint32_t", "BlockCount",
"bool", "ShouldInterpret"
"uint32_t", "BlockCount"
]
},
"CodeBlock": {
@@ -129,18 +130,16 @@
"DestClass": "GPR",
"DestSize": "8",
"Args": [
"__uint128_t", "CodeOriginal",
"uint64_t", "CodePtr",
"uint64_t", "CodeOriginalLow",
"uint64_t", "CodeOriginalHigh",
"int64_t", "Offset",
"uint8_t", "CodeLength"
]
},
"RemoveCodeEntry": {
"HasSideEffects": true,
"OpClass": "Misc",
"Args": [
"uint64_t", "RIP"
]
"OpClass": "Misc"
},
"GuestCallDirect": {
@@ -281,6 +280,32 @@
]
},
"EntrypointOffset": {
"Desc": ["Returns the <entrypoint> + Offset address"],
"OpClass": "ALU",
"HasDest": true,
"DestClass": "GPR",
"DestSize": "RegisterSize",
"Args": [
"int64_t", "Offset"
],
"HelperArgs": [
"uint8_t", "RegisterSize"
]
},
"InlineEntrypointOffset": {
"Desc": ["Returns the <entrypoint> + Offset address"],
"OpClass": "ALU",
"DestSize": "RegisterSize",
"Args": [
"int64_t", "Offset"
],
"HelperArgs": [
"uint8_t", "RegisterSize"
]
},
"Constant": {
"Desc": ["Generates a 64bit constant inside of a GPR",
"Unsupported to create a constant in FPR"
@@ -641,8 +666,7 @@
"ArgPtr"
],
"Args":[
"const char*", "ThunkName",
"uintptr_t", "ThunkFnPtr"
"SHA256Sum", "ThunkNameHash"
]
},
@@ -1114,7 +1138,11 @@
"DestClass": "GPRPair",
"FixedDestSize": "8",
"NumElements": "2",
"SSAArgs": "1"
"SSAArgs": "2",
"SSANames": [
"Function",
"Leaf"
]
},
"Bfi": {
@@ -3307,6 +3335,15 @@
]
},
"F80LoadFCW": {
"OpClass": "Vector",
"HasSideEffects": true,
"SSAArgs": "1",
"SSANames": [
"X80FCW"
]
},
"F80Add": {
"OpClass": "Vector",
"HasDest": true,
@@ -3436,6 +3473,9 @@
"SSANames": [
"X80Src"
],
"Args": [
"bool", "Truncate"
],
"HelperArgs": [
"uint8_t", "Size"
]
@@ -1,8 +1,17 @@
/*
$info$
meta: ir|dumper ~ IR -> Text
tags: ir|dumper
$end_info$
*/
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/LogManager.h>
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include <iomanip>
namespace FEXCore::IR {
#define IROP_GETNAME_IMPL
#define IROP_GETRAARGS_IMPL
@@ -12,16 +21,23 @@ namespace FEXCore::IR {
#include <FEXCore/IR/IRDefines.inc>
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, uint64_t Arg) {
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, const SHA256Sum &Arg) {
*out << "sha256:";
for(auto byte: Arg.data)
*out << std::hex << std::setfill('0') << std::setw(2) << (unsigned int)byte;
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, uint64_t Arg) {
*out << "#0x" << std::hex << Arg;
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, const char* Arg) {
[[maybe_unused]]
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, const char* Arg) {
*out << Arg;
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, CondClassType Arg) {
std::array<std::string, 14> CondNames = {
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, CondClassType Arg) {
std::array<std::string, 22> CondNames = {
"EQ",
"NEQ",
"UGE",
@@ -36,12 +52,20 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
"SLT",
"SGT",
"SLE",
"Invalid Cond",
"Invalid Cond",
"FLU",
"FGE",
"FLEU",
"FGT",
"FU",
"FNU"
};
*out << CondNames[Arg];
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, MemOffsetType Arg) {
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, MemOffsetType Arg) {
std::array<std::string, 3> Names = {
"SXTX",
"UXTW",
@@ -51,7 +75,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
*out << Names[Arg];
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, RegisterClassType Arg) {
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, RegisterClassType Arg) {
if (Arg == GPRClass.Val)
*out << "GPR";
else if (Arg == GPRFixedClass.Val)
@@ -66,26 +90,33 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
*out << "Unknown Registerclass " << Arg;
}
static void PrintArg(std::stringstream *out, IRListView<false> const* IR, OrderedNodeWrapper Arg, IR::RegisterAllocationPass *RAPass) {
static void PrintArg(std::stringstream *out, IRListView const* IR, OrderedNodeWrapper Arg, IR::RegisterAllocationData *RAData) {
auto [CodeNode, IROp] = IR->at(Arg)();
*out << "%ssa" << std::to_string(Arg.ID());
if (RAPass) {
uint64_t RegClass = RAPass->GetNodeRegister(Arg.ID());
FEXCore::IR::RegisterClassType Class {uint32_t(RegClass >> 32)};
uint32_t Reg = RegClass;
switch (Class) {
case FEXCore::IR::GPRClass.Val: *out << "(GPR"; break;
case FEXCore::IR::GPRFixedClass.Val: *out << "(GPRFixed"; break;
case FEXCore::IR::FPRClass.Val: *out << "(FPR"; break;
case FEXCore::IR::FPRFixedClass.Val: *out << "(FPRFixed"; break;
case FEXCore::IR::GPRPairClass.Val: *out << "(GPRPair"; break;
case FEXCore::IR::ComplexClass.Val: *out << "(Complex"; break;
case FEXCore::IR::InvalidClass.Val: *out << "(Invalid"; break;
default: *out << "(Unknown"; break;
}
if (Arg.ID() == 0) {
*out << "%Invalid";
} else {
*out << "%ssa" << std::to_string(Arg.ID());
if (RAData) {
auto PhyReg = RAData->GetNodeRegister(Arg.ID());
*out << std::dec << Reg << ")";
switch (PhyReg.Class) {
case FEXCore::IR::GPRClass.Val: *out << "(GPR"; break;
case FEXCore::IR::GPRFixedClass.Val: *out << "(GPRFixed"; break;
case FEXCore::IR::FPRClass.Val: *out << "(FPR"; break;
case FEXCore::IR::FPRFixedClass.Val: *out << "(FPRFixed"; break;
case FEXCore::IR::GPRPairClass.Val: *out << "(GPRPair"; break;
case FEXCore::IR::ComplexClass.Val: *out << "(Complex"; break;
case FEXCore::IR::InvalidClass.Val: *out << "(Invalid"; break;
default: *out << "(Unknown"; break;
}
if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) {
*out << std::dec << (uint32_t)PhyReg.Reg << ")";
} else {
*out << ")";
}
}
}
if (IROp->HasDest) {
@@ -108,15 +139,7 @@ static void PrintArg(std::stringstream *out, IRListView<false> const* IR, Ordere
}
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, IR::TypeDefinition Arg) {
*out << "i" << std::dec << static_cast<uint32_t>(Arg.Bytes() * 8);
if (Arg.Elements()) {
*out << "v" << std::dec << static_cast<uint32_t>(Arg.Elements());
}
}
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, FEXCore::IR::FenceType Arg) {
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, FEXCore::IR::FenceType Arg) {
if (Arg == IR::Fence_Load) {
*out << "Loads";
}
@@ -131,7 +154,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
}
}
void Dump(std::stringstream *out, IRListView<false> const* IR, IR::RegisterAllocationPass *RAPass) {
void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData) {
auto HeaderOp = IR->GetHeader();
int8_t CurrentIndent = 0;
@@ -188,11 +211,9 @@ void Dump(std::stringstream *out, IRListView<false> const* IR, IR::RegisterAlloc
*out << "%ssa" << std::to_string(ID);
if (RAPass) {
uint64_t RegClass = RAPass->GetNodeRegister(ID);
FEXCore::IR::RegisterClassType Class {uint32_t(RegClass >> 32)};
uint32_t Reg = RegClass;
switch (Class) {
if (RAData) {
auto PhyReg = RAData->GetNodeRegister(ID);
switch (PhyReg.Class) {
case FEXCore::IR::GPRClass.Val: *out << "(GPR"; break;
case FEXCore::IR::GPRFixedClass.Val: *out << "(GPRFixed"; break;
case FEXCore::IR::FPRClass.Val: *out << "(FPR"; break;
@@ -202,8 +223,11 @@ void Dump(std::stringstream *out, IRListView<false> const* IR, IR::RegisterAlloc
case FEXCore::IR::InvalidClass.Val: *out << "(Invalid"; break;
default: *out << "(Unknown"; break;
}
*out << std::dec << Reg << ")";
if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) {
*out << std::dec << (uint32_t)PhyReg.Reg << ")";
} else {
*out << ")";
}
}
*out << " i" << std::dec << (ElementSize * 8);
@@ -245,9 +269,9 @@ void Dump(std::stringstream *out, IRListView<false> const* IR, IR::RegisterAlloc
auto [NodeNode, IROp] = NodeBegin();
auto PhiOp = IROp->C<IR::IROp_PhiValue>();
*out << "[ ";
PrintArg(out, IR, PhiOp->Value, RAPass);
PrintArg(out, IR, PhiOp->Value, RAData);
*out << ", ";
PrintArg(out, IR, PhiOp->Block, RAPass);
PrintArg(out, IR, PhiOp->Block, RAData);
*out << " ]";
if (PhiOp->Next.ID())
+7
View File
@@ -1,3 +1,10 @@
/*
$info$
meta: ir|emitter ~ C++ Functions to generate IR. See IR.json for spec.
tags: ir|emitter
$end_info$
*/
#include <FEXCore/IR/IREmitter.h>
namespace FEXCore::IR {
+660
View File
@@ -0,0 +1,660 @@
/*
$info$
meta: ir|parser ~ Text -> IR
tags: ir|parser
$end_info$
*/
#include <string>
#include <vector>
#include <istream>
#include <unordered_map>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/IR/IREmitter.h>
namespace FEXCore::IR {
namespace {
enum class DecodeFailure {
DECODE_OKAY,
DECODE_UNKNOWN_TYPE,
DECODE_INVALID,
DECODE_INVALIDCHAR,
DECODE_INVALIDRANGE,
DECODE_INVALIDREGISTERCLASS,
DECODE_UNKNOWN_SSA,
DECODE_INVALID_CONDFLAG,
DECODE_INVALID_MEMOFFSETTYPE,
DECODE_INVALID_FENCETYPE,
};
std::string ltrim(std::string String) {
size_t pos = std::string::npos;
if ((pos = String.find_first_not_of(" \t\n\r")) != std::string::npos) {
String.erase(0, pos);
}
return String;
}
std::string rtrim(std::string String) {
size_t pos = std::string::npos;
if ((pos = String.find_last_not_of(" \t\n\r")) != std::string::npos) {
String.erase(String.begin() + pos + 1, String.end());
}
return String;
}
std::string trim(std::string String) {
return rtrim(ltrim(String));
}
std::string DecodeErrorToString(DecodeFailure Failure) {
switch (Failure) {
case DecodeFailure::DECODE_OKAY: return "Okay";
case DecodeFailure::DECODE_UNKNOWN_TYPE: return "Unknown Type";
case DecodeFailure::DECODE_INVALID: return "Invalid";
case DecodeFailure::DECODE_INVALIDCHAR: return "Invalid starting char";
case DecodeFailure::DECODE_INVALIDRANGE: return "Invalid integer range";
case DecodeFailure::DECODE_INVALIDREGISTERCLASS: return "Invalid register class";
case DecodeFailure::DECODE_UNKNOWN_SSA: return "Unknown SSA value";
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";
};
}
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) {
return {DecodeFailure::DECODE_UNKNOWN_TYPE, {}};
}
template<>
std::pair<DecodeFailure, uint8_t> DecodeValue(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) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint8_t Result = strtoul(&Arg.at(1), nullptr, 0);
if (errno == ERANGE || Result > 1) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result != 0};
}
template<>
std::pair<DecodeFailure, uint16_t> DecodeValue(std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint16_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, uint32_t> DecodeValue(std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint32_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, uint64_t> DecodeValue(std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
uint64_t Result = strtoull(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, int64_t> DecodeValue(std::string &Arg) {
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
int64_t Result = (int64_t)strtoull(&Arg.at(1), nullptr, 0);
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, IR::SHA256Sum> DecodeValue(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) != ':')
return {DecodeFailure::DECODE_INVALIDCHAR, Result};
auto GetDigit = [](const std::string &Arg, int pos, uint8_t *val) {
auto chr = Arg.at(pos);
if (chr >= '0' && chr <= '9') {
*val = chr - '0';
return true;
} else if (chr >= 'a' && chr <= 'f') {
*val = 10 + chr - 'a';
return true;
} else {
return false;
}
};
for (size_t i = 0; i < sizeof(Result.data); i++) {
uint8_t high, low;
if (!GetDigit(Arg, 7 + 2 * i + 0, &high) || !GetDigit(Arg, 7 + 2 * i + 1, &low)) {
return {DecodeFailure::DECODE_INVALIDRANGE, Result};
}
Result.data[i] = high * 16 + low;
}
return {DecodeFailure::DECODE_OKAY, Result};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::RegisterClassType> DecodeValue(std::string &Arg) {
if (Arg == "GPR") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::GPRClass};
}
else if (Arg == "FPR") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::FPRClass};
}
else if (Arg == "GPRPair") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::GPRPairClass};
}
else if (Arg == "Complex") {
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::ComplexClass};
}
return {DecodeFailure::DECODE_INVALIDREGISTERCLASS, FEXCore::IR::InvalidClass};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::TypeDefinition> DecodeValue(std::string &Arg) {
uint8_t Size{}, Elements{1};
int NumArgs = sscanf(Arg.c_str(), "i%hhdv%hhd", &Size, &Elements);
if (NumArgs != 1 && NumArgs != 2) {
return {DecodeFailure::DECODE_INVALID, {}};
}
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::TypeDefinition::Create(Size / 8, Elements)};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::CondClassType> DecodeValue(std::string &Arg) {
std::array<std::string, 22> CondNames = {
"EQ",
"NEQ",
"UGE",
"ULT",
"MI",
"PL",
"VS",
"VC",
"UGT",
"ULE",
"SGE",
"SLT",
"SGT",
"SLE",
"Invalid Cond",
"Invalid Cond",
"FLU",
"FGE",
"FLEU",
"FGT",
"FU",
"FNU"
};
for (size_t i = 0; i < CondNames.size(); ++i) {
if (CondNames[i] == Arg) {
return {DecodeFailure::DECODE_OKAY, CondClassType{static_cast<uint8_t>(i)}};
}
}
return {DecodeFailure::DECODE_INVALID_CONDFLAG, {}};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::MemOffsetType> DecodeValue(std::string &Arg) {
std::array<std::string, 3> Names = {
"SXTX",
"UXTW",
"SXTW",
};
for (size_t i = 0; i < Names.size(); ++i) {
if (Names[i] == Arg) {
return {DecodeFailure::DECODE_OKAY, MemOffsetType{static_cast<uint8_t>(i)}};
}
}
return {DecodeFailure::DECODE_INVALID_MEMOFFSETTYPE, {}};
}
template<>
std::pair<DecodeFailure, FEXCore::IR::FenceType> DecodeValue(std::string &Arg) {
std::array<std::string, 3> Names = {
"Loads",
"Stores",
"LoadStores",
};
for (size_t i = 0; i < Names.size(); ++i) {
if (Names[i] == Arg) {
return {DecodeFailure::DECODE_OKAY, FenceType{static_cast<uint8_t>(i)}};
}
}
return {DecodeFailure::DECODE_INVALID_FENCETYPE, {}};
}
template<>
std::pair<DecodeFailure, OrderedNode*> DecodeValue(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(" ");
if (ArgEnd != std::string::npos) {
SSAName = SSAName.substr(0, ArgEnd);
}
// Forward declarations may make this not succed
auto Op = SSANameMapper.find(SSAName);
if (Op == SSANameMapper.end()) {
return {DecodeFailure::DECODE_UNKNOWN_SSA, nullptr};
}
return {DecodeFailure::DECODE_OKAY, Op->second};
}
struct LineDefinition {
size_t LineNumber;
bool HasDefinition{};
std::string Definition{};
FEXCore::IR::TypeDefinition Size{};
std::string IROp{};
FEXCore::IR::IROps OpEnum;
bool HasArgs{};
std::vector<std::string> Args;
OrderedNode *Node{};
};
std::vector<std::string> Lines;
std::unordered_map<std::string, OrderedNode*> SSANameMapper;
std::vector<LineDefinition> Defs;
LineDefinition *CurrentDef{};
IRParser(std::istream *text) {
InitializeStaticTables();
std::string TmpLine;
while (!text->eof()) {
std::getline(*text, TmpLine);
if (text->eof()) {
break;
}
if (text->fail()) {
LogMan::Msg::E("Failed to getline on line: %ld", Lines.size());
return;
}
Lines.emplace_back(TmpLine);
}
ResetWorkingList();
Loaded = Parse();
}
bool Loaded = false;
#define IROP_PARSER_ALLOCATE_HELPERS
#include <FEXCore/IR/IRDefines.inc>
bool Parse() {
auto CheckPrintError = [&](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());
return false;
}
return true;
};
// String parse every line for our definitions
for (size_t i = 0; i < Lines.size(); ++i) {
std::string Line = Lines[i];
LineDefinition Def{};
CurrentDef = &Def;
Def.LineNumber = i;
Line = trim(Line);
// Skip empty lines
if (Line.empty()) {
continue;
}
if (Line[0] == ';') {
// This is a comment line
// Skip it
continue;
}
size_t CurrentPos{};
// 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) {
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");
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) {
size_t SSAEnd = 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;
Def.Size = DefinitionSize.second;
}
Def.Definition = trim(Line.substr(1, std::min(DefinitionEnd, SSAEnd) - 1));
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");
return false;
}
}
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) {
std::string Type = Def.Definition.substr(NameEnd + 1);
Type = trim(Type);
Def.Definition = trim(Def.Definition.substr(0, NameEnd));
auto DefinitionSize = DecodeValue<FEXCore::IR::TypeDefinition>(Type);
if (!CheckPrintError(Def, DefinitionSize.first)) return false;
Def.Size = DefinitionSize.second;
}
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");
return false;
}
}
// Let's get the IR op
size_t OpNameEnd = std::string::npos;
std::string RemainingLine = trim(Line.substr(CurrentPos));
CurrentPos = 0;
if ((OpNameEnd = RemainingLine.find_first_of(" \t\n\r\0", CurrentPos)) != std::string::npos) {
Def.IROp = RemainingLine.substr(CurrentPos, OpNameEnd);
Def.IROp = trim(Def.IROp);
Def.HasArgs = true;
CurrentPos = OpNameEnd;
}
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?");
return false;
}
Def.IROp = RemainingLine;
Def.HasArgs = false;
}
if (Def.HasArgs) {
RemainingLine = trim(RemainingLine.substr(CurrentPos));
CurrentPos = 0;
if (RemainingLine.empty()) {
// How did we get here?
Def.HasArgs = false;
}
else {
while (!RemainingLine.empty()) {
size_t ArgEnd = std::string::npos;
ArgEnd = RemainingLine.find_first_of(",");
std::string Arg = RemainingLine.substr(0, ArgEnd);
Arg = trim(Arg);
Def.Args.emplace_back(Arg);
RemainingLine.erase(0, ArgEnd+1); // +1 to ensure we go past the ','
if (ArgEnd == std::string::npos)
break;
}
}
}
Defs.emplace_back(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());
return false;
}
Def.OpEnum = Op->second;
}
// Emit the header op
IRPair<IROp_IRHeader> IRHeader;
{
auto &Def = Defs[0];
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());
return false;
}
auto Entry = DecodeValue<uint64_t>(Def.Args[0]);
auto CodeBlockCount = DecodeValue<uint64_t>(Def.Args[2]);
if (!CheckPrintError(Def, Entry.first)) return false;
if (!CheckPrintError(Def, CodeBlockCount.first)) return false;
IRHeader = _IRHeader(InvalidNode, Entry.second, CodeBlockCount.second);
}
SetWriteCursor(nullptr); // isolate the header from everything following
// Initialize SSANameMapper with Invalid value
SSANameMapper["%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;
Def.Node = CodeBlock.Node;
if (i == 1) {
// First code block is the entry block
// Link the header to the first block
IRHeader.first->Blocks = CodeBlock.Node->Wrapped(ListData.Begin());
}
CodeBlocks.emplace_back(CodeBlock.Node);
}
}
SetWriteCursor(nullptr); // isolate the block headers too
// Spin through all the definitions and add the ops to the basic blocks
OrderedNode *CurrentBlock{};
FEXCore::IR::IROp_CodeBlock *CurrentBlockOp{};
for(size_t i = 1; i < Defs.size(); ++i) {
auto &Def = Defs[i];
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!");
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!");
return false;
}
CurrentBlock = Def.Node;
CurrentBlockOp = CurrentBlock->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
break;
}
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!");
return false;
}
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
if (!CheckPrintError(Def, Adjust.first)) return false;
Def.Node = _BeginBlock(Adjust.second);
CurrentBlockOp->Begin = Def.Node->Wrapped(ListData.Begin());
break;
}
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!");
return false;
}
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
if (!CheckPrintError(Def, Adjust.first)) return false;
Def.Node = _EndBlock(Adjust.second);
CurrentBlockOp->Last = Def.Node->Wrapped(ListData.Begin());
CurrentBlock = nullptr;
CurrentBlockOp = nullptr;
break;
}
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");
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());
return false;
break;
}
}
if (Def.HasDefinition) {
auto IROp = Def.Node->Op(Data.Begin());
if (Def.Size.Elements()) {
IROp->Size = Def.Size.Bytes() * Def.Size.Elements();
IROp->ElementSize = Def.Size.Bytes();
}
else {
IROp->Size = Def.Size.Bytes();
IROp->ElementSize = 0;
}
SSANameMapper[Def.Definition] = Def.Node;
}
}
return true;
}
void InitializeStaticTables() {
if (NameToOpMap.size() == 0) {
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;
}
}
}
};
} // anon namespace
IREmitter* Parse(std::istream *in) {
auto parser = new IRParser(in);
if (parser->Loaded) {
return parser;
} else {
delete parser;
return nullptr;
}
}
}
+11 -5
View File
@@ -1,19 +1,25 @@
/*
$info$
meta: ir|opts ~ IR to IR Optimization
tags: ir|opts
desc: Defines which passes are run, and runs them
$end_info$
*/
#include "Interface/IR/PassManager.h"
#include "Interface/IR/Passes.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include "Interface/IR/PassManager.h"
#include <FEXCore/Config/Config.h>
namespace FEXCore::IR {
void PassManager::AddDefaultPasses(bool InlineConstants, bool StaticRegisterAllocation) {
FEXCore::Config::Value<bool> DisablePasses{FEXCore::Config::CONFIG_DEBUG_DISABLE_OPTIMIZATION_PASSES, false};
FEX_CONFIG_OPT(DisablePasses, O0);
if (!DisablePasses()) {
InsertPass(CreateContextLoadStoreElimination());
InsertPass(CreateDeadFlagStoreElimination());
InsertPass(CreateDeadGPRStoreElimination());
InsertPass(CreateDeadFPRStoreElimination());
InsertPass(CreateDeadStoreElimination());
InsertPass(CreatePassDeadCodeElimination());
InsertPass(CreateConstProp(InlineConstants));
+6
View File
@@ -1,3 +1,9 @@
/*
$info$
tags: ir|opts
$end_info$
*/
#pragma once
#include <FEXCore/IR/IntrusiveIRList.h>
Loaded 100 of 354 files, more files were not shown because too many files have changed in this diff. Show more