Compare commits

..
100 Commits
Author SHA1 Message Date
Ryan Houdek ea20429351 Docs: Update for release FEX-2507.1 2025-07-11 11:37:44 -07:00
Alyssa Rosenzweig 91828efa7a JIT: fix divisor masking
oversight. should fix Steam.

Fixes: de4becc26 ("OpcodeDispatcher: mask certain divisors")
Closes: #4652
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-11 11:34:45 -07:00
Billy Laws cce605d5e0 PoolBufferWithTimedRetirement: Unclaim in dtor
Buffers are tied to the lifetime of their owned flag, and as that
is a member of PoolBufferWithTimedRetirement we must always unclaim here.

Avoids the need to manually remember this quirk (which was forgot for the
temporary compilation buffer in JIT.cpp) at every use-site.
2025-07-11 11:34:07 -07:00
Ryan Houdek 3ba84ad06a Docs: Update for release FEX-2507 2025-07-07 23:49:56 -07:00
Ryan Houdek c6aae9e05a Merge pull request #4634 from Sonicadvance1/fix_horizon
EmulatedFiles: Emulate `current_clocksource`
2025-07-07 21:16:54 -07:00
Ryan Houdek 95b4618833 Merge pull request #4644 from ChanthMiao/fix/sigframe_mistake
Fix: wrong magic value in fpstate.
2025-07-06 17:07:31 -07:00
Changwei Miao 1fe17d55d9 Fix: wrong magic value in fpstate.
FEX should only set fpx_sw_bytes.magic1 with FP_XSTATE_MAGIC when
avx is enabled. Otherwise it may cause segfault in ntdll::save_context,
which requires access to extended xstate info if magic1 equals FP_XSTATE_MAGIC.

Signed-off-by: Changwei Miao <chanthmiao@outlook.com>
2025-07-06 18:44:37 +08:00
Ryan Houdek ead73371d9 EmulatedFiles: Emulate current_clocksource
WINE uses this to determine TSC frequency and because it doesn't say
`tsc` on ARM devices, it was ignoring TSC and instead using CPU maximum
frequency.

This was causing Horizon to think the TSC ran at whatever the max
frequency of a core was  (1.8Ghz to 2.6Ghz depending?) This was causing
all of Horizon Zero Dawn's physics to run at slower than real time
speeds because our 1Ghz (on Orion) TSC is significantly lower than the
max clock speeds of the cores.

This is still a bug in Wine that it is using the maximum CPU clock speed
in the case of current_clocksource not being TSC, but that's a battle
for a different time.
2025-07-05 21:10:53 -07:00
Ryan Houdek 6f089a4323 Merge pull request #4643 from tstellar/llvm-21
Fix build with LLVM >= 21
2025-07-05 16:51:38 -07:00
Ryan Houdek 640f024551 Merge pull request #4641 from Sonicadvance1/optimize_sincos
JIT: Optimize x87 FSINCOS
2025-07-05 16:26:37 -07:00
Tom Stellard 99920f89dd Fix build with LLVM >= 21 2025-07-05 16:50:34 +00:00
Ryan Houdek 8ea276267f InstcountCI: Update 2025-07-03 18:05:52 -07:00
Ryan Houdek 6ebbd91245 JIT: Optimize x87 FSINCOS
Turns out Bayonetta hammers SINCOS, our splitting the operation is
actually harming the performance of games that heavily use FSINCOS. We
instead can actually combine the operation which improves performance.
Not enough to get the game running full speed consistently on my Radxa,
but good numbers in my microbenchmark.

```
Test, Total Cycles, Total Runs, Cycles Average, Internal Loops, Average cycles per internal, per/second

64-bit:
Before:
FSIN, 2691031290, 50000, 53820.6, 1000, 53.8206, 18580.237319
FCOS, 2719397120, 50000, 54387.9, 1000, 54.3879, 18386.428239
FSINCOS, 5586917530, 50000, 111738, 1000, 111.738, 8949.478801

After:
FSIN, 2669959250, 50000, 53399.2, 1000, 53.3992, 18726.877573
FCOS, 2740942260, 50000, 54818.8, 1000, 54.8188, 18241.901965
FSINCOS, 3189472870, 50000, 63789.5, 1000, 63.7895, 15676.571659

80-bit:
Before:
FSIN, 24702939380, 50000, 494059, 1000, 494.059, 2024.050629
FCOS, 19127131020, 50000, 382543, 1000, 382.543, 2614.087808
FSINCOS, 40386785260, 50000, 807736, 1000, 807.736, 1238.028719

After:
FSIN, 24869980710, 50000, 497400, 1000, 497.4, 2010.455922
FCOS, 19131849590, 50000, 382637, 1000, 382.637, 2613.443084
FSINCOS, 38329985570, 50000, 766600, 1000, 766.6, 1304.461749

Improvement 64-bit: 1.75x
Improvement 80-bit: 1.05x
```

Only a minor improvement at 80-bit precision since cephes doesn't provide a combined sincos operation, but the f64 implementation is significantly improved, allowing 75% more operations per second.

Disabled in the simulator because we can't easily support pairs of
vector registers being returned.
2025-07-03 18:05:51 -07:00
Ryan Houdek fa0a54deb9 Merge pull request #4640 from alyssarosenzweig/bug/fix-hades
Fix Hades
2025-07-03 14:53:01 -07:00
Alyssa Rosenzweig 046043090f unittests: add move merging test
this hits a nasty case with post-RA merging. fails on main.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-03 17:22:56 -04:00
Alyssa Rosenzweig 61150a18cc RegisterAllocationPass: fix bookkeeping with merging
this fixes Hades.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-03 17:22:53 -04:00
Ryan Houdek 95ca20cfee Merge pull request #4636 from alyssarosenzweig/bug/ra-invariant
RegisterAllocationPass: assert an invariant in post-RA prop
2025-07-02 18:17:08 -07:00
Alyssa Rosenzweig 5a536d47fd RegisterAllocationPass: assert an invariant in post-RA prop
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-02 13:53:47 -04:00
Ryan Houdek afbc7da027 Merge pull request #4629 from alyssarosenzweig/opt/cpuid-basic
Optimize some constant cpuid/xgetbv cases
2025-07-02 10:25:01 -07:00
Alyssa Rosenzweig c093c08c40 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-02 13:04:49 -04:00
Alyssa Rosenzweig 360d8c629e RegisterAllocationPass: optimize cpuid
for constant function where we don't have a leaf. this isn't fully general but
we can't do better without a more general post-RA optimizer. i'm not inclined to
do that unless/until we get hot blocks demonstrating its value (that we can
compare against the JIT time hit of the heavier-duty optimizer.)

however this special case we can (and should) optimize for now.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-02 13:04:49 -04:00
Alyssa Rosenzweig abb41d39e4 RegisterAllocationPass: optimize xgetbv
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-02 13:04:49 -04:00
Alyssa Rosenzweig d4eb4ef594 IR: plumb CPUID into RA pass
for cpuid folding.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-02 13:04:49 -04:00
Alyssa Rosenzweig 02f45854e8 IR: include a fence in CPUID
easier for post-RA to chew thru.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-07-02 13:04:49 -04:00
Ryan Houdek 62de1004df Merge pull request #4635 from neobrain/fix_libfwd_wl_more
LibraryForwarding/wayland: Add new interface objects
2025-07-02 09:01:11 -07:00
Tony Wasserka 7f216ca02f LibraryForwarding/wayland: Add new interface objects 2025-07-02 16:51:03 +02:00
LC 492b0fdda8 Merge pull request #4632 from neobrain/feature_nix
Build: Add nix-based helpers to facilitate cross-compilation
2025-07-01 16:23:58 -04:00
LC bb072c0112 Merge pull request #4633 from Sonicadvance1/noexec_test
unittests: Adds unittest for no-exec testing
2025-07-01 16:20:44 -04:00
Ryan Houdek afabe7cb47 Merge pull request #4627 from alyssarosenzweig/opt/long-div-peephole-ready
Optimize long division
2025-06-30 13:59:48 -07:00
Ryan Houdek 38e0fc2434 unittests: Adds unittest for no-exec testing
In preparation for #4474
2025-06-30 13:19:33 -07:00
Alyssa Rosenzweig 16a70eafc6 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-30 15:59:27 -04:00
Alyssa Rosenzweig de4becc26e OpcodeDispatcher: mask certain divisors
needed for fusing.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-30 15:58:33 -04:00
Alyssa Rosenzweig af23f4325f OpcodeDispatcher: reorder xor-with-self sequence
this lets us peephole fuse things even when there are flags calculated in the
way

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-30 15:58:33 -04:00
Ryan Houdek 94af96df8f Merge pull request #4631 from neobrain/fix_libfwd_wl_cutter
LibraryForwarding: Fix various Wayland issues
2025-06-30 11:38:13 -07:00
Ryan Houdek e685ab818e Merge pull request #4619 from neobrain/refactor_drop_config_h_in
Remove code generation build step for install prefix
2025-06-30 11:35:26 -07:00
Ryan Houdek 5f2a72b65b Merge pull request #4624 from Sonicadvance1/static_analysis_fixes
Some static code analysis fixes
2025-06-30 11:28:06 -07:00
Tony Wasserka c1842a6167 Build: Add nix-based helpers to manage toolchains for ARM64EC/WOW64
The cmake_configure_woa*.sh scripts will automatically install any required
cross-toolchains required to enable either ARM64EC or WOW64 builds of FEX,
and it will initialize the current build folder appropriately.

For advanced uses, a shell can be opened by running `nix-shell WineOnArm/shell.nix`,
which will make the toolchain available via environment variables. This also
generates a meson crossfile for building VKD3D or vkd3d-proton.
2025-06-30 16:50:13 +02:00
Tony Wasserka 3f3907b5d1 Build: Add nix-based helpers to manage toolchains for FEXLinuxTests
The cmake_enable_flt.sh script will automatically install the required
cross-toolchains required to build FEXLinuxTests, and it will reconfigure
the current build folder appropriately.

For advanced uses, a shell can be opened by running `nix-shell FEXLinuxTests/shell.nix`,
which will make the toolchain available via environment variables.
2025-06-30 16:50:13 +02:00
Tony Wasserka 1899465390 Build: Add nix-based helpers to manage toolchains for library forwarding
The cmake_enable_libfwd.sh script will automatically install any required
cross- toolchains and development headers required to enable library
forwarding, and it will reconfigure the current build folder appropriately.

For advanced uses, a shell can be opened by running `nix-shell LibraryForwarding/shell.nix`,
which will make the toolchain available via environment variables.
2025-06-30 16:50:13 +02:00
Tony Wasserka 18360d4ccb LibraryForwarding/wayland: Add more method signatures 2025-06-27 10:57:10 +02:00
Tony Wasserka a691c3cd99 LibraryForwarding/wayland: Fix mprotect call when crossing page boundaries 2025-06-27 10:57:10 +02:00
Tony Wasserka 70bc561bbf LibraryForwarding/wayland: Fix wl_proxy_marshal_array_constructor 2025-06-27 10:57:10 +02:00
Tony Wasserka b9222d8431 LibraryForwarding/unittests: Fix build with clang 20 2025-06-27 10:57:10 +02:00
Tony Wasserka a5fad89e57 Merge pull request #4621 from neobrain/feature_update_readme
Update Readme.md
2025-06-26 21:54:53 +02:00
Tony Wasserka a14360b89d Update Readme.md 2025-06-26 21:41:23 +02:00
Tony Wasserka c9aaedd217 CPack: Update package description 2025-06-26 21:41:23 +02:00
Alyssa Rosenzweig cda15ce9ea RegisterAllocationPass: optimize long divsion
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-24 16:10:29 -04:00
Alyssa Rosenzweig 3f3b6ad337 IR: merge regular/long divisions
this makes it a lot easier to turn a long division into a non-long division,
just by nulling out a source.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-24 16:10:29 -04:00
Alyssa Rosenzweig 62410c4381 InstCountCI: add another udiv case
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-24 16:10:22 -04:00
Ryan Houdek cf82b56dd8 CPUBackend: Remove unused variable
CID 482006
2025-06-19 16:51:30 -07:00
Ryan Houdek 4a74bea7ab InstcountCI: Don't use a global static initializer for CodeSizeValidation
Relies on fmt facet initialization order which isn't guaranteed to have
correct initialization order.

CID 482003
2025-06-19 16:51:26 -07:00
Ryan Houdek c6d8e60ef8 OpcodeDispatcher: Make sure to initialize ArithRef
CID 482002
2025-06-19 16:46:15 -07:00
Ryan Houdek 1212cd526a VDSOEmulation: Sanitize sysconf result
Unlikely to fail but make sure.

CID 482021
2025-06-19 16:43:53 -07:00
Ryan Houdek 79a8ed53b6 CPUBackend: Make sure to zero initialize variable
CID 482022
2025-06-19 16:41:24 -07:00
Ryan Houdek a6ce115d9c FEXLoader: Handle bad LogFile path
Just go silent but print a log about it.

CID 482031
CID 482023
2025-06-19 16:40:03 -07:00
Ryan Houdek 646a5a7f9e CodeEmitter/ASIMD: Removes redundant ternary selection
Redundant and unnecessary.

CID 482017
2025-06-19 16:31:08 -07:00
Ryan Houdek 7f71b6f1b2 Passes: Use move instead of copy semantics
To initialize in-place.

CID 482035
2025-06-19 16:22:39 -07:00
Ryan Houdek 16d5ca447f FEXCore: DebugData is never null now
This is a required data structure to exist.

CID 482036
2025-06-19 16:21:19 -07:00
Ryan Houdek 3d0c20a263 Merge pull request #4615 from neobrain/feature_logging_qol
Improve log message formatting
2025-06-19 12:50:44 -07:00
Ryan Houdek 1c38b8b046 Merge pull request #4614 from alyssarosenzweig/opt/easy-mov-elim
Merge moves that are immediately consumed
2025-06-19 12:26:07 -07:00
Alyssa Rosenzweig e1124480be InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig 5243f50ed1 RegisterAllocationPass: merge 32-bit mov + 64-bit and
mov wA, wB
  and xA, xA, ...

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig cde805147f RegisterAllocationPass: merge 32-bit moves
mov wA, wB
  op wA, wA, ..

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig 7e39eb3df2 RegisterAllocationPass: merge full size moves
mov xA, xB
  op xA, xA, ..

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig af366d4480 RegisterAllocationPass: skip inlineconstant in RA
similar reasoning as guestopcode.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig 33ef98aae7 RegisterAllocationPass: refactor push/pop merge
to make way for move merging.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig c16db2db4a RegisterAllocationPass: simplify an expression
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig 059d980c33 IR: give StoreRegister a precoloured destination
this will eliminate an annoying special case in post-RA opts.

No difference proven at 95.0% confidence

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Alyssa Rosenzweig 581381fd86 IR: make 0 the invalid physical register
so zero init works as expected

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-19 13:44:23 -04:00
Ryan Houdek 0072b289bb Merge pull request #4622 from neobrain/feature_fexconfig_logo
FEXConfig: Add icon
2025-06-18 01:46:30 -07:00
Tony Wasserka e2bd79087e FEXConfig: Add icon 2025-06-18 10:33:07 +02:00
Ryan Houdek 6cd78fc90d Merge pull request #4618 from neobrain/feature_infer_32bit_libfwd_paths
LibraryForwarding: Infer folders for 32-bit wrappers automatically
2025-06-17 17:55:38 -07:00
Ryan Houdek c346aca241 Merge pull request #4620 from neobrain/refactor_data_files
Move data files to Data/
2025-06-17 17:54:28 -07:00
Ryan Houdek bf83569f0b Merge pull request #4623 from neobrain/feature_tracy_0_12
External: Update Tracy submodule to version 0.12.1
2025-06-17 17:53:47 -07:00
Tony Wasserka 1502f04a8a External: Update Tracy submodule to version 0.12.1
Notably, this update adds flame graph functionality for aggregated data.
2025-06-17 17:51:52 +02:00
Tony Wasserka 7bd9d0ae23 Data: Move Dockerfile 2025-06-17 16:40:42 +02:00
Tony Wasserka cf57afdf26 Data: Move CI folder to Data/CI 2025-06-17 16:40:42 +02:00
Tony Wasserka 43e6aebc7a Data: Move CMake support scripts to Data/CMake 2025-06-17 16:40:42 +02:00
Tony Wasserka 22780993e1 Data: Move CPack files to Data/CMake/ 2025-06-17 16:40:42 +02:00
Tony Wasserka 578dcee9af Data: Move toolchain files to a central location 2025-06-17 16:40:42 +02:00
Tony Wasserka 61d77e3f9b LibraryForwarding: Infer folders for 32-bit wrappers automatically
There's no need to bother the user to select these paths manually.
Instead, just use the same folder names with _32 appended.

Fixes #4588.
2025-06-17 11:57:35 +02:00
Tony Wasserka 4ce0acba80 Remove now unused Config.h.in 2025-06-17 11:40:32 +02:00
Tony Wasserka d137212222 FEXGetConfig: Infer install prefix from executable path 2025-06-17 11:40:32 +02:00
Tony Wasserka 9ad4e3a6a0 FEXBash: Clean up and fix FEXInterpreter lookup
Previously, the first attempt to look up a FEXInterpreter would always
fail due to a missing path separator.

Additionally, fallback lookup now uses /proc/self/exe to find a path relative
to the FEXBash executable. The previous use of FindContainerPrefix does not
seem to be required anymore in current Steam versions.
2025-06-17 11:40:32 +02:00
Tony Wasserka 57627d4fcf LogManager: Drop unused STDOUT/STDERR log levels 2025-06-16 13:54:03 +02:00
Tony Wasserka 23b69271eb Use consistent log message formatting for all modules 2025-06-16 13:54:03 +02:00
LC 9d2f557666 Merge pull request #4616 from alyssarosenzweig/ici/32bit-div
InstructionCountCI: add 32-bit division cases
2025-06-13 13:18:55 -04:00
Alyssa Rosenzweig 4f9e352ff0 InstructionCountCI: add 32-bit division cases
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-13 13:05:35 -04:00
Tony Wasserka 3e85e60a30 LogManager: Use colors for logging when possible 2025-06-13 15:14:47 +02:00
Tony Wasserka febce21b21 FEXServer: Separate PID and TID with a pipe instead of a period
Since most software considers the former a word boundary but not the latter,
this allows the individual values to be copy-pasted more conveniently.
2025-06-13 14:54:22 +02:00
Tony Wasserka 755364e2df FEXServer: Clean up time display for logging
This is now relative to the time of the first message. Furthermore, display
precision is limited to milliseconds (which are actually zero-padded now!).
2025-06-13 14:54:13 +02:00
Tony Wasserka df63979773 LogManager: Avoid ^C being printed when quitting foreground FEXServer 2025-06-13 14:31:25 +02:00
Tony Wasserka 992d86bbc1 LogManager: Shorten debug level strings to a single letter 2025-06-13 14:31:25 +02:00
Ryan Houdek 534b338161 Merge pull request #4612 from alyssarosenzweig/ir/merge-divrem-2
IR: merge integer division & remainder
2025-06-12 14:29:02 -07:00
Alyssa Rosenzweig ef250f936c InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-12 16:45:49 -04:00
Alyssa Rosenzweig e57130e364 JIT: drop UDiv extensions
we already extend in the dispatcher.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-12 16:45:49 -04:00
Alyssa Rosenzweig eedcb35270 IR: merge ldiv/lrem handlers
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-12 16:45:49 -04:00
Alyssa Rosenzweig 1eb470083c IR: merge div/rem opcodes
it's simpler & faster to calculate both together, matching the x86 semantic.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2025-06-10 17:08:20 -04:00
LC 1f15a4e35b Merge pull request #4611 from Sonicadvance1/ubisoft_ptrace
Linux: Implement enough of ptrace to allow Ubisoft launcher
2025-06-10 11:35:49 -04:00
Ryan Houdek 7ed9bea16b Linux: Implement enough of ptrace to allow Ubisoft launcher
Wine does some minimal attaching, peeking, poking, and detaching to have
a different process inspect another's TEB region. Ubisoft's launcher
does this to check if a debugger is attached and rejects it in the case
that it is.

While this implementation isn't all-encompassing, it is good enough for
this family of games.
2025-06-09 16:32:42 -07:00
133 changed files with 7176 additions and 6916 deletions

No files matched your search

+1 -1
View File
@@ -78,7 +78,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 -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/toolchain_mingw.cmake -DMINGW_TRIPLE=$MINGW_TRIPLE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_TESTS=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/Data/CMake/toolchain_mingw.cmake -DMINGW_TRIPLE=$MINGW_TRIPLE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_TESTS=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
- name: Build
working-directory: ${{runner.workspace}}/build
+5 -9
View File
@@ -35,8 +35,8 @@ set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend to use fo
option(ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT "Enables glibc memory allocation hooking with fault for CI testing")
option(USE_PDB_DEBUGINFO "Builds debug info in PDB format" FALSE)
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
set (X86_DEV_ROOTFS "/" CACHE FILEPATH "Path to the sysroot used for cross-compiling for i686 and x86_64")
set (DATA_DIRECTORY "" CACHE PATH "Global data directory (override)")
if (NOT DATA_DIRECTORY)
@@ -97,7 +97,7 @@ endif()
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
@@ -436,10 +436,6 @@ endif()
add_compile_options(-Wall)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/Config.h.in
${CMAKE_BINARY_DIR}/generated/ConfigDefines.h)
include(CTest)
if (BUILD_TESTS)
message(STATUS "Unit tests are enabled")
@@ -622,12 +618,12 @@ set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.com>")
set (CPACK_PACKAGE_VERSION_MAJOR "${FEX_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${FEX_VERSION_MINOR}")
set (CPACK_PACKAGE_VERSION_PATCH "${FEX_VERSION_PATCH}")
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CPack/Description.txt")
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/Description.txt")
# Debian defines
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libstdc++6, libepoxy0, libsdl2-2.0-0, libegl1, libx11-6, squashfuse")
set (CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
"${CMAKE_CURRENT_SOURCE_DIR}/CPack/postinst;${CMAKE_CURRENT_SOURCE_DIR}/CPack/prerm;${CMAKE_CURRENT_SOURCE_DIR}/CPack/triggers")
"${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/postinst;${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/prerm;${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/triggers")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
# binfmt_misc conflicts with qemu-user-static
# We also only install binfmt_misc on aarch64 hosts
-3
View File
@@ -1,3 +0,0 @@
x86 and x86-64 Linux emulator
FEX is very much work in progress, so expect things to change.
+14 -52
View File
@@ -820,7 +820,6 @@ public:
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b00010, rd, rn);
@@ -856,7 +855,6 @@ public:
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b00110, rd, rn);
@@ -1195,7 +1193,6 @@ public:
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b00010, rd, rn);
@@ -1225,7 +1222,6 @@ public:
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b00110, rd, rn);
@@ -1322,9 +1318,7 @@ public:
void fcvtxn(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10110, rd.D(), rn.D());
}
@@ -1333,9 +1327,7 @@ public:
void fcvtxn2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit subregsize supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10110, rd.Q(), rn.Q());
}
@@ -1344,9 +1336,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11000, rd, rn);
}
@@ -1355,9 +1345,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11001, rd, rn);
}
@@ -1367,9 +1355,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11010, rd, rn);
}
@@ -1378,9 +1364,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11011, rd, rn);
}
@@ -1389,9 +1373,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11100, rd, rn);
}
@@ -1400,9 +1382,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11101, rd, rn);
}
@@ -1411,9 +1391,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11110, rd, rn);
}
@@ -1422,9 +1400,7 @@ public:
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
"supported");
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11111, rd, rn);
}
@@ -1553,7 +1529,6 @@ public:
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMDAcrossLanes<T>(Op, 0, ConvertedSize, 0b00011, rd, rn);
@@ -1597,7 +1572,6 @@ public:
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
ASIMDAcrossLanes<T>(Op, 1, ConvertedSize, 0b00011, rd, rn);
@@ -1630,10 +1604,7 @@ public:
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
"unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
@@ -1647,10 +1618,7 @@ public:
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
"unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
ARMEmitter::SubRegSize::i8Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
@@ -1664,10 +1632,7 @@ public:
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
"unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i32Bit :
ARMEmitter::SubRegSize::i32Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i32Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
@@ -1681,10 +1646,7 @@ public:
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
"unsupported");
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit :
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i32Bit :
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i32Bit :
ARMEmitter::SubRegSize::i32Bit;
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i32Bit;
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
File renamed without changes.
File renamed without changes.
File renamed without changes.
+3
View File
@@ -0,0 +1,3 @@
x86 and x86-64 Linux emulator
FEX allows you to run x86 applications on ARM64 Linux devices. It offers broad compatibility with both 32-bit and 64-bit binaries, and it can be used alongside Wine/Proton to play Windows games.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
# This is a reference AArch64 cross compile script
# Pass in to cmake when building:
# eg: cmake -DCMAKE_TOOLCHAIN_FILE=../CMakeToolchains/AArch64.cmake ..
# eg: cmake --toolchain ../Data/CMake/toolchain_aarch64.cmake ..
if (NOT DEFINED ENV{SYSROOT})
message(FATAL_ERROR "Need to have SYSROOT environment variable set")
endif()
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
+35
View File
@@ -0,0 +1,35 @@
{ pkgs ? import <nixpkgs> { } }:
let
pkgsCross32 = pkgs.pkgsCross.gnu32;
pkgsCross64 = pkgs.pkgsCross.gnu64;
gcc32 = pkgs.writeText "toolchain_nix_gcc_x86_32.txt" ''
set(CMAKE_SYSTEM_PROCESSOR i686)
set(CMAKE_C_COMPILER ${pkgsCross32.buildPackages.gcc}/bin/i686-unknown-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${pkgsCross32.buildPackages.gcc}/bin/i686-unknown-linux-gnu-g++)
'';
gcc64 = pkgs.writeText "toolchain_nix_gcc_x86_64.txt" ''
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER ${pkgsCross64.buildPackages.gcc}/bin/x86_64-unknown-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${pkgsCross64.buildPackages.gcc}/bin/x86_64-unknown-linux-gnu-g++)
'';
in
pkgs.mkShell {
buildInputs = [
pkgsCross64.buildPackages.clang
pkgsCross32.buildPackages.clang
];
shellHook = ''
if [[ $- == *i* ]]; then
echo "toolchain32: ${gcc32}"
echo "toolchain64: ${gcc64}"
echo ""
echo "Use \$FEX_CMAKE_TOOLCHAINS to configure CMake."
fi
'';
FEX_CMAKE_TOOLCHAINS = "-DX86_32_TOOLCHAIN_FILE=${gcc32} -DX86_64_TOOLCHAIN_FILE=${gcc64}";
}
+83
View File
@@ -0,0 +1,83 @@
{ pkgs ? import <nixpkgs> { } }:
let
pkgsCross32 = pkgs.pkgsCross.gnu32;
pkgsCross64 = pkgs.pkgsCross.gnu64;
devRootFS = pkgs.buildEnv {
name = "fex-dev-rootfs";
paths = [
pkgsCross64.stdenv.cc.libc_dev
pkgsCross32.stdenv.cc.libc_dev
pkgsCross64.stdenv.cc.cc
pkgsCross32.stdenv.cc.cc
pkgs.alsa-lib.dev
pkgs.libdrm.dev
pkgs.libGL.dev
pkgs.wayland.dev
pkgs.xorg.libX11.dev
pkgs.xorg.libxcb.dev
pkgs.xorg.libXrandr.dev
pkgs.xorg.libXrender.dev
pkgs.xorg.xorgproto
];
ignoreCollisions = true;
pathsToLink = [
"/include"
"/lib"
];
postBuild = ''
mkdir -p $out/usr
ln -s $out/include $out/usr/
'';
};
toolchain32 = pkgs.writeText "toolchain_nix_x86_32.txt" ''
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_SYSTEM_PROCESSOR i686)
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_C_COMPILER ${pkgsCross32.buildPackages.clang}/bin/i686-unknown-linux-gnu-clang)
set(CMAKE_CXX_COMPILER ${pkgsCross32.buildPackages.clang}/bin/i686-unknown-linux-gnu-clang++)
set(CLANG_FLAGS "-nodefaultlibs -nostartfiles -lstdc++ -target i686-linux-gnu -msse2 -mfpmath=sse --sysroot=${devRootFS} -iwithsysroot/include")
set(CMAKE_C_FLAGS "''${CMAKE_C_FLAGS} ''${CLANG_FLAGS}")
set(CMAKE_CXX_FLAGS "''${CMAKE_CXX_FLAGS} ''${CLANG_FLAGS}")
'';
toolchain64 = pkgs.writeText "toolchain_nix_x86_64.txt" ''
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_C_COMPILER ${pkgsCross64.buildPackages.clang}/bin/x86_64-unknown-linux-gnu-clang)
set(CMAKE_CXX_COMPILER ${pkgsCross64.buildPackages.clang}/bin/x86_64-unknown-linux-gnu-clang++)
set(CLANG_FLAGS "-nodefaultlibs -nostartfiles -lstdc++ -target x86_64-linux-gnu --sysroot=${devRootFS} -iwithsysroot/usr/include")
set(CMAKE_C_FLAGS "''${CMAKE_C_FLAGS} ''${CLANG_FLAGS}")
set(CMAKE_CXX_FLAGS "''${CMAKE_CXX_FLAGS} ''${CLANG_FLAGS}")
'';
in
pkgs.mkShell {
buildInputs = [
pkgsCross64.buildPackages.clang
pkgsCross32.buildPackages.clang
];
shellHook = ''
if [[ $- == *i* ]]; then
echo "Set up dev RootFS at ${devRootFS}"
echo "toolchain32: ${toolchain32}"
echo "toolchain64: ${toolchain64}"
echo ""
echo "Use \$FEX_CMAKE_TOOLCHAINS to configure CMake."
fi
'';
FEX_CMAKE_TOOLCHAINS = "-DX86_32_TOOLCHAIN_FILE=${toolchain32} -DX86_64_TOOLCHAIN_FILE=${toolchain64} -DX86_DEV_ROOTFS=${devRootFS}";
ROOTFS = "${devRootFS}";
}
+52
View File
@@ -0,0 +1,52 @@
{ pkgs ? import <nixpkgs> { } }:
let
toolchain = pkgs.fetchzip {
url = "https://github.com/bylaws/llvm-mingw/releases/download/20250305/llvm-mingw-20250305-ucrt-ubuntu-20.04-aarch64.tar.xz";
sha256 = "sha256-cA03/ab9O61eO9+S2JzIXD4V0HzTXK5/AYyxW2d73Po=";
};
cmakeToolchainFile = pkgs.substitute {
# Use absolute paths that are discoverable outside of the nix shell
src = ../../CMake/toolchain_mingw.cmake;
substitutions = ["--replace-fail" "\${MINGW_TRIPLE}-" "${toolchain}/bin/\${MINGW_TRIPLE}-"];
};
mesonCrossFile = pkgs.writeText "crossfile_llvm_mingw.txt" ''
[binaries]
ar = '${toolchain}/bin/arm64ec-w64-mingw32-ar'
c = '${toolchain}/bin/arm64ec-w64-mingw32-gcc'
cpp = '${toolchain}/bin/arm64ec-w64-mingw32-g++'
ld = '${toolchain}/bin/arm64ec-w64-mingw32-ld'
windres = '${toolchain}/bin/arm64ec-w64-mingw32-windres'
strip = '${toolchain}/bin/strip'
widl = '${toolchain}/bin/arm64ec-w64-mingw32-widl'
pkgconfig = 'aarch64-linux-gnu-pkg-config'
[host_machine]
system = 'windows'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'
'';
in
pkgs.mkShell {
buildInputs = [
toolchain
];
shellHook = ''
if [[ $- == *i* ]]; then
echo "llvm-mingw set up at ${toolchain}."
echo ""
echo "To configure DXVK/vkd3d-proton: meson setup \$FEX_MESON_CROSSFILE"
echo ""
echo "To configure 32-bit FEX build: cmake \$FEX_CMAKE_TOOLCHAIN_WOW64"
echo "To configure 64-bit FEX build: cmake \$FEX_CMAKE_TOOLCHAIN_ARM64EC"
fi
'';
# E.g. cmake $FEX_CMAKE_TOOLCHAIN_ARM64EC -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LTO=False -DBUILD_TESTS=False
FEX_CMAKE_TOOLCHAIN_ARM64EC = "--toolchain ${cmakeToolchainFile} -DMINGW_TRIPLE=arm64ec-w64-mingw32 -DCMAKE_INSTALL_LIBDIR=/usr/lib/wine/aarch64-windows";
FEX_CMAKE_TOOLCHAIN_WOW64 = "--toolchain ${cmakeToolchainFile} -DMINGW_TRIPLE=aarch64-w64-mingw32 -DCMAKE_INSTALL_LIBDIR=/usr/lib/wine/aarch64-windows";
FEX_MESON_CROSSFILE = "--cross-file ${mesonCrossFile}";
}
+21
View File
@@ -0,0 +1,21 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash WineOnArm/shell.nix
# Helper script to configure CMake for building FEX as library for emulation
# of 32-bit applications in Wine/Proton.
# The required cross-toolchains will be set up and managed by nix.
if [ $# -eq 0 ]
then
echo "Expected CMake argument list"
exit 1
fi
if [ -f CMakeCache.txt ]
then
echo "Expected empty build folder"
exit 1
fi
set -o xtrace
cmake $FEX_CMAKE_TOOLCHAIN_WOW64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LTO=False -DBUILD_TESTS=False $@
+21
View File
@@ -0,0 +1,21 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash WineOnArm/shell.nix
# Helper script to configure CMake for building FEX as library for emulation
# of 64-bit applications in Wine/Proton
# Nix is used to install and manage the required cross-toolchains.
if [ $# -eq 0 ]
then
echo "Expected CMake argument list"
exit 1
fi
if [ -f CMakeCache.txt ]
then
echo "Expected empty build folder"
exit 1
fi
set -o xtrace
cmake $FEX_CMAKE_TOOLCHAIN_ARM64EC -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LTO=False -DBUILD_TESTS=False $@
+17
View File
@@ -0,0 +1,17 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash FEXLinuxTests/shell.nix
# Helper script to configure CMake for building FEXLinuxTests.
# Nix is used to install and manage the required cross-toolchains.
if [ ! -f CMakeCache.txt ]
then
echo "Must be run from a pre-configured CMake build folder"
exit 1
fi
# Remove previous build to ensure the new toolchain is applied
rm -rf unittests/FEXLinuxTests
set -o xtrace
cmake . $FEX_CMAKE_TOOLCHAINS -DBUILD_TESTS=ON -DBUILD_FEX_LINUX_TESTS=ON
+22
View File
@@ -0,0 +1,22 @@
# Helper script to configure CMake for library forwarding in FEX.
# Nix is used to install and manage the required cross-toolchains.
if [ ! -f CMakeCache.txt ]
then
echo "Must be run from a pre-configured CMake build folder"
exit 1
fi
# Remove previous build to ensure the new toolchain is applied
rm -rf guest-libs guest-libs-32 Guest Guest_32
# Set clang executable path manually since the one from the nix store
# will be picked up otherwise
CLANG_EXEC_PATH=""
if ! grep -q CLANG_EXEC_PATH CMakeCache.txt
then
CLANG_EXEC_PATH="-DCLANG_EXEC_PATH=`which clang`"
fi
nix-shell `dirname -- "$0"`/LibraryForwarding/shell.nix \
--run "set -o xtrace; cmake . \$FEX_CMAKE_TOOLCHAINS -DBUILD_THUNKS=ON $CLANG_EXEC_PATH; set +o xtrace"
+1 -1
+18
View File
@@ -3,14 +3,32 @@
#ifdef _M_X86_64
#include <xmmintrin.h>
#include <immintrin.h>
#endif
namespace FEXCore {
struct VectorScalarF64Pair {
double val[2];
};
#ifdef _M_ARM_64
// Can't use uint8x16_t directly from arm_neon.h here.
// Overrides softfloat-3e's defines which causes problems.
using VectorRegType = __attribute__((neon_vector_type(16))) uint8_t;
struct VectorRegPairType {
VectorRegType val[2];
};
static inline VectorRegPairType MakeVectorRegPair(VectorRegType low, VectorRegType high) {
return VectorRegPairType {low, high};
}
#elif defined(_M_X86_64)
using VectorRegType = __m128i;
using VectorRegPairType = __m256i;
static inline VectorRegPairType MakeVectorRegPair(VectorRegType low, VectorRegType high) {
return _mm256_set_m128i(high, low);
}
#endif
} // namespace FEXCore
+2 -16
View File
@@ -118,7 +118,7 @@
},
"ThunkHostLibs": {
"Type": "str",
"Default": "@CMAKE_INSTALL_FULL_LIBDIR@/fex-emu/HostThunks/",
"Default": "@CMAKE_INSTALL_FULL_LIBDIR@/fex-emu/HostThunks",
"ShortArg": "t",
"Desc": [
"Folder to find the host-side thunking libraries."
@@ -126,26 +126,12 @@
},
"ThunkGuestLibs": {
"Type": "str",
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks/",
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks",
"ShortArg": "j",
"Desc": [
"Folder to find the guest-side thunking libraries."
]
},
"ThunkHostLibs32": {
"Type": "str",
"Default": "@CMAKE_INSTALL_FULL_LIBDIR@/fex-emu/HostThunks_32/",
"Desc": [
"Folder to find the 32-bit host-side thunking libraries."
]
},
"ThunkGuestLibs32": {
"Type": "str",
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks_32/",
"Desc": [
"Folder to find the 32-bit guest-side thunking libraries."
]
},
"ThunkConfig": {
"Type": "str",
"Default": "",
+1 -2
View File
@@ -71,7 +71,7 @@ namespace CPU {
fextl::shared_ptr<CodeBuffer> StartLargerCodeBuffer();
// Write offset into the latest CodeBuffer
std::size_t LatestOffset;
std::size_t LatestOffset {};
// Protects writes to the latest CodeBuffer and changes to LatestOffset
FEXCore::ForkableUniqueMutex CodeBufferWriteMutex;
@@ -198,7 +198,6 @@ namespace CPU {
FEXCore::Core::InternalThreadState* ThreadState;
size_t MaxCodeSize;
[[nodiscard]]
CodeBuffer* GetEmptyCodeBuffer();
+8 -8
View File
@@ -426,7 +426,7 @@ void ContextImpl::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread)
Thread->PassManager->RegisterSyscallHandler(SyscallHandler);
// Create CPU backend
Thread->PassManager->InsertRegisterAllocationPass();
Thread->PassManager->InsertRegisterAllocationPass(this);
Thread->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, Thread);
Thread->PassManager->Finalize();
@@ -771,14 +771,8 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT
if (!IRView) {
return {nullptr, nullptr, 0, 0};
}
auto DebugData = fextl::make_unique<FEXCore::Core::DebugData>();
// If the trap flag is set we generate single instruction blocks that each check to generate a single step exception.
bool TFSet = Thread->CurrentFrame->State.flags[X86State::RFLAG_TF_RAW_LOC];
// Attempt to get the CPU backend to compile this code
// Re-check if another thread raced us in compiling this block.
// We could lock CodeBufferWriteMutex earlier to prevent this from happening,
// but this would increase lock contention. Redundant frontend runs aren't
@@ -790,6 +784,11 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT
}
}
auto DebugData = fextl::make_unique<FEXCore::Core::DebugData>();
// If the trap flag is set we generate single instruction blocks that each check to generate a single step exception.
bool TFSet = Thread->CurrentFrame->State.flags[X86State::RFLAG_TF_RAW_LOC];
auto CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, Length, TotalInstructions == 1, &*IRView, DebugData.get(), TFSet);
// Release the IR
@@ -988,7 +987,8 @@ void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t Gu
const auto GPRSize = GetGPROpSize();
if (GPRSize == IR::OpSize::i64Bit) {
emit->_StoreRegister(emit->_Constant(Entrypoint), X86State::REG_R11, IR::GPRClass, GPRSize);
IR::Ref R = emit->_StoreRegister(emit->_Constant(Entrypoint), GPRSize);
R->Reg = IR::PhysicalRegister(IR::GPRFixedClass, X86State::REG_R11).Raw;
} else {
emit->_StoreContext(GPRSize, IR::FPRClass, emit->_VCastFromGPR(IR::OpSize::i64Bit, IR::OpSize::i64Bit, emit->_Constant(Entrypoint)),
offsetof(Core::CPUState, mm[0][0]));
@@ -517,14 +517,15 @@ void Dispatcher::EmitDispatcher() {
ldr(ARMEmitter::XReg::x3, R, Offset);
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(ARMEmitter::Reg::r3);
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t, uint64_t>(ARMEmitter::Reg::r3);
} else {
blr(ARMEmitter::Reg::r3);
}
// Result is now in x0
// Result is now in x0, x1
if (!TMP_ABIARGS) {
mov(TMP1, ARMEmitter::XReg::x0);
mov(TMP2, ARMEmitter::XReg::x1);
}
FillStaticRegs();
@@ -539,8 +540,6 @@ void Dispatcher::EmitDispatcher() {
LUDIVHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LUDIV));
LDIVHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LDIV));
LUREMHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LUREM));
LREMHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LREM));
// Interpreter fallbacks
{
@@ -559,6 +558,8 @@ void Dispatcher::EmitDispatcher() {
FABI_I64_I16_F80_F80_PTR,
FABI_F80_I16_F80_PTR,
FABI_F80_I16_F80_F80_PTR,
FABI_F80x2_I16_F80_PTR,
FABI_F64x2_I16_F64_PTR,
FABI_I32_I64_I64_V128_V128_I16,
FABI_I32_V128_V128_I16,
}};
@@ -627,6 +628,22 @@ uint64_t Dispatcher::GenerateABICall(FallbackABI ABI) {
constexpr static auto VABI1 = ARMEmitter::VReg::v0;
constexpr static auto VABI2 = ARMEmitter::VReg::v1;
auto FillF80x2Result = [&]() {
if (!TMP_ABIARGS) {
mov(VTMP1.Q(), VABI1.Q());
mov(VTMP2.Q(), VABI2.Q());
}
FillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, true);
};
auto FillF64x2Result = [&]() {
if (!TMP_ABIARGS) {
fmov(VTMP1.D(), VABI1.D());
fmov(VTMP2.D(), VABI2.D());
}
FillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, true);
};
auto FillF80Result = [&]() {
if (VTMP1 != VABI1) {
mov(VTMP1.Q(), VABI1.Q());
@@ -947,6 +964,52 @@ uint64_t Dispatcher::GenerateABICall(FallbackABI ABI) {
FillF80Result();
} break;
case FABI_F80x2_I16_F80_PTR: {
// Linux Reg/Win32 Reg:
// tmp4 (x4/x13): FallbackHandler
// x30: return
// vtmp1 (v0/v16): vector source 1
// vtmp2 (v1/v16): vector source 2
SpillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, TMP3, true);
ldrh(ARMEmitter::WReg::w0, STATE, offsetof(FEXCore::Core::CPUState, FCW));
mov(ARMEmitter::XReg::x1, STATE);
if (!TMP_ABIARGS) {
mov(VABI1.Q(), VTMP1.Q());
}
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
// GenerateIndirectRuntimeCall<FEXCore::VectorRegPairType, uint16_t, FEXCore::VectorRegType, uint64_t>(FallbackPointerReg);
} else {
blr(FallbackPointerReg);
}
FillF80x2Result();
} break;
case FABI_F64x2_I16_F64_PTR: {
// Linux Reg/Win32 Reg:
// tmp4 (x4/x13): FallbackHandler
// x30: return
// vtmp1 (v0/v16): vector source 1
// vtmp2 (v1/v16): vector source 2
SpillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, TMP3, true);
ldrh(ARMEmitter::WReg::w0, STATE, offsetof(FEXCore::Core::CPUState, FCW));
mov(ARMEmitter::XReg::x1, STATE);
if (!TMP_ABIARGS) {
fmov(VABI1.D(), VTMP1.D());
}
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
// GenerateIndirectRuntimeCall<FEXCore::VectorScalarF64Pair, uint16_t, FEXCore::VectorRegType, uint64_t>(FallbackPointerReg);
} else {
blr(FallbackPointerReg);
}
FillF64x2Result();
} break;
case FABI_I32_I64_I64_V128_V128_I16: {
// Linux Reg/Win32 Reg:
// stack: FallbackHandler
@@ -1036,8 +1099,6 @@ void Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState* Thread)
auto& AArch64 = Thread->CurrentFrame->Pointers.AArch64;
AArch64.LUDIVHandler = LUDIVHandlerAddress;
AArch64.LDIVHandler = LDIVHandlerAddress;
AArch64.LUREMHandler = LUREMHandlerAddress;
AArch64.LREMHandler = LREMHandlerAddress;
// Fill in the fallback handlers
InterpreterOps::FillFallbackIndexPointers(Common.FallbackHandlerPointers, &ABIPointers[0]);
@@ -118,8 +118,6 @@ private:
// Long division helpers
uint64_t LUDIVHandlerAddress {};
uint64_t LDIVHandlerAddress {};
uint64_t LUREMHandlerAddress {};
uint64_t LREMHandlerAddress {};
void EmitDispatcher();
uint64_t GenerateABICall(FallbackABI ABI);
@@ -69,10 +69,6 @@ Decoder::Decoder(FEXCore::Context::ContextImpl* ctx)
, OSABI {ctx->SyscallHandler ? ctx->SyscallHandler->GetOSABI() : FEXCore::HLE::SyscallOSABI::OS_UNKNOWN}
, PoolObject {ctx->FrontendAllocator, sizeof(FEXCore::X86Tables::DecodedInst) * DefaultDecodedBufferSize} {}
Decoder::~Decoder() {
PoolObject.UnclaimBuffer();
}
uint8_t Decoder::ReadByte() {
uint8_t Byte = InstStream[InstructionSize];
LOGMAN_THROW_A_FMT(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
-1
View File
@@ -34,7 +34,6 @@ public:
};
Decoder(FEXCore::Context::ContextImpl* ctx);
~Decoder();
void DecodeInstructionsAtEntry(const uint8_t* InstStream, uint64_t PC, uint64_t MaxInst,
std::function<void(uint64_t BlockEntry, uint64_t Start, uint64_t Length)> AddContainedCodePage);
@@ -202,6 +202,15 @@ struct OpHandlers<IR::OP_F80COS> {
}
};
template<>
struct OpHandlers<IR::OP_F80SINCOS> {
FEXCORE_PRESERVE_ALL_ATTR static VectorRegPairType handle(uint16_t FCW, VectorRegType Src1, FEXCore::Core::CpuStateFrame* Frame) {
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
softfloat_state State = SoftFloatStateFromFCW(FCW, true);
return FEXCore::MakeVectorRegPair(X80SoftFloat::FSIN(&State, Src1), X80SoftFloat::FCOS(&State, Src1));
}
};
template<>
struct OpHandlers<IR::OP_F80XTRACT_EXP> {
FEXCORE_PRESERVE_ALL_ATTR static VectorRegType handle(uint16_t FCW, VectorRegType Src1, FEXCore::Core::CpuStateFrame* Frame) {
@@ -315,6 +324,21 @@ struct OpHandlers<IR::OP_F64COS> {
}
};
template<>
struct OpHandlers<IR::OP_F64SINCOS> {
FEXCORE_PRESERVE_ALL_ATTR static VectorScalarF64Pair handle(uint16_t FCW, double src, FEXCore::Core::CpuStateFrame* Frame) {
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
double sin, cos;
#ifdef _WIN32
sin = ::sin(src);
cos = ::cos(src);
#else
sincos(src, &sin, &cos);
#endif
return VectorScalarF64Pair {sin, cos};
}
};
template<>
struct OpHandlers<IR::OP_F64TAN> {
FEXCORE_PRESERVE_ALL_ATTR static double handle(uint16_t FCW, double src, FEXCore::Core::CpuStateFrame* Frame) {
@@ -50,6 +50,8 @@ void InterpreterOps::FillFallbackIndexPointers(Core::FallbackABIInfo* Info, uint
Info[Core::OPINDEX_F80SQRT] = {ABIHandlers[FABI_F80_I16_F80_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80SQRT>::handle)};
Info[Core::OPINDEX_F80SIN] = {ABIHandlers[FABI_F80_I16_F80_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80SIN>::handle)};
Info[Core::OPINDEX_F80COS] = {ABIHandlers[FABI_F80_I16_F80_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80COS>::handle)};
Info[Core::OPINDEX_F80SINCOS] = {ABIHandlers[FABI_F80x2_I16_F80_PTR],
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80SINCOS>::handle)};
Info[Core::OPINDEX_F80XTRACT_EXP] = {ABIHandlers[FABI_F80_I16_F80_PTR],
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80XTRACT_EXP>::handle)};
Info[Core::OPINDEX_F80XTRACT_SIG] = {ABIHandlers[FABI_F80_I16_F80_PTR],
@@ -82,6 +84,8 @@ void InterpreterOps::FillFallbackIndexPointers(Core::FallbackABIInfo* Info, uint
// Double Precision Unary
Info[Core::OPINDEX_F64SIN] = {ABIHandlers[FABI_F64_I16_F64_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64SIN>::handle)};
Info[Core::OPINDEX_F64COS] = {ABIHandlers[FABI_F64_I16_F64_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64COS>::handle)};
Info[Core::OPINDEX_F64SINCOS] = {ABIHandlers[FABI_F64x2_I16_F64_PTR],
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64SINCOS>::handle)};
Info[Core::OPINDEX_F64TAN] = {ABIHandlers[FABI_F64_I16_F64_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64TAN>::handle)};
Info[Core::OPINDEX_F64F2XM1] = {ABIHandlers[FABI_F64_I16_F64_PTR],
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64F2XM1>::handle)};
@@ -198,6 +202,12 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
return true; \
}
#define COMMON_UNARYPAIR_X87_OP(OP) \
case IR::OP_F80##OP: { \
*Info = {FABI_F80x2_I16_F80_PTR, Core::OPINDEX_F80##OP}; \
return true; \
}
#define COMMON_BINARY_X87_OP(OP) \
case IR::OP_F80##OP: { \
*Info = {FABI_F80_I16_F80_F80_PTR, Core::OPINDEX_F80##OP}; \
@@ -215,6 +225,12 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
*Info = {FABI_F64_I16_F64_PTR, Core::OPINDEX_F64##OP}; \
return true; \
}
#define COMMON_UNARYPAIR_F64_OP(OP) \
case IR::OP_F64##OP: { \
*Info = {FABI_F64x2_I16_F64_PTR, Core::OPINDEX_F64##OP}; \
return true; \
}
#define COMMON_BINARY_F64_OP(OP) \
case IR::OP_F64##OP: { \
*Info = {FABI_F64_I16_F64_F64_PTR, Core::OPINDEX_F64##OP}; \
@@ -228,6 +244,7 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
COMMON_UNARY_X87_OP(SQRT)
COMMON_UNARY_X87_OP(SIN)
COMMON_UNARY_X87_OP(COS)
COMMON_UNARYPAIR_X87_OP(SINCOS)
COMMON_UNARY_X87_OP(XTRACT_EXP)
COMMON_UNARY_X87_OP(XTRACT_SIG)
COMMON_UNARY_X87_OP(BCDSTORE)
@@ -249,6 +266,7 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
COMMON_UNARY_F64_OP(TAN)
COMMON_UNARY_F64_OP(SIN)
COMMON_UNARY_F64_OP(COS)
COMMON_UNARYPAIR_F64_OP(SINCOS)
// Double Precision Binary
COMMON_BINARY_F64_OP(FYL2X)
@@ -27,6 +27,8 @@ enum FallbackABI {
FABI_I64_I16_F80_F80_PTR,
FABI_F80_I16_F80_PTR,
FABI_F80_I16_F80_F80_PTR,
FABI_F80x2_I16_F80_PTR,
FABI_F64x2_I16_F64_PTR,
FABI_I32_I64_I64_V128_V128_I16,
FABI_I32_V128_V128_I16,
FABI_UNKNOWN,
+71 -281
View File
@@ -401,122 +401,6 @@ DEF_OP(SMull) {
smull(GetReg(Node).X(), GetReg(Op->Src1).W(), GetReg(Op->Src2).W());
}
DEF_OP(Div) {
auto Op = IROp->C<IR::IROp_Div>();
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
const auto OpSize = IROp->Size;
const auto EmitSize = ConvertSize(IROp);
const auto Dst = GetReg(Node);
auto Src1 = GetReg(Op->Src1);
auto Src2 = GetReg(Op->Src2);
if (OpSize == IR::OpSize::i8Bit) {
sxtb(EmitSize, TMP1, Src1);
sxtb(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
} else if (OpSize == IR::OpSize::i16Bit) {
sxth(EmitSize, TMP1, Src1);
sxth(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
}
sdiv(EmitSize, Dst, Src1, Src2);
}
DEF_OP(UDiv) {
auto Op = IROp->C<IR::IROp_UDiv>();
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
const auto OpSize = IROp->Size;
const auto EmitSize = ConvertSize(IROp);
const auto Dst = GetReg(Node);
auto Src1 = GetReg(Op->Src1);
auto Src2 = GetReg(Op->Src2);
if (OpSize == IR::OpSize::i8Bit) {
uxtb(EmitSize, TMP1, Src1);
uxtb(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
} else if (OpSize == IR::OpSize::i16Bit) {
uxth(EmitSize, TMP1, Src1);
uxth(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
}
udiv(EmitSize, Dst, Src1, Src2);
}
DEF_OP(Rem) {
auto Op = IROp->C<IR::IROp_Rem>();
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
const auto OpSize = IROp->Size;
const auto EmitSize = ConvertSize(IROp);
const auto Dst = GetReg(Node);
auto Src1 = GetReg(Op->Src1);
auto Src2 = GetReg(Op->Src2);
if (OpSize == IR::OpSize::i8Bit) {
sxtb(EmitSize, TMP1, Src1);
sxtb(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
} else if (OpSize == IR::OpSize::i16Bit) {
sxth(EmitSize, TMP1, Src1);
sxth(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
}
sdiv(EmitSize, TMP1, Src1, Src2);
msub(EmitSize, Dst, TMP1, Src2, Src1);
}
DEF_OP(URem) {
auto Op = IROp->C<IR::IROp_URem>();
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
const auto OpSize = IROp->Size;
const auto EmitSize = ConvertSize(IROp);
const auto Dst = GetReg(Node);
auto Src1 = GetReg(Op->Src1);
auto Src2 = GetReg(Op->Src2);
if (OpSize == IR::OpSize::i8Bit) {
uxtb(EmitSize, TMP1, Src1);
uxtb(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
} else if (OpSize == IR::OpSize::i16Bit) {
uxth(EmitSize, TMP1, Src1);
uxth(EmitSize, TMP2, Src2);
Src1 = TMP1;
Src2 = TMP2;
}
udiv(EmitSize, TMP3, Src1, Src2);
msub(EmitSize, Dst, TMP3, Src2, Src1);
}
DEF_OP(MulH) {
auto Op = IROp->C<IR::IROp_MulH>();
const auto OpSize = IROp->Size;
@@ -955,15 +839,39 @@ DEF_OP(PExt) {
}
}
DEF_OP(LDiv) {
auto Op = IROp->C<IR::IROp_LDiv>();
DEF_OP(Div) {
auto Op = IROp->C<IR::IROp_Div>();
const auto OpSize = IROp->Size;
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
const auto Dst = GetReg(Node);
const auto Quotient = GetReg(Op->OutQuotient);
const auto Remainder = GetReg(Op->OutRemainder);
auto Lower = GetReg(Op->Lower);
auto Divisor = GetReg(Op->Divisor);
if (Op->Upper.IsInvalid()) {
const auto EmitSize = ConvertSize(IROp);
if (OpSize == IR::OpSize::i8Bit) {
sxtb(EmitSize, TMP1, Lower);
sxtb(EmitSize, TMP2, Divisor);
Lower = TMP1;
Divisor = TMP2;
} else if (OpSize == IR::OpSize::i16Bit) {
sxth(EmitSize, TMP1, Lower);
sxth(EmitSize, TMP2, Divisor);
Lower = TMP1;
Divisor = TMP2;
}
sdiv(EmitSize, Quotient, Lower, Divisor);
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
return;
}
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
const auto Upper = GetReg(Op->Upper);
const auto Lower = GetReg(Op->Lower);
const auto Divisor = GetReg(Op->Divisor);
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
@@ -972,7 +880,8 @@ DEF_OP(LDiv) {
uxth(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 16, 16);
sxth(EmitSize, TMP2, Divisor);
sdiv(EmitSize, Dst, TMP1, TMP2);
sdiv(EmitSize, Quotient, TMP1, TMP2);
msub(EmitSize, Remainder, Quotient, TMP2, TMP1);
break;
}
case IR::OpSize::i32Bit: {
@@ -980,7 +889,8 @@ DEF_OP(LDiv) {
mov(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 32, 32);
sxtw(TMP2, Divisor.W());
sdiv(EmitSize, Dst, TMP1, TMP2);
sdiv(EmitSize, Quotient, TMP1, TMP2);
msub(EmitSize, Remainder, Quotient, TMP2, TMP1);
break;
}
case IR::OpSize::i64Bit: {
@@ -1007,8 +917,9 @@ DEF_OP(LDiv) {
blr(TMP4);
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
// Move result to its destination register
mov(EmitSize, Dst, TMP1);
// Move results to the destination registers
mov(EmitSize, Quotient, TMP1);
mov(EmitSize, Remainder, TMP2);
// Skip 64-bit path
b(&LongDIVRet);
@@ -1016,39 +927,57 @@ DEF_OP(LDiv) {
Bind(&Only64Bit);
// 64-Bit only
{ sdiv(EmitSize, Dst, Lower, Divisor); }
{
sdiv(EmitSize, Quotient, Lower, Divisor);
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
}
Bind(&LongDIVRet);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown LDIV Size: {}", OpSize); break;
default: LOGMAN_MSG_A_FMT("Unknown DIV Size: {}", OpSize); break;
}
}
DEF_OP(LUDiv) {
auto Op = IROp->C<IR::IROp_LUDiv>();
DEF_OP(UDiv) {
auto Op = IROp->C<IR::IROp_UDiv>();
const auto OpSize = IROp->Size;
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
const auto Dst = GetReg(Node);
const auto Upper = GetReg(Op->Upper);
const auto Quotient = GetReg(Op->OutQuotient);
const auto Remainder = GetReg(Op->OutRemainder);
const auto Lower = GetReg(Op->Lower);
const auto Divisor = GetReg(Op->Divisor);
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64=
if (Op->Upper.IsInvalid()) {
const auto EmitSize = ConvertSize(IROp);
udiv(EmitSize, Quotient, Lower, Divisor);
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
return;
}
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
const auto Upper = GetReg(Op->Upper);
switch (OpSize) {
case IR::OpSize::i16Bit: {
uxth(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 16, 16);
udiv(EmitSize, Dst, TMP1, Divisor);
udiv(EmitSize, Quotient, TMP1, Divisor);
msub(EmitSize, Remainder, Quotient, Divisor, TMP1);
break;
}
case IR::OpSize::i32Bit: {
// We need to mask divisor if we have Upper bits, since the frontend does
// not on the hope that we can optimize to use the path above.
mov(ARMEmitter::Size::i32Bit, TMP2, Divisor);
// TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits.
mov(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 32, 32);
udiv(EmitSize, Dst, TMP1, Divisor);
udiv(EmitSize, Quotient, TMP1, TMP2);
msub(EmitSize, Remainder, Quotient, TMP2, TMP1);
break;
}
case IR::OpSize::i64Bit: {
@@ -1071,8 +1000,9 @@ DEF_OP(LUDiv) {
blr(TMP4);
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
// Move result to its destination register
mov(EmitSize, Dst, TMP1);
// Move results to the destination registers
mov(EmitSize, Quotient, TMP1);
mov(EmitSize, Remainder, TMP2);
// Skip 64-bit path
b(&LongDIVRet);
@@ -1080,7 +1010,10 @@ DEF_OP(LUDiv) {
Bind(&Only64Bit);
// 64-Bit only
{ udiv(EmitSize, Dst, Lower, Divisor); }
{
udiv(EmitSize, Quotient, Lower, Divisor);
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
}
Bind(&LongDIVRet);
break;
@@ -1089,149 +1022,6 @@ DEF_OP(LUDiv) {
}
}
DEF_OP(LRem) {
auto Op = IROp->C<IR::IROp_LRem>();
const auto OpSize = IROp->Size;
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
const auto Dst = GetReg(Node);
const auto Upper = GetReg(Op->Upper);
const auto Lower = GetReg(Op->Lower);
const auto Divisor = GetReg(Op->Divisor);
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
switch (OpSize) {
case IR::OpSize::i16Bit: {
uxth(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 16, 16);
sxth(EmitSize, TMP2, Divisor);
sdiv(EmitSize, TMP3, TMP1, TMP2);
msub(EmitSize, Dst, TMP3, TMP2, TMP1);
break;
}
case IR::OpSize::i32Bit: {
// TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits.
mov(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 32, 32);
sxtw(TMP3, Divisor.W());
sdiv(EmitSize, TMP2, TMP1, TMP3);
msub(EmitSize, Dst, TMP2, TMP3, TMP1);
break;
}
case IR::OpSize::i64Bit: {
ARMEmitter::ForwardLabel Only64Bit {};
ARMEmitter::ForwardLabel LongDIVRet {};
// Check if the upper bits match the top bit of the lower 64-bits
// Sign extend the top bit of lower bits
sbfx(EmitSize, TMP1, Lower, 63, 1);
eor(EmitSize, TMP1, TMP1, Upper);
// If the sign bit matches then the result is zero
cbz(EmitSize, TMP1, &Only64Bit);
// Long divide
{
mov(EmitSize, TMP1, Upper);
mov(EmitSize, TMP2, Lower);
mov(EmitSize, TMP3, Divisor);
ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LREMHandler));
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
blr(TMP4);
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
// Move result to its destination register
mov(EmitSize, Dst, TMP1);
// Skip 64-bit path
b(&LongDIVRet);
}
Bind(&Only64Bit);
// 64-Bit only
{
sdiv(EmitSize, TMP1, Lower, Divisor);
msub(EmitSize, Dst, TMP1, Divisor, Lower);
}
Bind(&LongDIVRet);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown LREM Size: {}", OpSize); break;
}
}
DEF_OP(LURem) {
auto Op = IROp->C<IR::IROp_LURem>();
const auto OpSize = IROp->Size;
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
const auto Dst = GetReg(Node);
const auto Upper = GetReg(Op->Upper);
const auto Lower = GetReg(Op->Lower);
const auto Divisor = GetReg(Op->Divisor);
// Each source is OpSize in size
// So you can have up to a 128bit divide from x86-64
switch (OpSize) {
case IR::OpSize::i16Bit: {
uxth(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 16, 16);
udiv(EmitSize, TMP2, TMP1, Divisor);
msub(EmitSize, Dst, TMP2, Divisor, TMP1);
break;
}
case IR::OpSize::i32Bit: {
// TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits.
mov(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 32, 32);
udiv(EmitSize, TMP2, TMP1, Divisor);
msub(EmitSize, Dst, TMP2, Divisor, TMP1);
break;
}
case IR::OpSize::i64Bit: {
ARMEmitter::ForwardLabel Only64Bit {};
ARMEmitter::ForwardLabel LongDIVRet {};
// Check the upper bits for zero
// If the upper bits are zero then we can do a 64-bit divide
cbz(EmitSize, Upper, &Only64Bit);
// Long divide
{
mov(EmitSize, TMP1, Upper);
mov(EmitSize, TMP2, Lower);
mov(EmitSize, TMP3, Divisor);
ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LUREMHandler));
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
blr(TMP4);
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
// Move result to its destination register
mov(EmitSize, Dst, TMP1);
// Skip 64-bit path
b(&LongDIVRet);
}
Bind(&Only64Bit);
// 64-Bit only
{
udiv(EmitSize, TMP1, Lower, Divisor);
msub(EmitSize, Dst, TMP1, Divisor, Lower);
}
Bind(&LongDIVRet);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown LUREM Size: {}", OpSize); break;
}
}
DEF_OP(Not) {
auto Op = IROp->C<IR::IROp_Not>();
@@ -411,6 +411,7 @@ DEF_OP(ThreadRemoveCodeEntry) {
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
isb();
mov(ARMEmitter::Size::i64Bit, TMP2, GetReg(Op->Function));
mov(ARMEmitter::Size::i64Bit, TMP3, GetReg(Op->Leaf));
+83 -29
View File
@@ -41,31 +41,32 @@ $end_info$
#include <limits>
namespace {
static uint64_t LUDIV(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
struct DivRem {
uint64_t Quotient;
uint64_t Remainder;
};
static struct DivRem LUDIV(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
__uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
__uint128_t Res = Source / Divisor;
return Res;
return {
.Quotient = (uint64_t)(Source / Divisor),
.Remainder = (uint64_t)(Source % Divisor),
};
}
static int64_t LDIV(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
static struct DivRem
LDIV(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
__int128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
__int128_t Res = Source / Divisor;
return Res;
return {
.Quotient = (uint64_t)(Source / Divisor),
.Remainder = (uint64_t)(Source % Divisor),
};
}
static uint64_t LUREM(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
__uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
__uint128_t Res = Source % Divisor;
return Res;
}
static int64_t LREM(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
__int128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
__int128_t Res = Source % Divisor;
return Res;
}
static void PrintValue(uint64_t Value) {
static void
PrintValue(uint64_t Value) {
LogMan::Msg::DFmt("Value: 0x{:x}", Value);
}
@@ -83,6 +84,16 @@ void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) {
LOGMAN_MSG_A_FMT("Unhandled IR Op: {}", FEXCore::IR::GetName(IROp->Op));
#endif
} else {
auto FillF80x2Result = [&](auto DstLo, auto DstHi) {
mov(DstLo.Q(), VTMP1.Q());
mov(DstHi.Q(), VTMP2.Q());
};
auto FillF64x2Result = [&](auto DstLo, auto DstHi) {
fmov(DstLo.D(), VTMP1.D());
fmov(DstHi.D(), VTMP2.D());
};
auto FillF80Result = [&]() {
const auto Dst = GetVReg(Node);
mov(Dst.Q(), VTMP1.Q());
@@ -228,6 +239,30 @@ void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) {
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
FillF64Result();
} break;
case FABI_F64x2_I16_F64_PTR: {
// Linux Reg/Win32 Reg:
// tmp4 (x4/x13): FallbackHandler
// x30: return
// vtmp1 (v0/v16): vector source
// vtmp2 (v1/v16): vector source
#ifdef VIXL_SIMULATOR
LOGMAN_THROW_A_FMT(CTX->Config.DisableVixlIndirectCalls, "Vector register pairs unsupported by simulator currently");
#endif
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
const auto Src1 = GetVReg(IROp->Args[0]);
const auto DstLo = GetVReg(IROp->Args[1]);
const auto DstHi = GetVReg(IROp->Args[2]);
fmov(VTMP1.D(), Src1.D());
ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].ABIHandler));
ldr(TMP4, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].Func));
blr(TMP1);
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
FillF64x2Result(DstLo, DstHi);
} break;
case FABI_F64_I16_F64_F64_PTR: {
// Linux Reg/Win32 Reg:
@@ -344,6 +379,31 @@ void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) {
FillF80Result();
} break;
case FABI_F80x2_I16_F80_PTR: {
// Linux Reg/Win32 Reg:
// tmp4 (x4/x13): FallbackHandler
// x30: return
// vtmp1 (v0/v16): vector source 1
// vtmp2 (v1/v16): vector source 2
#ifdef VIXL_SIMULATOR
LOGMAN_THROW_A_FMT(CTX->Config.DisableVixlIndirectCalls, "Vector register pairs unsupported by simulator currently");
#endif
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
const auto Src1 = GetVReg(IROp->Args[0]);
const auto DstLo = GetVReg(IROp->Args[1]);
const auto DstHi = GetVReg(IROp->Args[2]);
mov(VTMP1.Q(), Src1.Q());
ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].ABIHandler));
ldr(TMP4, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].Func));
blr(TMP1);
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
FillF80x2Result(DstLo, DstHi);
} break;
case FABI_F80_I16_F80_F80_PTR: {
// Linux Reg/Win32 Reg:
// tmp4 (x4/x13): FallbackHandler
@@ -545,8 +605,6 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::In
AArch64.LUDIV = reinterpret_cast<uint64_t>(LUDIV);
AArch64.LDIV = reinterpret_cast<uint64_t>(LDIV);
AArch64.LUREM = reinterpret_cast<uint64_t>(LUREM);
AArch64.LREM = reinterpret_cast<uint64_t>(LREM);
}
CurrentCodeBuffer = CodeBuffers.GetLatest();
@@ -794,10 +852,8 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
}
}
if (DebugData) {
DebugData->Subblocks.push_back({static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockEntry),
static_cast<uint32_t>(GetCursorAddress<uint8_t*>() - BlockStartHostCode)});
}
DebugData->Subblocks.push_back({static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockEntry),
static_cast<uint32_t>(GetCursorAddress<uint8_t*>() - BlockStartHostCode)});
}
// Make sure last branch is generated. It certainly can't be eliminated here.
@@ -940,10 +996,8 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
}
#endif
if (DebugData) {
DebugData->HostCodeSize = CodeData.Size;
DebugData->Relocations = &Relocations;
}
DebugData->HostCodeSize = CodeData.Size;
DebugData->Relocations = &Relocations;
this->IR = nullptr;
@@ -11,6 +11,7 @@ $end_info$
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/CPUID.h"
#include "Interface/Core/JIT/JITClass.h"
#include "Interface/IR/RegisterAllocationData.h"
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/MathUtils.h>
@@ -174,23 +175,16 @@ DEF_OP(LoadAF) {
DEF_OP(StoreRegister) {
const auto Op = IROp->C<IR::IROp_StoreRegister>();
auto Reg = IR::PhysicalRegister(Node);
if (Op->Class == IR::GPRClass) {
unsigned Reg = Op->Reg == Core::CPUState::PF_AS_GREG ? (StaticRegisters.size() - 2) :
Op->Reg == Core::CPUState::AF_AS_GREG ? (StaticRegisters.size() - 1) :
Op->Reg;
LOGMAN_THROW_A_FMT(Reg < StaticRegisters.size(), "out of range reg");
const auto reg = StaticRegisters[Reg];
if (Reg.Class == IR::GPRFixedClass) {
// Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode.
mov(ARMEmitter::Size::i64Bit, reg, GetReg(Op->Value));
} else if (Op->Class == IR::FPRClass) {
mov(ARMEmitter::Size::i64Bit, GetReg(Reg), GetReg(Op->Value));
} else if (Reg.Class == IR::FPRFixedClass) {
[[maybe_unused]] const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit;
LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "reg out of range");
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
const auto guest = StaticFPRegisters[Op->Reg];
const auto guest = GetVReg(Reg);
const auto host = GetVReg(Op->Value);
if (HostSupportsAVX256) {
@@ -199,7 +193,7 @@ DEF_OP(StoreRegister) {
mov(guest.Q(), host.Q());
}
} else {
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Op->Class);
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Reg.Class);
}
}
@@ -1311,7 +1311,6 @@ void OpDispatchBuilder::CPUIDOp(OpcodeArgs) {
Ref RCX = _AllocateGPR(false);
Ref RDX = _AllocateGPR(false);
_Fence({FEXCore::IR::Fence_Inst});
_CPUID(Src, Leaf, RAX, RBX, RCX, RDX);
StoreGPRRegister(X86State::REG_RAX, RAX);
@@ -2852,9 +2851,10 @@ void OpDispatchBuilder::AASOp(OpcodeArgs) {
void OpDispatchBuilder::AAMOp(OpcodeArgs) {
auto AL = LoadGPRRegister(X86State::REG_RAX, OpSize::i8Bit);
auto Imm8 = _Constant(Op->Src[0].Data.Literal.Value & 0xFF);
auto UDivOp = _UDiv(OpSize::i64Bit, AL, Imm8);
auto URemOp = _URem(OpSize::i64Bit, AL, Imm8);
auto Res = _AddShift(OpSize::i64Bit, URemOp, UDivOp, ShiftType::LSL, 8);
Ref Quotient = _AllocateGPR(true);
Ref Remainder = _AllocateGPR(true);
_UDiv(OpSize::i64Bit, AL, Invalid(), Imm8, Quotient, Remainder);
auto Res = _AddShift(OpSize::i64Bit, Remainder, Quotient, ShiftType::LSL, 8);
StoreGPRRegister(X86State::REG_RAX, Res, OpSize::i16Bit);
SetNZ_ZeroCV(OpSize::i8Bit, Res);
@@ -3581,52 +3581,43 @@ void OpDispatchBuilder::NEGOp(OpcodeArgs) {
}
void OpDispatchBuilder::DIVOp(OpcodeArgs) {
// This loads the divisor
Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
const auto GPRSize = CTX->GetGPROpSize();
const auto Size = OpSizeFromSrc(Op);
auto Size = OpSizeFromSrc(Op);
// This loads the divisor. 32-bit/64-bit paths mask inside the JIT, 8/16 do not.
Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = Size >= OpSize::i32Bit});
if (Size == OpSize::i64Bit && !CTX->Config.Is64BitMode) {
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
DecodeFailure = true;
return;
}
Ref Quotient = _AllocateGPR(true);
Ref Remainder = _AllocateGPR(true);
if (Size == OpSize::i8Bit) {
Ref Src1 = LoadGPRRegister(X86State::REG_RAX, OpSize::i16Bit);
auto UDivOp = _UDiv(OpSize::i16Bit, Src1, Divisor);
auto URemOp = _URem(OpSize::i16Bit, Src1, Divisor);
_UDiv(OpSize::i16Bit, Src1, Invalid(), Divisor, Quotient, Remainder);
// AX[15:0] = concat<URem[7:0]:UDiv[7:0]>
auto ResultAX = _Bfi(GPRSize, 8, 8, UDivOp, URemOp);
auto ResultAX = _Bfi(GPRSize, 8, 8, Quotient, Remainder);
StoreGPRRegister(X86State::REG_RAX, ResultAX, OpSize::i16Bit);
} else if (Size == OpSize::i16Bit) {
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
auto UDivOp = _LUDiv(OpSize::i16Bit, Src1, Src2, Divisor);
auto URemOp = _LURem(OpSize::i16Bit, Src1, Src2, Divisor);
StoreGPRRegister(X86State::REG_RAX, UDivOp, Size);
StoreGPRRegister(X86State::REG_RDX, URemOp, Size);
} else if (Size == OpSize::i32Bit) {
} else {
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
Ref UDivOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LUDiv(OpSize::i32Bit, Src1, Src2, Divisor));
Ref URemOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LURem(OpSize::i32Bit, Src1, Src2, Divisor));
_UDiv(Size, Src1, Src2, Divisor, Quotient, Remainder);
StoreGPRRegister(X86State::REG_RAX, UDivOp);
StoreGPRRegister(X86State::REG_RDX, URemOp);
} else if (Size == OpSize::i64Bit) {
if (!CTX->Config.Is64BitMode) {
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
DecodeFailure = true;
return;
if (Size == OpSize::i32Bit) {
Quotient = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Quotient);
Remainder = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Remainder);
Size = OpSize::iInvalid;
}
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
auto UDivOp = _LUDiv(OpSize::i64Bit, Src1, Src2, Divisor);
auto URemOp = _LURem(OpSize::i64Bit, Src1, Src2, Divisor);
StoreGPRRegister(X86State::REG_RAX, UDivOp);
StoreGPRRegister(X86State::REG_RDX, URemOp);
StoreGPRRegister(X86State::REG_RAX, Quotient, Size);
StoreGPRRegister(X86State::REG_RDX, Remainder, Size);
}
}
@@ -3635,50 +3626,41 @@ void OpDispatchBuilder::IDIVOp(OpcodeArgs) {
Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
const auto GPRSize = CTX->GetGPROpSize();
const auto Size = OpSizeFromSrc(Op);
auto Size = OpSizeFromSrc(Op);
if (Size == OpSize::i64Bit && !CTX->Config.Is64BitMode) {
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
DecodeFailure = true;
return;
}
Ref Quotient = _AllocateGPR(true);
Ref Remainder = _AllocateGPR(true);
if (Size == OpSize::i8Bit) {
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
Src1 = _Sbfe(OpSize::i64Bit, 16, 0, Src1);
Divisor = _Sbfe(OpSize::i64Bit, 8, 0, Divisor);
auto UDivOp = _Div(OpSize::i64Bit, Src1, Divisor);
auto URemOp = _Rem(OpSize::i64Bit, Src1, Divisor);
_Div(OpSize::i64Bit, Src1, Invalid(), Divisor, Quotient, Remainder);
// AX[15:0] = concat<URem[7:0]:UDiv[7:0]>
auto ResultAX = _Bfi(GPRSize, 8, 8, UDivOp, URemOp);
auto ResultAX = _Bfi(GPRSize, 8, 8, Quotient, Remainder);
StoreGPRRegister(X86State::REG_RAX, ResultAX, OpSize::i16Bit);
} else if (Size == OpSize::i16Bit) {
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
auto UDivOp = _LDiv(OpSize::i16Bit, Src1, Src2, Divisor);
auto URemOp = _LRem(OpSize::i16Bit, Src1, Src2, Divisor);
StoreGPRRegister(X86State::REG_RAX, UDivOp, Size);
StoreGPRRegister(X86State::REG_RDX, URemOp, Size);
} else if (Size == OpSize::i32Bit) {
} else {
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
Ref UDivOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LDiv(OpSize::i32Bit, Src1, Src2, Divisor));
Ref URemOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LRem(OpSize::i32Bit, Src1, Src2, Divisor));
_Div(Size, Src1, Src2, Divisor, Quotient, Remainder);
StoreGPRRegister(X86State::REG_RAX, UDivOp);
StoreGPRRegister(X86State::REG_RDX, URemOp);
} else if (Size == OpSize::i64Bit) {
if (!CTX->Config.Is64BitMode) {
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
DecodeFailure = true;
return;
if (Size == OpSize::i32Bit) {
Quotient = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Quotient);
Remainder = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Remainder);
Size = OpSize::iInvalid;
}
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
auto UDivOp = _LDiv(OpSize::i64Bit, Src1, Src2, Divisor);
auto URemOp = _LRem(OpSize::i64Bit, Src1, Src2, Divisor);
StoreGPRRegister(X86State::REG_RAX, UDivOp);
StoreGPRRegister(X86State::REG_RDX, URemOp);
StoreGPRRegister(X86State::REG_RAX, Quotient, Size);
StoreGPRRegister(X86State::REG_RDX, Remainder, Size);
}
}
@@ -4412,9 +4394,6 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::I
if (!DestIsLockedMem(Op) && ALUIROp == FEXCore::IR::IROps::OP_XOR && Op->Dest.IsGPR() && Op->Src[SrcIdx].IsGPR() &&
Op->Dest.Data.GPR == Op->Src[SrcIdx].Data.GPR) {
// Move 0 into the register
StoreResult(GPRClass, Op, _Constant(0), OpSize::iInvalid);
// Set flags for zero result with inverted carry. We subtract an arbitrary
// register from itself to get the zero, since `subs wzr, #0` is not
// encodable. This is optimal and works regardless of the opsize.
@@ -4423,6 +4402,10 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::I
InvalidateAF();
CalculatePF(_SubWithFlags(OpSize::i32Bit, Zero, Zero));
CFInverted = true;
FlushRegisterCache();
// Move 0 into the register
StoreResult(GPRClass, Op, _Constant(0), OpSize::iInvalid);
return;
}
@@ -7,6 +7,7 @@
#include "Interface/Context/Context.h"
#include "Interface/IR/IR.h"
#include "Interface/IR/IREmitter.h"
#include "Interface/IR/RegisterAllocationData.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/Context.h>
@@ -1208,13 +1209,15 @@ public:
Ref Value = RegCache.Value[Index];
if (Index >= GPR0Index && Index <= GPR15Index) {
_StoreRegister(Value, Index - GPR0Index, GPRClass, GPRSize);
Ref R = _StoreRegister(Value, GPRSize);
R->Reg = PhysicalRegister(GPRFixedClass, Index - GPR0Index).Raw;
} else if (Index == PFIndex) {
_StorePF(Value, GPRSize);
} else if (Index == AFIndex) {
_StoreAF(Value, GPRSize);
} else if (Index >= FPR0Index && Index <= FPR15Index) {
_StoreRegister(Value, Index - FPR0Index, FPRClass, VectorSize);
Ref R = _StoreRegister(Value, VectorSize);
R->Reg = PhysicalRegister(FPRFixedClass, Index - FPR0Index).Raw;
} else if (Index == DFIndex) {
_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(Core::CPUState, flags[X86State::RFLAG_DF_RAW_LOC]));
} else {
@@ -2489,10 +2492,10 @@ private:
}
struct ArithRef {
IREmitter* E;
bool IsConstant;
IREmitter* E {};
bool IsConstant {};
union {
Ref R;
Ref R {};
uint64_t C;
};
+5
View File
@@ -165,6 +165,11 @@ struct FEX_PACKED NodeWrapperBase final {
LOGMAN_THROW_A_FMT(IsPointer(), "Offsets are within 2GiB range");
}
void SetInvalid() {
NodeOffset = 0;
LOGMAN_THROW_A_FMT(IsInvalid(), "Zero state");
}
void SetImmediate(uint32_t Immediate) {
LOGMAN_THROW_A_FMT(Immediate < (1u << 31), "Bounded");
NodeOffset = Immediate | (1u << 31);
+31 -80
View File
@@ -79,15 +79,13 @@
"constexpr uint8_t COND_AL = 32 /* always */",
"constexpr FEXCore::IR::RegisterClassType GPRClass {0}",
"constexpr FEXCore::IR::RegisterClassType GPRFixedClass {1}",
"constexpr FEXCore::IR::RegisterClassType FPRClass {2}",
"constexpr FEXCore::IR::RegisterClassType FPRFixedClass {3}",
"constexpr FEXCore::IR::RegisterClassType InvalidClass {0}",
"constexpr FEXCore::IR::RegisterClassType GPRClass {1}",
"constexpr FEXCore::IR::RegisterClassType GPRFixedClass {2}",
"constexpr FEXCore::IR::RegisterClassType FPRClass {3}",
"constexpr FEXCore::IR::RegisterClassType FPRFixedClass {4}",
"constexpr FEXCore::IR::RegisterClassType ComplexClass {5}",
"constexpr FEXCore::IR::RegisterClassType InvalidClass {7}",
"",
"// Only up to 30 registers per register class",
"constexpr uint8_t InvalidReg {31}",
"constexpr uint8_t NumClasses {6}",
"",
"constexpr FEXCore::IR::TypeDefinition i8 {TypeDefinition::Create(1, 0)}",
"constexpr FEXCore::IR::TypeDefinition i16 {TypeDefinition::Create(2, 0)}",
@@ -380,14 +378,11 @@
"DestSize": "Size"
},
"StoreRegister SSA:$Value, u32:$Reg, RegisterClass:$Class, OpSize:#Size": {
"SSA = StoreRegister SSA:$Value, OpSize:#Size": {
"HasSideEffects": true,
"Desc": ["Stores a value to a given register.",
"Size must match the execution mode."],
"DestSize": "Size",
"EmitValidation": [
"WalkFindRegClass($Value) == $Class"
]
"DestSize": "Size"
},
"StorePF GPR:$Value, OpSize:#Size": {
@@ -1459,38 +1454,6 @@
],
"DestSize": "FEXCore::IR::OpSize::i64Bit"
},
"GPR = Div OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Integer signed division"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = UDiv OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Integer unsigned division"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = Rem OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Integer signed remainder"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = URem OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Integer unsigned remainder"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = MulH OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Integer signed multiply returning high results",
"op:",
@@ -1635,45 +1598,23 @@
]
},
"GPR = LDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
"GPR:$Quotient, GPR:$Remainder = Div OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
"Desc": ["Integer long signed division returning lower bits",
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
"Then the divisor divides the temporary dividend and returns the results in the original sized register",
"If Upper is invalid, this is a non-long division."
],
"DestSize": "Size",
"HasSideEffects": true
},
"GPR:$Quotient, GPR:$Remainder = UDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
"Desc": ["Integer long unsigned division returning lower bits",
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
"Then the divisor divides the temporary dividend and returns the results in the original sized register"
"Then the divisor divides the temporary dividend and returns the results in the original sized register",
"If Upper is invalid, this is a non-long division."
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = LUDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
"Desc": ["Integer long unsigned division returning lower bits",
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
"Then the divisor divides the temporary dividend and returns the results in the original sized register"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = LRem OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
"Desc": ["Integer long signed remainder returning lower bits",
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
"Then the divisor divides the temporary dividend and returns the remainder results in the original sized register"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = LURem OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
"Desc": ["Integer long unsigned remainder returning lower bits",
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
"Then the divisor divides the temporary dividend and returns the remainder results in the original sized register"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
"HasSideEffects": true
},
"Float to GPR": {"Ignore": 1},
@@ -2847,6 +2788,11 @@
"FPR = F64COS FPR:$Src": {
"DestSize": "OpSize::i64Bit",
"JITDispatch": false
},
"FPR:$Sin, FPR:$Cos = F64SINCOS FPR:$Src": {
"DestSize": "OpSize::i64Bit",
"HasSideEffects": true,
"JITDispatch": false
}
},
"F80": {
@@ -3186,6 +3132,11 @@
"DestSize": "OpSize::i128Bit",
"JITDispatch": false
},
"FPR:$Sin, FPR:$Cos = F80SINCOS FPR:$X80Src": {
"DestSize": "OpSize::i128Bit",
"HasSideEffects": true,
"JITDispatch": false
},
"F80SINCOSStack": {
"X87": true,
"HasSideEffects": true
@@ -127,10 +127,6 @@ public:
PoolObject.ReownOrClaimBuffer();
}
~DualIntrusiveAllocatorThreadPool() {
PoolObject.UnclaimBuffer();
}
void ReownOrClaimBuffer() {
Data = PoolObject.ReownOrClaimBuffer();
List = Data + MemorySize;
+2 -2
View File
@@ -82,8 +82,8 @@ void PassManager::AddDefaultValidationPasses() {
#endif
}
void PassManager::InsertRegisterAllocationPass() {
InsertPass(IR::CreateRegisterAllocationPass(), "RA");
void PassManager::InsertRegisterAllocationPass(FEXCore::Context::ContextImpl* ctx) {
InsertPass(IR::CreateRegisterAllocationPass(&ctx->CPUID), "RA");
}
void PassManager::Run(IREmitter* IREmit) {
+1 -1
View File
@@ -56,7 +56,7 @@ public:
return PassPtr;
}
void InsertRegisterAllocationPass();
void InsertRegisterAllocationPass(FEXCore::Context::ContextImpl* ctx);
void Run(IREmitter* IREmit);
+2 -1
View File
@@ -4,6 +4,7 @@
#include <FEXCore/fextl/memory.h>
namespace FEXCore {
class CPUIDEmu;
struct HostFeatures;
} // namespace FEXCore
@@ -17,7 +18,7 @@ class RegisterAllocationPass;
fextl::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool SupportsTSOImm9);
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination();
fextl::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass();
fextl::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass(const FEXCore::CPUIDEmu* CPUID);
fextl::unique_ptr<FEXCore::IR::Pass> CreateX87StackOptimizationPass(const FEXCore::HostFeatures&, OpSize GPROpSize);
namespace Validation {
@@ -103,7 +103,7 @@ void IRValidation::Run(IREmitter* IREmit) {
}
// If no physical register was assigned
if (PhyReg.Reg == IR::InvalidReg) {
if (PhyReg.IsInvalid()) {
HadError |= true;
Errors << "%" << ID << ": Had destination but with no register assigned" << std::endl;
}
@@ -117,7 +117,7 @@ struct ControlFlowGraph {
// Add some initial capacity
Info.Predecessors.reserve(2);
BlockMap[ID] = Info;
BlockMap[ID] = std::move(Info);
Worklist.push_back(ID);
}
}
@@ -10,6 +10,7 @@ $end_info$
#include "Interface/IR/IREmitter.h"
#include "Interface/IR/RegisterAllocationData.h"
#include "Interface/IR/Passes.h"
#include "Interface/Core/CPUID.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/Profiler.h>
@@ -21,9 +22,6 @@ using namespace FEXCore;
namespace FEXCore::IR {
namespace {
[[maybe_unused]] constexpr uint32_t INVALID_REG = IR::InvalidReg;
constexpr uint32_t INVALID_CLASS = IR::InvalidClass.Val;
struct RegisterClass {
uint32_t Available;
uint32_t Count;
@@ -55,15 +53,18 @@ namespace {
class ConstrainedRAPass final : public RegisterAllocationPass {
public:
explicit ConstrainedRAPass(const FEXCore::CPUIDEmu* CPUID)
: CPUID {CPUID} {}
void Run(IREmitter* IREmit) override;
void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override;
bool TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp);
private:
RegisterClass Classes[INVALID_CLASS];
RegisterClass Classes[IR::NumClasses];
IREmitter* IREmit;
IRListView* IR;
const FEXCore::CPUIDEmu* CPUID;
// Map of nodes to their preferred register, to coalesce load/store reg.
fextl::vector<PhysicalRegister> PreferredReg;
@@ -106,15 +107,8 @@ private:
return false;
}
switch (IR->GetOp<IROp_Header>(Arg)->Op) {
case OP_INLINECONSTANT:
case OP_INLINEENTRYPOINTOFFSET:
case OP_IRHEADER: return false;
case OP_SPILLREGISTER: LOGMAN_MSG_A_FMT("should not be seen"); return false;
default: return true;
}
auto Op = IR->GetOp<IROp_Header>(Arg)->Op;
return Op != OP_INLINECONSTANT && Op != OP_INLINEENTRYPOINTOFFSET;
};
RegisterClass* GetClass(PhysicalRegister Reg) {
@@ -168,33 +162,24 @@ private:
return nullptr;
};
PhysicalRegister DecodeSRAReg(const IROp_Header* IROp) {
RegisterClassType Class {};
uint8_t Reg {};
PhysicalRegister DecodeSRAReg(const IROp_Header* IROp, Ref Node) {
uint8_t FlagOffset = Classes[GPRFixedClass.Val].Count - 2;
if (IROp->Op == OP_LOADREGISTER) {
const IROp_LoadRegister* Op = IROp->C<IR::IROp_LoadRegister>();
Class = Op->Class;
Reg = Op->Reg;
} else if (IROp->Op == OP_STOREREGISTER) {
const IROp_StoreRegister* Op = IROp->C<IR::IROp_StoreRegister>();
Class = Op->Class;
Reg = Op->Reg;
if (IROp->Op == OP_STOREREGISTER) {
return PhysicalRegister(Node);
} else if (IROp->Op == OP_LOADPF || IROp->Op == OP_STOREPF) {
return PhysicalRegister {GPRFixedClass, FlagOffset};
} else if (IROp->Op == OP_LOADAF || IROp->Op == OP_STOREAF) {
return PhysicalRegister {GPRFixedClass, (uint8_t)(FlagOffset + 1)};
}
LOGMAN_THROW_A_FMT(Class == GPRClass || Class == FPRClass, "SRA classes");
if (Class == FPRClass) {
return PhysicalRegister {FPRFixedClass, Reg};
} else {
return PhysicalRegister {GPRFixedClass, Reg};
const IROp_LoadRegister* Op = IROp->C<IR::IROp_LoadRegister>();
LOGMAN_THROW_A_FMT(Op->Class == GPRClass || Op->Class == FPRClass, "SRA classes");
if (Op->Class == FPRClass) {
return PhysicalRegister {FPRFixedClass, (uint8_t)Op->Reg};
} else {
return PhysicalRegister {GPRFixedClass, (uint8_t)Op->Reg};
}
}
};
@@ -204,8 +189,8 @@ private:
case OP_ALLOCATEGPRAFTER: return true;
case OP_ALLOCATEFPR: return true;
case OP_RMWHANDLE: return PhysicalRegister(Node) == PhysicalRegister(Header->Args[0]);
case OP_LOADREGISTER: return PhysicalRegister(Node) == DecodeSRAReg(Header);
case OP_STOREREGISTER: return PhysicalRegister(Header->Args[0]) == DecodeSRAReg(Header);
case OP_LOADREGISTER: return PhysicalRegister(Node) == DecodeSRAReg(Header, Node);
case OP_STOREREGISTER: return PhysicalRegister(Header->Args[0]) == DecodeSRAReg(Header, Node);
default: return false;
}
}
@@ -378,37 +363,128 @@ private:
};
void ConstrainedRAPass::AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) {
LOGMAN_THROW_A_FMT(RegisterCount <= INVALID_REG, "Up to {} regs supported", INVALID_REG);
LOGMAN_THROW_A_FMT(RegisterCount <= 31, "Up to 31 regs supported");
Classes[Class].Count = RegisterCount;
}
inline bool KillMove(IROp_Header* LastOp, IROp_Header* IROp, Ref LastNode, Ref CodeNode) {
// 32-bit moves in x86_64 are represented as a Bfe, detect them.
if (LastOp->Op == OP_BFE && LastOp->C<IR::IROp_Bfe>()->lsb == 0 && LastOp->C<IR::IROp_Bfe>()->Width == 32) {
auto Op = IROp->Op;
if (Op == OP_AND) {
// Rewrite "mov wA, wB; and xA, xA, xC" into "and wA, wB, wC", since
// ((b & 0xffffffff) & c) == (b & c) & 0xffffffff.
IROp->Size = OpSize::i32Bit;
return true;
} else if (IROp->Size == OpSize::i32Bit) {
return Op == OP_OR || Op == OP_XOR || Op == OP_AND || Op == OP_SUB || Op == OP_LSHL || Op == OP_LSHR || Op == OP_ASHR;
}
}
return LastOp->Op == OP_STOREREGISTER;
}
inline bool IsSignext(const IROp_Header* IROp, OrderedNodeWrapper Src, OpSize Size) {
if (IROp->Op == OP_SBFE) {
auto Sbfe = IROp->C<IR::IROp_Sbfe>();
return Sbfe->Width == 1 && Sbfe->lsb == (IR::OpSizeAsBits(Size) - 1) && Sbfe->Src == Src;
} else {
return false;
}
}
inline bool IsZero(const IROp_Header* IROp) {
return IROp->Op == OP_CONSTANT && IROp->C<IROp_Constant>()->Constant == 0;
}
bool ConstrainedRAPass::TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp) {
if (IROp->Op == OP_PUSH) {
auto LastOp = IR->GetOp<IROp_Header>(LastNode);
if (LastOp->Op == OP_PUSH) {
auto SP = PhysicalRegister(CodeNode);
auto Push = IR->GetOp<IROp_Push>(CodeNode);
auto LastPush = IR->GetOp<IROp_Push>(LastNode);
auto LastOp = IR->GetOp<IROp_Header>(LastNode);
if (LastOp->Size == IROp->Size && LastPush->ValueSize == Push->ValueSize && SP == PhysicalRegister(LastNode) &&
SP == PhysicalRegister(IROp->Args[1]) && SP == PhysicalRegister(LastOp->Args[1]) && SP != PhysicalRegister(IROp->Args[0]) &&
SP != PhysicalRegister(LastOp->Args[0]) && Push->ValueSize >= OpSize::i32Bit) {
if (IROp->Op == OP_PUSH && LastOp->Op == OP_PUSH) {
auto SP = PhysicalRegister(CodeNode);
auto Push = IR->GetOp<IROp_Push>(CodeNode);
auto LastPush = IR->GetOp<IROp_Push>(LastNode);
IREmit->SetWriteCursorBefore(LastNode);
IREmit->_PushTwo(IROp->Size, Push->ValueSize, IROp->Args[0], LastOp->Args[0], IROp->Args[1]);
return true;
}
if (LastOp->Size == IROp->Size && LastPush->ValueSize == Push->ValueSize && SP == PhysicalRegister(LastNode) &&
SP == PhysicalRegister(IROp->Args[1]) && SP == PhysicalRegister(LastOp->Args[1]) && SP != PhysicalRegister(IROp->Args[0]) &&
SP != PhysicalRegister(LastOp->Args[0]) && Push->ValueSize >= OpSize::i32Bit) {
IREmit->SetWriteCursorBefore(LastNode);
IREmit->_PushTwo(IROp->Size, Push->ValueSize, IROp->Args[0], LastOp->Args[0], IROp->Args[1]);
IREmit->RemovePostRA(CodeNode);
return true;
}
} else if (IROp->Op == OP_POP) {
auto LastOp = IR->GetOp<IROp_Header>(LastNode);
auto SP = PhysicalRegister(IROp->Args[0]);
if (LastOp->Op == OP_POP && LastOp->Size == IROp->Size && IROp->Size >= OpSize::i32Bit && SP == PhysicalRegister(LastOp->Args[0])) {
IREmit->SetWriteCursorBefore(LastNode);
IREmit->_PopTwo(IROp->Size, IROp->Args[0], LastOp->Args[1], IROp->Args[1]);
IREmit->RemovePostRA(CodeNode);
return true;
}
} else if ((IROp->Op == OP_DIV || IROp->Op == OP_UDIV) && IROp->Size >= OpSize::i32Bit) {
// If Upper came from a sign/zero extension, we only need a 64-bit division.
auto Op = IROp->CW<IR::IROp_Div>();
if (!Op->Upper.IsInvalid() && PhysicalRegister(Op->Upper) == PhysicalRegister(LastNode)) {
if (IROp->Op == OP_DIV ? IsSignext(LastOp, Op->Lower, IROp->Size) : IsZero(LastOp)) {
Op->Upper.SetInvalid();
return PhysicalRegister(LastNode) == PhysicalRegister(Op->OutRemainder);
}
}
} else if (IROp->Op == OP_XGETBV && PhysicalRegister(IROp->Args[0]) == PhysicalRegister(LastNode) && LastOp->Op == OP_CONSTANT) {
// Try to constant fold
uint64_t ConstantFunction = LastOp->C<IROp_Constant>()->Constant;
auto Op = IROp->CW<IR::IROp_XGetBV>();
if (CPUID->DoesXCRFunctionReportConstantData(ConstantFunction)) {
const auto Result = CPUID->RunXCRFunction(ConstantFunction);
IREmit->SetWriteCursorBefore(CodeNode);
IREmit->_Constant(Result.eax).Node->Reg = PhysicalRegister(Op->OutEAX).Raw;
IREmit->_Constant(Result.edx).Node->Reg = PhysicalRegister(Op->OutEDX).Raw;
IREmit->RemovePostRA(CodeNode);
return false;
}
} else if (IROp->Op == OP_CPUID && PhysicalRegister(IROp->Args[0]) == PhysicalRegister(LastNode) && LastOp->Op == OP_CONSTANT) {
// Try to constant fold. As a limitation of merging only 2 instructions, we
// can only handle constant functions, not constant leafs. This could be
// lifted if we generalized at a (significant) complexity cost.
uint64_t ConstantFunction = LastOp->C<IROp_Constant>()->Constant;
auto Op = IROp->CW<IR::IROp_CPUID>();
const auto SupportsConstant = CPUID->DoesFunctionReportConstantData(ConstantFunction);
if (SupportsConstant.SupportsConstantFunction == CPUIDEmu::SupportsConstant::CONSTANT &&
SupportsConstant.NeedsLeaf != CPUIDEmu::NeedsLeafConstant::NEEDSLEAFCONSTANT) {
const auto Result = CPUID->RunFunction(ConstantFunction, 0 /* leaf */);
IREmit->SetWriteCursorBefore(CodeNode);
IREmit->_Fence({FEXCore::IR::Fence_Inst});
IREmit->_Constant(Result.eax).Node->Reg = PhysicalRegister(Op->OutEAX).Raw;
IREmit->_Constant(Result.ebx).Node->Reg = PhysicalRegister(Op->OutEBX).Raw;
IREmit->_Constant(Result.ecx).Node->Reg = PhysicalRegister(Op->OutECX).Raw;
IREmit->_Constant(Result.edx).Node->Reg = PhysicalRegister(Op->OutEDX).Raw;
IREmit->RemovePostRA(CodeNode);
return false;
}
}
// Merge moves that are immediately consumed.
//
// x86 code inserts such moves to workaround x86's 2-address code. Because
// arm64 is 3-address code, we can optimize these out.
//
// Note we rely on the short-circuiting here.
if (PhysicalRegister(LastNode) == PhysicalRegister(CodeNode) && KillMove(LastOp, IROp, LastNode, CodeNode)) {
LOGMAN_THROW_A_FMT(!PhysicalRegister(CodeNode).IsInvalid(), "invariant");
for (auto s = 0; s < IR::GetRAArgs(IROp->Op); ++s) {
if (IROp->Args[s].IsImmediate() && PhysicalRegister(IROp->Args[s]) == PhysicalRegister(LastNode)) {
IROp->Args[s].SetImmediate(PhysicalRegister(LastOp->Args[0]).Raw);
}
}
return true;
}
return false;
@@ -473,7 +549,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
// each register, used below. Since we initialized Class->Available,
// RegToSSA is otherwise undefined so we can stash our temps there.
if (auto Node = DecodeSRANode(IROp, CodeNode); Node != nullptr) {
auto Reg = DecodeSRAReg(IROp);
auto Reg = DecodeSRAReg(IROp, CodeNode);
PreferredReg[IR->GetID(Node).Value] = Reg;
GetClass(Reg)->RegToSSA[Reg.Reg] = CodeNode;
@@ -519,23 +595,29 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
// Forward pass: Assign registers, spilling & optimizing as we go.
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
// GuestOpcode does not read or write registers, and must be skipped for
// push/pop merging. Since we'd be doing this check anyway for merging, do
// the check now so we can skip the rest of the logic too.
if (IROp->Op == OP_GUESTOPCODE) {
// These do not read or write registers, and must be skipped for merging.
// Since we'd be doing this check anyway for merging, do the check now so
// we can skip the rest of the logic too.
if (IROp->Op == OP_GUESTOPCODE || IROp->Op == OP_INLINECONSTANT) {
continue;
}
// Static registers must be consistent at SRA load/store. Evict to ensure.
if (auto Node = DecodeSRANode(IROp, CodeNode); Node != nullptr) {
auto Reg = DecodeSRAReg(IROp);
auto Reg = DecodeSRAReg(IROp, CodeNode);
RegisterClass* Class = &Classes[Reg.Class];
if (!(Class->Available & (1u << Reg.Reg))) {
Ref Old = Class->RegToSSA[Reg.Reg];
if (Old != Node) {
// Before inserting instructions, we need to set the cursor and
// reset LastNode so we don't merge across an inserted copy.
// Otherwise, we would erroneously miss the copy when determining if
// we can merge, and end up unsoundly merging a mov+xchg sequence.
IREmit->SetWriteCursorBefore(CodeNode);
LastNode = nullptr;
Ref Copy;
if (Reg.Class == FPRFixedClass) {
@@ -566,6 +648,8 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
if (!IsInRegisterFile(Old)) {
IREmit->SetWriteCursorBefore(CodeNode);
LastNode = nullptr;
Ref Fill = InsertFill(Old);
AssignReg(IR->GetOp<IROp_Header>(Fill), Fill, IROp);
@@ -599,7 +683,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
}
// Assign destinations.
if (GetHasDest(IROp->Op)) {
if (GetHasDest(IROp->Op) && PhysicalRegister(CodeNode).IsInvalid()) {
AssignReg(IROp, CodeNode, IROp);
}
@@ -608,7 +692,6 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
IREmit->RemovePostRA(CodeNode);
} else if (LastNode && TryPostRAMerge(LastNode, CodeNode, IROp)) {
// Merge adjacent instructions
IREmit->RemovePostRA(CodeNode);
IREmit->RemovePostRA(LastNode);
LastNode = nullptr;
} else {
@@ -627,7 +710,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
IR->GetHeader()->PostRA = true;
}
fextl::unique_ptr<IR::RegisterAllocationPass> CreateRegisterAllocationPass() {
return fextl::make_unique<ConstrainedRAPass>();
fextl::unique_ptr<IR::RegisterAllocationPass> CreateRegisterAllocationPass(const FEXCore::CPUIDEmu* CPUID) {
return fextl::make_unique<ConstrainedRAPass>(CPUID);
}
} // namespace FEXCore::IR
@@ -161,6 +161,7 @@ private:
const FEXCore::HostFeatures& Features;
const OpSize GPROpSize;
bool ReducedPrecisionMode;
FEX_CONFIG_OPT(DisableVixlIndirectCalls, DISABLE_VIXL_INDIRECT_RUNTIME_CALLS);
// Helpers
Ref RotateRight8(uint32_t V, Ref Amount);
@@ -774,12 +775,26 @@ void X87StackOptimization::Run(IREmitter* Emit) {
Ref SinValue {};
Ref CosValue {};
if (ReducedPrecisionMode) {
SinValue = IREmit->_F64SIN(St0);
CosValue = IREmit->_F64COS(St0);
} else {
SinValue = IREmit->_F80SIN(St0);
CosValue = IREmit->_F80COS(St0);
#ifdef VIXL_SIMULATOR
if (DisableVixlIndirectCalls() == 0) {
if (ReducedPrecisionMode) {
SinValue = IREmit->_F64SIN(St0);
CosValue = IREmit->_F64COS(St0);
} else {
SinValue = IREmit->_F80SIN(St0);
CosValue = IREmit->_F80COS(St0);
}
} else
#endif
{
SinValue = IREmit->_AllocateFPR(OpSize::i128Bit, OpSize::i128Bit);
CosValue = IREmit->_AllocateFPR(OpSize::i128Bit, OpSize::i128Bit);
if (ReducedPrecisionMode) {
IREmit->_F64SINCOS(St0, SinValue, CosValue);
} else {
IREmit->_F80SINCOS(St0, SinValue, CosValue);
}
}
// Push values
@@ -31,11 +31,12 @@ union PhysicalRegister {
: Raw(Node->Reg) {}
static const PhysicalRegister Invalid() {
return PhysicalRegister(InvalidClass, InvalidReg);
return PhysicalRegister(InvalidClass, 0);
}
bool IsInvalid() const {
return *this == Invalid();
static_assert(InvalidClass == 0);
return Raw == 0;
}
};
+2
View File
@@ -209,6 +209,7 @@ enum FallbackHandlerIndex {
OPINDEX_F80SQRT,
OPINDEX_F80SIN,
OPINDEX_F80COS,
OPINDEX_F80SINCOS,
OPINDEX_F80XTRACT_EXP,
OPINDEX_F80XTRACT_SIG,
OPINDEX_F80BCDSTORE,
@@ -228,6 +229,7 @@ enum FallbackHandlerIndex {
// Double Precision
OPINDEX_F64SIN,
OPINDEX_F64COS,
OPINDEX_F64SINCOS,
OPINDEX_F64TAN,
OPINDEX_F64ATAN,
OPINDEX_F64F2XM1,
+15 -18
View File
@@ -6,6 +6,7 @@
#include <cstdarg>
#include <fmt/format.h>
#include <fmt/color.h>
namespace LogMan {
enum DebugLevels {
@@ -14,23 +15,29 @@ enum DebugLevels {
ERROR = 2, ///< Only Errors printed
DEBUG = 3, ///< Debug messages added
INFO = 4, ///< Info messages added
STDOUT = 5, ///< Meant to go to STDOUT
STDERR = 6, ///< Meant to go to STDERR
};
static inline const char* DebugLevelStr(uint32_t Level) {
switch (Level) {
case NONE: return "NONE";
case ASSERT: return "ASSERT";
case ERROR: return "ERROR";
case DEBUG: return "DEBUG";
case INFO: return "INFO";
case STDOUT: return "STDOUT";
case STDERR: return "STDERR";
case ASSERT: return "A";
case ERROR: return "E";
case DEBUG: return "D";
case INFO: return "I";
default: return "???"; break;
}
}
static inline fmt::text_style DebugLevelStyle(uint32_t Level) {
switch (Level) {
case LogMan::ASSERT: return fmt::bg(fmt::color::red) | fmt::emphasis::bold | fmt::fg(fmt::color::white);
case LogMan::ERROR: return fmt::fg(fmt::color::red);
case LogMan::DEBUG: return fmt::fg(fmt::color::gray);
case LogMan::INFO: return fmt::fg(fmt::color::green);
default: return {}; break;
}
}
constexpr DebugLevels MSG_LEVEL = INFO;
// Note that all logging functions with the Fmt or _FMT suffix on them expect
@@ -104,16 +111,6 @@ namespace Msg {
MFmtImpl(INFO, fmt, fmt::make_format_args(args...));
}
template<typename... Args>
static inline void OutFmt(const char* fmt, const Args&... args) {
MFmtImpl(STDOUT, fmt, fmt::make_format_args(args...));
}
template<typename... Args>
static inline void ErrFmt(const char* fmt, const Args&... args) {
MFmtImpl(STDERR, fmt, fmt::make_format_args(args...));
}
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
template<typename... Args>
static inline void AFmt(const char* fmt, const Args&... args) {
@@ -445,6 +445,10 @@ public:
: ThreadAllocator {Allocator}
, Size {Size} {}
~PoolBufferWithTimedRetirement() {
UnclaimBuffer();
}
/**
* @brief Return the owned buffer or allocate another one from the `Allocator`
*
+24 -33
View File
@@ -1,45 +1,36 @@
[中文](https://github.com/FEX-Emu/FEX/blob/main/docs/Readme_CN.md)
# FEX - Fast x86 emulation frontend
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.15+ interface to the guest, and supports only AArch64 as a host.
FEX is very much work in progress, so expect things to change.
# FEX: Emulate x86 Programs on ARM64
FEX allows you to run x86 applications on ARM64 Linux devices, similar to qemu-user and box64.
It offers broad compatibility with both 32-bit and 64-bit binaries, and it can be used alongside Wine/Proton to play Windows games.
It supports forwarding API calls to host system libraries like OpenGL or Vulkan to reduce emulation overhead.
An experimental code cache helps minimize in-game stuttering as much as possible.
Furthermore, a per-app configuration system allows tweaking performance per game, e.g. by skipping costly memory model emulation.
We also provide a user-friendly FEXConfig GUI to explore and change these settings.
## Quick start guide
## Prerequisites
FEX requires ARMv8.0+ hardware. It has been tested with the following Linux distributions, though others are likely to work as well:
- Arch Linux
- Fedora Linux
- openSUSE
- Ubuntu 22.04/24.04/24.10
An x86-64 RootFS is required and can be downloaded using our `FEXRootFSFetcher` tool for many distributions.
For other distributions you will need to generate your own RootFS (our [wiki page](https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS) might help).
## Quick Start
### For Ubuntu 22.04, 24.04 and 24.10
Execute the following command in the terminal to install FEX through a PPA.
`curl --silent https://raw.githubusercontent.com/FEX-Emu/FEX/main/Scripts/InstallFEX.py --output /tmp/InstallFEX.py && python3 /tmp/InstallFEX.py && rm /tmp/InstallFEX.py`
```sh
curl --silent https://raw.githubusercontent.com/FEX-Emu/FEX/main/Scripts/InstallFEX.py | python3
```
This command will walk you through installing FEX through a PPA, and downloading a RootFS for use with FEX.
Ubuntu PPA is updated with our monthly releases.
### For everyone else
Please see [Building FEX](#building-fex).
## Getting Started
FEX has been tested to build and run on ARMv8.0+ hardware.
ARMv7 hardware will not work.
Expected operating system usage is Linux. FEX has been tested with the following Linux OSes:
- Ubuntu 22.04
- Ubuntu 24.04
- Ubuntu 24.10
- Arch Linux
On AArch64 hosts the user **MUST** have an x86-64 RootFS [Creating a RootFS](#RootFS-Generation).
### For other Distributions
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX).
### Navigating the Source
See the [Source Outline](docs/SourceOutline.md) for more information.
### Building FEX
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX).
### RootFS generation
AArch64 hosts require a rootfs for running applications.
Follow the guide on the wiki page for seeing how to set up the rootfs from scratch
https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS
![FEX diagram](docs/Diagram.svg)
+12 -7
View File
@@ -186,7 +186,7 @@ void CodeSizeValidation::CalculateBaseStats(FEXCore::Context::Context* CTX, FEXC
SetupInfoDisabled = false;
}
static CodeSizeValidation Validation {};
static CodeSizeValidation* Validation {};
} // namespace CodeSize
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
@@ -194,19 +194,19 @@ void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
if (Level == LogMan::INFO) {
// Disassemble information is sent through the Info log level.
if (!CodeSize::Validation.ParseMessage(Message)) {
if (!CodeSize::Validation->ParseMessage(Message)) {
return;
}
if (CodeSize::Validation.InfoPrintingDisabled()) {
if (CodeSize::Validation->InfoPrintingDisabled()) {
return;
}
}
fextl::fmt::print("[{}] {}\n", CharLevel, Message);
fextl::fmt::print("{} {}\n", CharLevel, Message);
}
void AssertHandler(const char* Message) {
fextl::fmt::print("[ASSERT] {}\n", Message);
fextl::fmt::print("A {}\n", Message);
// make sure buffers are flushed
fflush(nullptr);
@@ -248,7 +248,7 @@ static bool TestInstructions(FEXCore::Context::Context* CTX, FEXCore::Core::Inte
LogMan::Msg::IFmt("Compiling instruction '{}'", CurrentTest->TestInst);
TestData[i] =
CodeSize::Validation.CompileAndGetStats(CTX, Thread, reinterpret_cast<void*>(CodeRIP), CurrentTest->CodeSize, CurrentTest->x86InstCount);
CodeSize::Validation->CompileAndGetStats(CTX, Thread, reinterpret_cast<void*>(CodeRIP), CurrentTest->CodeSize, CurrentTest->x86InstCount);
// Go to the next test.
CurrentTest = reinterpret_cast<const TestInfo*>(&CurrentTest->Code[CurrentTest->CodeSize]);
@@ -467,6 +467,11 @@ public:
int main(int argc, char** argv, char** const envp) {
FEXCore::Allocator::GLIBCScopedFault GLIBFaultScope;
// Initialize early as the message handlers use it.
CodeSize::CodeSizeValidation Validation {};
CodeSize::Validation = &Validation;
LogMan::Throw::InstallHandler(AssertHandler);
LogMan::Msg::InstallHandler(MsgHandler);
FEXCore::Config::Initialize();
@@ -659,7 +664,7 @@ int main(int argc, char** argv, char** const envp) {
auto ParentThread = CTX->CreateThread(0, 0);
// Calculate the base stats for instruction testing.
CodeSize::Validation.CalculateBaseStats(CTX.get(), ParentThread);
CodeSize::Validation->CalculateBaseStats(CTX.get(), ParentThread);
// Test all the instructions.
auto Result = TestInstructions(CTX.get(), ParentThread, argc >= 2 ? argv[2] : nullptr) ? 0 : 1;
+1 -5
View File
@@ -1,9 +1,5 @@
add_executable(FEXBash FEXBash.cpp)
target_include_directories(FEXBash
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_include_directories(FEXBash PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Source/)
target_link_libraries(FEXBash
PRIVATE
+12 -3
View File
@@ -6,7 +6,6 @@ desc: Launches bash under FEX and passes arguments via -c to it
$end_info$
*/
#include "ConfigDefines.h"
#include "Common/ArgumentLoader.h"
#include <FEXCore/Config/Config.h>
@@ -25,11 +24,21 @@ int main(int argc, char** argv, char** const envp) {
// Use /bin/sh for -c commands and /bin/bash for interactive mode
const char* BashPath = Args.empty() ? "/bin/bash" : "/bin/sh";
std::string FEXInterpreterPath = std::filesystem::path(argv[0]).parent_path().string() + "FEXInterpreter";
std::string FEXInterpreterPath = std::filesystem::path(argv[0]).parent_path().string() + "/FEXInterpreter";
// Check if a local FEXInterpreter to FEXBash exists
// If it does then it takes priority over the installed one
if (!std::filesystem::exists(FEXInterpreterPath)) {
FEXInterpreterPath = FEXCore::Config::FindContainerPrefix() + FEXINTERPRETER_PATH;
char FEXBashPath[PATH_MAX];
auto Result = readlink("/proc/self/exe", FEXBashPath, PATH_MAX);
if (Result != -1) {
FEXInterpreterPath = std::filesystem::path(&FEXBashPath[0], &FEXBashPath[Result]).parent_path().string() + "/FEXInterpreter";
}
if (!std::filesystem::exists(FEXInterpreterPath)) {
fmt::print(stderr, "Could not locate FEXInterpreter executable\n");
std::abort();
}
}
const char* FEXArgs[] = {
FEXInterpreterPath.c_str(),
+1 -1
View File
@@ -446,6 +446,6 @@ int main(int Argc, char** Argv) {
}
ConfigRuntime Runtime(ConfigFilename.c_str());
App.setWindowIcon(QIcon(":/icon.png"));
return App.exec();
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+1
View File
@@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>icon.png</file>
</qresource>
<qresource prefix="/dialogs">
<file alias="FileDialog.qml">qt5/FileDialog.qml</file>
+1
View File
@@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>icon.png</file>
</qresource>
<qresource prefix="/dialogs">
<file alias="FileDialog.qml">qt6/FileDialog.qml</file>
+7 -2
View File
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: MIT
#include "ConfigDefines.h"
#include "Common/cpp-optparse/OptionParser.h"
#include "Common/Config.h"
#include "Common/FEXServerClient.h"
@@ -155,7 +154,13 @@ int main(int argc, char** argv, char** envp) {
}
if (Options.is_set_by_user("install_prefix")) {
fprintf(stdout, FEX_INSTALL_PREFIX "\n");
char SelfPath[PATH_MAX];
auto Result = readlink("/proc/self/exe", SelfPath, PATH_MAX);
if (Result == -1) {
Result = 0;
}
auto InstallPrefix = std::filesystem::path(&SelfPath[0], &SelfPath[Result]).parent_path().parent_path().string();
fprintf(stdout, "%s\n", InstallPrefix.c_str());
}
if (Options.is_set_by_user("current_rootfs")) {
+18 -11
View File
@@ -68,24 +68,22 @@ namespace {
static bool SilentLog {};
static int OutputFD {STDERR_FILENO};
// Set an empty style to disable coloring when FEXServer output is e.g. piped to a file
static bool DisableOutputColors {};
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
if (SilentLog) {
return;
}
const auto Output = fextl::fmt::format("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
const auto Style = DisableOutputColors ? fmt::text_style {} : LogMan::DebugLevelStyle(Level);
const auto Output = fextl::fmt::format("{} {}\n", fmt::styled(LogMan::DebugLevelStr(Level), Style), Message);
write(OutputFD, Output.c_str(), Output.size());
fsync(OutputFD);
}
void AssertHandler(const char* Message) {
if (SilentLog) {
return;
}
const auto Output = fextl::fmt::format("[ASSERT] {}\n", Message);
write(OutputFD, Output.c_str(), Output.size());
fsync(OutputFD);
return MsgHandler(LogMan::ASSERT, Message);
}
} // Anonymous namespace
@@ -368,10 +366,11 @@ int main(int argc, char** argv, char** const envp) {
//
// We want to maintain the original output location otherwise we
// can run in to problems of writing to some file
auto LogFD = OutputFD;
if (LogFile == "stderr") {
OutputFD = dup(STDERR_FILENO);
LogFD = dup(STDERR_FILENO);
} else if (LogFile == "stdout") {
OutputFD = dup(STDOUT_FILENO);
LogFD = dup(STDOUT_FILENO);
} else if (LogFile == "server") {
FEXServerLogging::FEXServerFD = FEXServerClient::RequestLogFD(FEXServerClient::GetServerFD());
if (FEXServerLogging::FEXServerFD != -1) {
@@ -380,9 +379,17 @@ int main(int argc, char** argv, char** const envp) {
}
} else if (!LogFile.empty()) {
constexpr int USER_PERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
OutputFD = open(LogFile.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY, USER_PERMS);
LogFD = open(LogFile.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY, USER_PERMS);
}
if (LogFD == -1) {
LogMan::Msg::EFmt("Couldn't open log file. Going Silent.");
SilentLog = true;
} else {
OutputFD = LogFD;
}
}
DisableOutputColors = !isatty(OutputFD);
if (StartupSleep() && (StartupSleepProcName().empty() || Program.ProgramName == StartupSleepProcName())) {
LogMan::Msg::IFmt("[{}][{}] Sleeping for {} seconds", ::getpid(), Program.ProgramName, StartupSleep());
+25 -5
View File
@@ -9,6 +9,8 @@
#include "Common/Config.h"
#include "Common/FEXServerClient.h"
#include <fmt/color.h>
#include <chrono>
#include <dirent.h>
#include <fcntl.h>
@@ -23,23 +25,38 @@
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <termios.h>
#include <thread>
#include <unistd.h>
static timespec StartTime {};
// Set an empty style to disable coloring when FEXServer output is e.g. piped to a file
static std::optional<fmt::text_style> DisableColors = isatty(STDOUT_FILENO) ? std::nullopt : std::optional {fmt::text_style {}};
namespace Logging {
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
const auto Output = fmt::format("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
const auto Output = fmt::format("{} {}\n", fmt::styled(LogMan::DebugLevelStr(Level), DisableColors.value_or(DebugLevelStyle(Level))), Message);
write(STDOUT_FILENO, Output.c_str(), Output.size());
}
void AssertHandler(const char* Message) {
const auto Output = fmt::format("[ASSERT] {}\n", Message);
write(STDOUT_FILENO, Output.c_str(), Output.size());
return MsgHandler(LogMan::ASSERT, Message);
}
void ClientMsgHandler(int FD, FEXServerClient::Logging::PacketMsg* const Msg, const char* MsgStr) {
const auto Output = fmt::format("[{}][{}.{}][{}.{}] {}\n", LogMan::DebugLevelStr(Msg->Level), Msg->Header.Timestamp.tv_sec,
Msg->Header.Timestamp.tv_nsec, Msg->Header.PID, Msg->Header.TID, MsgStr);
if (!StartTime.tv_sec && !StartTime.tv_nsec) {
StartTime = Msg->Header.Timestamp;
}
auto seconds = Msg->Header.Timestamp.tv_sec - StartTime.tv_sec - (Msg->Header.Timestamp.tv_nsec < StartTime.tv_nsec);
auto nanos = (1'000'000'000 + Msg->Header.Timestamp.tv_nsec - StartTime.tv_nsec) % 1'000'000'000;
char Metadata[128];
auto Cursor =
fmt::format_to(&Metadata[0], DisableColors.value_or(LogMan::DebugLevelStyle(Msg->Level)), "{}", LogMan::DebugLevelStr(Msg->Level));
Cursor = fmt::format_to(Cursor, DisableColors.value_or(fmt::fg(fmt::color::light_gray)), " {}|{} ", Msg->Header.PID, Msg->Header.TID);
Cursor = fmt::format_to(Cursor, DisableColors.value_or(fmt::fg(fmt::color::gray)), "{}.{:03}", seconds, nanos / 1000000);
*Cursor = 0;
auto Output = fmt::format("{} {}\n", Metadata, MsgStr);
write(STDERR_FILENO, Output.c_str(), Output.size());
}
} // namespace Logging
@@ -50,6 +67,9 @@ void ActionHandler(int sig, siginfo_t* info, void* context) {
if (sig == SIGINT) {
// Someone trying to kill us. Shutdown.
ProcessPipe::Shutdown();
// Clear "^C" string that most terminals print when pressing Ctrl+C.
fprintf(stderr, "\r");
return;
}
_exit(1);
@@ -545,6 +545,19 @@ EmulatedFDManager::EmulatedFDManager(FEXCore::Context::Context* ctx)
return FD;
};
// Wine reads this to ensure TSC is trusted by the kernel. Otherwise it falls back to maximum clock speed of the CPU cores.
// Without this, games like Horizon Zero Dawn would run their physics in slow-motion.
FDReadCreators["/sys/devices/system/clocksource/clocksource0/current_clocksource"] =
[&](FEXCore::Context::Context* ctx, int32_t fd, const char* pathname, int32_t flags, mode_t mode) -> int32_t {
int FD = GenTmpFD(pathname, flags);
const char source[] = "tsc\n";
// + 1 to ensure null at the end
write(FD, source, strlen(source) + 1);
lseek(FD, 0, SEEK_SET);
SealTmpFD(FD);
return FD;
};
auto NumCPUCores = [&](FEXCore::Context::Context* ctx, int32_t fd, const char* pathname, int32_t flags, mode_t mode) -> int32_t {
int FD = GenTmpFD(pathname, flags);
write(FD, (void*)&cpus_online.at(0), cpus_online.size());
@@ -263,7 +263,13 @@ FileManager::FileManager(FEXCore::Context::Context* ctx)
}
// Now that we loaded the thunks object, walk through and ensure dependencies are enabled as well
const auto& ThunkGuestPath = Is64BitMode() ? ThunkGuestLibs() : ThunkGuestLibs32();
auto ThunkGuestPath = ThunkGuestLibs();
while (ThunkGuestPath.ends_with('/')) {
ThunkGuestPath.pop_back();
}
if (!Is64BitMode()) {
ThunkGuestPath += "_32";
}
for (const auto& DBObject : ThunkDB) {
if (!DBObject.second.Enabled) {
continue;
@@ -171,7 +171,6 @@ private:
FEX_CONFIG_OPT(Filename, APP_FILENAME);
FEX_CONFIG_OPT(LDPath, ROOTFS);
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
FEX_CONFIG_OPT(ThunkGuestLibs32, THUNKGUESTLIBS32);
FEX_CONFIG_OPT(ThunkConfig, THUNKCONFIG);
FEX_CONFIG_OPT(AppConfigName, APP_CONFIG_NAME);
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
@@ -82,7 +82,7 @@ uint64_t SetSignal(GuestSAMask* Set, int Signal) {
void SignalDelegator::HandleSignal(FEX::HLE::ThreadStateObject* Thread, int Signal, void* Info, void* UContext) {
// Let the host take first stab at handling the signal
if (!Thread) {
LogMan::Msg::AFmt("[{}] Thread has received a signal and hasn't registered itself with the delegate! Programming error!",
LogMan::Msg::AFmt("Thread {} has received a signal and hasn't registered itself with the delegate! Programming error!",
FHU::Syscalls::gettid());
} else {
SignalHandler& Handler = HostHandlers[Signal];
@@ -93,7 +93,7 @@ template<typename T>
static void SetXStateInfo(T* xstate, bool is_avx_enabled) {
auto* fpstate = &xstate->fpstate;
fpstate->sw_reserved.magic1 = FEXCore::x86_64::fpx_sw_bytes::FP_XSTATE_MAGIC;
fpstate->sw_reserved.magic1 = is_avx_enabled ? FEXCore::x86_64::fpx_sw_bytes::FP_XSTATE_MAGIC : 0;
fpstate->sw_reserved.extended_size = is_avx_enabled ? sizeof(T) : 0;
fpstate->sw_reserved.xfeatures |= FEXCore::x86_64::fpx_sw_bytes::FEATURE_FP | FEXCore::x86_64::fpx_sw_bytes::FEATURE_SSE;
@@ -25,6 +25,7 @@ $end_info$
#include <sys/utsname.h>
#include <sys/klog.h>
#include <sys/personality.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <git_version.h>
@@ -98,5 +99,24 @@ void RegisterInfo(FEX::HLE::SyscallHandler* Handler) {
[](FEXCore::Core::CpuStateFrame* Frame, unsigned int operation, unsigned int flags, void* args) -> uint64_t {
return FEX::HLE::_SyscallHandler->SeccompEmulator.Handle(Frame, operation, flags, args);
});
REGISTER_SYSCALL_IMPL(
ptrace, [](FEXCore::Core::CpuStateFrame* Frame, int /*enum __ptrace_request*/ request, pid_t pid, void* addr, void* data) -> uint64_t {
uint64_t Result {};
switch (request) {
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
case PTRACE_ATTACH:
case PTRACE_DETACH:
// Passthrough these requests. Allows Wine to run the Ubisoft launcher.
Result = ::syscall(SYSCALL_DEF(ptrace), request, pid, addr, data);
SYSCALL_ERRNO();
default: break;
}
// We don't support this
return -EPERM;
});
}
} // namespace FEX::HLE
@@ -27,13 +27,6 @@ struct CpuStateFrame;
namespace FEX::HLE {
void RegisterStubs(FEX::HLE::SyscallHandler* Handler) {
REGISTER_SYSCALL_IMPL(
ptrace, [](FEXCore::Core::CpuStateFrame* Frame, int /*enum __ptrace_request*/ request, pid_t pid, void* addr, void* data) -> uint64_t {
// We don't support this
return -EPERM;
});
REGISTER_SYSCALL_IMPL(modify_ldt, [](FEXCore::Core::CpuStateFrame* Frame, int func, void* ptr, unsigned long bytecount) -> uint64_t {
SYSCALL_STUB(modify_ldt);
});
+5 -2
View File
@@ -207,11 +207,14 @@ private:
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32);
};
void ThunkHandler_impl::LoadLib(std::string_view Name) {
auto SOName = (Is64BitMode() ? ThunkHostLibsPath() : ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so";
auto SOName = ThunkHostLibsPath();
while (SOName.ends_with('/')) {
SOName.pop_back();
}
SOName = fmt::format("{}{}/{}-host.so", SOName, (Is64BitMode() ? "" : "_32"), Name);
LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName);
@@ -656,8 +656,9 @@ void LoadGuestVDSOSymbols(bool Is64Bit, char* VDSOBase) {
void LoadUnique32BitSigreturn(VDSOMapping* Mapping, FEX::HLE::SyscallHandler* const Handler) {
// Hardcoded to one page for now
const auto PageSize = sysconf(_SC_PAGESIZE);
Mapping->OptionalMappingSize = PageSize > 0 ? PageSize : FEXCore::Utils::FEX_PAGE_SIZE;
auto PageSize = sysconf(_SC_PAGESIZE);
PageSize = PageSize > 0 ? PageSize : FEXCore::Utils::FEX_PAGE_SIZE;
Mapping->OptionalMappingSize = PageSize;
// First 64bit page
constexpr uintptr_t LOCATION_MAX = 0x1'0000'0000;
@@ -738,14 +739,11 @@ void UnloadVDSOMapping(const VDSOMapping& Mapping) {
VDSOMapping LoadVDSOThunks(bool Is64Bit, FEX::HLE::SyscallHandler* const Handler) {
VDSOMapping Mapping {};
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
FEX_CONFIG_OPT(ThunkGuestLibs32, THUNKGUESTLIBS32);
fextl::string ThunkGuestPath {};
if (Is64Bit) {
ThunkGuestPath = fextl::fmt::format("{}/libVDSO-guest.so", ThunkGuestLibs());
} else {
ThunkGuestPath = fextl::fmt::format("{}/libVDSO-guest.so", ThunkGuestLibs32());
fextl::string ThunkGuestPath = ThunkGuestLibs();
while (ThunkGuestPath.ends_with('/')) {
ThunkGuestPath.pop_back();
}
ThunkGuestPath = fextl::fmt::format("{}{}/libVDSO-guest.so", ThunkGuestPath, Is64Bit ? "" : "_32");
// Load VDSO if we can
int VDSOFD = ::open(ThunkGuestPath.c_str(), O_RDONLY);
@@ -49,11 +49,11 @@ $end_info$
#endif
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
fextl::fmt::print("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
fextl::fmt::print("{} {}\n", LogMan::DebugLevelStr(Level), Message);
}
void AssertHandler(const char* Message) {
fextl::fmt::print("[ASSERT] {}\n", Message);
fextl::fmt::print("A {}\n", Message);
// make sure buffers are flushed
fflush(nullptr);
+2 -2
View File
@@ -14,7 +14,7 @@ void (*WineDbgOut)(const char* Message);
FILE* LogFile;
static void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
const auto Output = fextl::fmt::format("[{}][{:X}] {}\n", LogMan::DebugLevelStr(Level), GetCurrentThreadId(), Message);
const auto Output = fextl::fmt::format("{} {:X} {}\n", LogMan::DebugLevelStr(Level), GetCurrentThreadId(), Message);
if (WineDbgOut) {
WineDbgOut(Output.c_str());
} else if (LogFile) {
@@ -23,7 +23,7 @@ static void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
}
static void AssertHandler(const char* Message) {
const auto Output = fextl::fmt::format("[ASSERT] {}\n", Message);
const auto Output = fextl::fmt::format("A {}\n", Message);
if (WineDbgOut) {
WineDbgOut(Output.c_str());
} else if (LogFile) {
+4
View File
@@ -806,8 +806,12 @@ void GenerateThunkLibsAction::OnAnalysisComplete(clang::ASTContext& context) {
bool GenerateThunkLibsActionFactory::runInvocation(std::shared_ptr<clang::CompilerInvocation> Invocation, clang::FileManager* Files,
std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
clang::DiagnosticConsumer* DiagConsumer) {
#if LLVM_VERSION_MAJOR >= 21
clang::CompilerInstance Compiler(std::move(Invocation), std::move(PCHContainerOps));
#else
clang::CompilerInstance Compiler(std::move(PCHContainerOps));
Compiler.setInvocation(std::move(Invocation));
#endif
Compiler.setFileManager(Files);
GenerateThunkLibsAction Action(libname, output_filenames, abi);
+2 -2
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.14)
project(guest-thunks)
include(${FEX_PROJECT_SOURCE_DIR}/CMakeFiles/version_to_variables.cmake)
include(${FEX_PROJECT_SOURCE_DIR}/Data/CMake/version_to_variables.cmake)
option(ENABLE_CLANG_THUNKS "Enable building thunks with clang" FALSE)
@@ -35,7 +35,7 @@ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${FEX_PROJECT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
"${FEX_PROJECT_SOURCE_DIR}/Data/CMake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
+1 -1
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.14)
project(host-thunks)
include(${FEX_PROJECT_SOURCE_DIR}/CMakeFiles/version_to_variables.cmake)
include(${FEX_PROJECT_SOURCE_DIR}/Data/CMake/version_to_variables.cmake)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20)
+36
View File
@@ -25,6 +25,17 @@ extern "C" const wl_interface wl_seat_interface {};
extern "C" const wl_interface wl_surface_interface {};
extern "C" const wl_interface wl_keyboard_interface {};
extern "C" const wl_interface wl_callback_interface {};
extern "C" const wl_interface wl_display_interface {};
extern "C" const wl_interface wl_data_offer_interface {};
extern "C" const wl_interface wl_data_source_interface {};
extern "C" const wl_interface wl_data_device_interface {};
extern "C" const wl_interface wl_data_device_manager_interface {};
extern "C" const wl_interface wl_shell_interface {};
extern "C" const wl_interface wl_shell_surface_interface {};
extern "C" const wl_interface wl_touch_interface {};
extern "C" const wl_interface wl_region_interface {};
extern "C" const wl_interface wl_subcompositor_interface {};
extern "C" const wl_interface wl_subsurface_interface {};
#include <algorithm>
#include <array>
@@ -124,6 +135,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
} else if (signature == "ii") {
// E.g. xdg_toplevel::configure_bounds
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'i'>(callback[i]);
} else if (signature == "iu") {
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'u'>(callback[i]);
} else if (signature == "iia") {
// E.g. xdg_toplevel::configure
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'i', 'a'>(callback[i]);
@@ -142,6 +155,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
} else if (signature == "uff") {
// E.g. wl_pointer_listener::motion
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'f', 'f'>(callback[i]);
} else if (signature == "uffff") {
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'f', 'f', 'f', 'f'>(callback[i]);
} else if (signature == "uhu") {
// E.g. wl_keyboard_listener::keymap
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'h', 'u'>(callback[i]);
@@ -154,6 +169,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
} else if (signature == "uiii") {
// E.g. wl_output_listener::mode
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'i', 'i', 'i'>(callback[i]);
} else if (signature == "uiiii") {
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'i', 'i', 'i', 'i'>(callback[i]);
} else if (signature == "uo") {
// E.g. wl_pointer_listener::leave
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o'>(callback[i]);
@@ -166,6 +183,10 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
} else if (signature == "uoffo") {
// E.g. wl_data_device_listener::enter
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o', 'f', 'f', 'o'>(callback[i]);
} else if (signature == "us") {
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 's'>(callback[i]);
} else if (signature == "uss") {
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 's', 's'>(callback[i]);
} else if (signature == "usu") {
// E.g. wl_registry::global
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 's', 'u'>(callback[i]);
@@ -181,6 +202,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
} else if (signature == "uuoiff") {
// E.g. wl_touch_listener::down
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'o', 'i', 'f', 'f'>(callback[i]);
} else if (signature == "uuou") {
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'o', 'u'>(callback[i]);
} else if (signature == "uuu") {
// E.g. zwp_linux_dmabuf_v1::modifier
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'u'>(callback[i]);
@@ -193,6 +216,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
} else if (signature == "s") {
// E.g. wl_seat::name
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s'>(callback[i]);
} else if (signature == "ss") {
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s', 's'>(callback[i]);
} else if (signature == "sii") {
// E.g. zwp_text_input_v3::preedit_string
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s', 'i', 'i'>(callback[i]);
@@ -335,6 +360,17 @@ void OnInit() {
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_surface_interface), "wl_surface_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_keyboard_interface), "wl_keyboard_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_callback_interface), "wl_callback_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_display_interface), "wl_display_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_offer_interface), "wl_data_offer_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_source_interface), "wl_data_source_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_device_interface), "wl_data_device_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_device_manager_interface), "wl_data_device_manager_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shell_interface), "wl_shell_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shell_surface_interface), "wl_shell_surface_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_touch_interface), "wl_touch_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_region_interface), "wl_region_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_subcompositor_interface), "wl_subcompositor_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_subsurface_interface), "wl_subsurface_interface");
}
// Would insert spaces around -
+21 -4
View File
@@ -213,7 +213,7 @@ static wl_proxy* fex_wl_proxy_marshal_array(wl_proxy* proxy, uint32_t opcode, gu
#endif
} else if (constructor && version && !flags) {
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_constructor_versioned(proxy, opcode, host_args.data(), interface, version.value());
} else if (constructor && version && !flags) {
} else if (constructor && !version && !flags) {
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_constructor(proxy, opcode, host_args.data(), interface);
} else {
fprintf(stderr, "Invalid configuration\n");
@@ -352,6 +352,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
} else if (signature == "ii") {
// E.g. xdg_toplevel::configure_bounds
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i'>(callback);
} else if (signature == "iu") {
WaylandFinalizeHostTrampolineForGuestListener<'i', 'u'>(callback);
} else if (signature == "iia") {
// E.g. xdg_toplevel::configure
FEX::HLE::FinalizeHostTrampolineForGuestFunction((FEX::HLE::HostToGuestTrampolinePtr*)callback,
@@ -371,6 +373,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
} else if (signature == "uff") {
// E.g. wl_pointer_listener::motion
WaylandFinalizeHostTrampolineForGuestListener<'u', 'f', 'f'>(callback);
} else if (signature == "uffff") {
WaylandFinalizeHostTrampolineForGuestListener<'u', 'f', 'f', 'f', 'f'>(callback);
} else if (signature == "uhu") {
// E.g. wl_keyboard_listener::keymap
WaylandFinalizeHostTrampolineForGuestListener<'u', 'h', 'u'>(callback);
@@ -383,6 +387,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
} else if (signature == "uiii") {
// E.g. wl_output_listener::mode
WaylandFinalizeHostTrampolineForGuestListener<'u', 'i', 'i', 'i'>(callback);
} else if (signature == "uiiii") {
WaylandFinalizeHostTrampolineForGuestListener<'u', 'i', 'i', 'i', 'i'>(callback);
} else if (signature == "uo") {
// E.g. wl_pointer_listener::leave
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o'>(callback);
@@ -396,6 +402,10 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
} else if (signature == "uoffo") {
// E.g. wl_data_device_listener::enter
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o', 'f', 'f', 'o'>(callback);
} else if (signature == "us") {
WaylandFinalizeHostTrampolineForGuestListener<'u', 's'>(callback);
} else if (signature == "uss") {
WaylandFinalizeHostTrampolineForGuestListener<'u', 's', 's'>(callback);
} else if (signature == "usu") {
// E.g. wl_registry::global
WaylandFinalizeHostTrampolineForGuestListener<'u', 's', 'u'>(callback);
@@ -411,6 +421,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
} else if (signature == "uuoiff") {
// E.g. wl_touch_listener::down
WaylandFinalizeHostTrampolineForGuestListener<'u', 'u', 'o', 'i', 'f', 'f'>(callback);
} else if (signature == "uuou") {
WaylandFinalizeHostTrampolineForGuestListener<'u', 'u', 'o', 'u'>(callback);
} else if (signature == "uuu") {
// E.g. zwp_linux_dmabuf_v1::modifier
WaylandFinalizeHostTrampolineForGuestListener<'u', 'u', 'u'>(callback);
@@ -426,6 +438,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
} else if (signature == "sii") {
// E.g. zwp_text_input_v3::preedit_string
WaylandFinalizeHostTrampolineForGuestListener<'s', 'i', 'i'>(callback);
} else if (signature == "ss") {
WaylandFinalizeHostTrampolineForGuestListener<'s', 's'>(callback);
} else {
fprintf(stderr, "TODO: Unknown wayland event signature descriptor %s\n", signature.data());
std::abort();
@@ -449,8 +463,11 @@ void fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(guest_layout
// them into the rodata section of the application itself instead of the
// library. To copy the host information to them on startup, we must
// temporarily disable write-protection on this data hence.
auto page_begin = reinterpret_cast<uintptr_t>(guest_interface_raw.force_get_host_pointer()) & ~uintptr_t {0xfff};
if (0 != mprotect((void*)page_begin, 0x1000, PROT_READ | PROT_WRITE)) {
// NOTE: This may span page boundaries, so up to 2 pages may need to be changed
const auto source_addr = reinterpret_cast<uintptr_t>(guest_interface_raw.force_get_host_pointer());
const auto page_begin = source_addr & ~uintptr_t {0xfff};
const auto remap_size = ((source_addr & 0xfff) + sizeof(*guest_interface_raw.force_get_host_pointer()) > 0x1000) ? 0x2000 : 0x1000;
if (0 != mprotect((void*)page_begin, remap_size, PROT_READ | PROT_WRITE)) {
fprintf(stderr, "ERROR: %s\n", strerror(errno));
std::abort();
}
@@ -476,7 +493,7 @@ void fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(guest_layout
#endif
// TODO: Disabled until we ensure the interface data is indeed stored in rodata
// mprotect((void*)page_begin, 0x1000, PROT_READ);
// mprotect((void*)page_begin, remap_size, PROT_READ);
}
void fexfn_impl_libwayland_client_fex_wl_get_method_signature(wl_proxy* proxy, uint32_t opcode, char* out) {
-3
View File
@@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="581px" height="551px" viewBox="-0.5 -0.5 581 551" content="&lt;mxfile host=&quot;www.draw.io&quot; modified=&quot;2019-10-12T15:22:42.933Z&quot; agent=&quot;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36&quot; etag=&quot;KXzHp2X2mUPny1tpjJrR&quot; version=&quot;12.1.0&quot; type=&quot;google&quot; pages=&quot;2&quot;&gt;&lt;diagram name=&quot;Page-1&quot; id=&quot;44bbcf24-548e-d532-59d3-359de5b44cbb&quot;&gt;7Vtbk6MoFP41eZwuxftj3zJbtTNbXZWpmu1HoiQybcRRcttfv2AgUSFpKxMTq+3OQ8sRAb9zvnMOCCPrcbH5msMs/k4ilIyAEW1G1tMIAGC4gP3jku1OYgLD3knmOY6E7CCY4P+QEBpCusQRKmoVKSEJxVldGJI0RSGtyWCek3W92owk9V4zOEeKYBLCRErvnIP8J45oLOSmGxxu/IXwPBad+8Dd3ZjC8G2ek2UqekxJinZ3FlA2I96yiGFE1hWR9TyyHnNC6O5qsXlECUdWYiafo1s50JH1ENNFwgomuyxvj488bLZ5mL1XjlJa7e5Yey6CyJ7B6cyEs6lrmV8sgcAKJkvZQ7PLdYwpmmQw5OU1s576GAqakzf0SBKSM0kJnRRKLfBqM5wkstIIWDOH/7icpFQYk2lrX0mAsEI5RZuKSLziV0QWiOZbVkUaMxCWK0zZtYUS1hWrMDxhLnHFIixD1ITCGOf7xg+osgsBbFuQnQGAbHttQTbdTkC2PyLIZh3kvX2+D7LdBcgqxv+wQKLgXMFUB3kDwPvxeHw/VvAf8bflf/xODDPewGIz59HrDuZhjJlvRtYdzLIEh5BikrKKrPRjW1ZN+cCYgNcUouL3kilDoymuENZGcp/gOWvliZLsMvqzZXAU+vNcVX+2b+jU53SgPtNSVIUiFkRFESVTsn4+CB7KkIgiocryNruWaDXJA/M9qFxnKI3ueVhn5WlCwredaIz5cJ9EhWr9X4jSrRDAJSVMRHIakzlJYfKNcJVoWMqsxDf4T6PWCBbxfvRHtVmQZR6iI4gJH8LebY7okTpCUxzJkzaRo4SZ6aqexFxUv0DjAt2Ecijwil3OaQnETjTNmxLWX6XeZFtQtNA0MCEzuuZE0j52U2dAUchY9cD/Sc5vi8m6G+dse3Xn7Fsa52xryN1J/JOR4pPbbbltteC22xtuW5fk9olHiwymehfAmqjePMMzDNtfuFZQ9xcGkFPW23iM4DYeg2Qovb7DYHrLt//y3u6AI8uvf+RAnBYORCaAPfAgmqWIoeobbTAV91xHlF/LsmF4ovyCcsxAR7kYcXc24l/aRMpHGfxwW6mQEZzSotLyCxdU0hkH1NyTXDU5mNuuxYPx7Yd2nj2qqyITlK9weNv5ZCHG0E3K6NZDgOtoUkZHEwCcTgKAMySHcB5/3TY+HvSCwEEjv3C9jglsukOyn0pAORFPPMbwWjwBMr5cJp60skezF/boN+0RdG2P3qc9Nu3Rdv3b2+PFc+Cz7NEDdXt0gq7t0R+UPR5yases25wBrm5z/cipXfPKLlD9nPvhc2rP6FNODT5z6nf567fgL7h4DnO2Sn2FU99JtExUSkkGhGSRkbSE5AGKT4ohK3K3d4JADcoFwYz9MTmjU4TZ440P0x1QyXLqVNp/mqxSydd8bfY7odKgphfnUUmmVqe5dPH56flLDsZgyOS4Xp/INKi50ZlkktsWT5LJ6g+ZpE1+fDJ5wX57Zz/oNKip3Zl0Am3oZPeHTmAwdAqCXiV6qh8bL9Ow3NL3Z9NWDmwQnDttPba1cMYGN6pvLdzzu5O03HMbzs/SbTXSzXG9LrYRApUoDNkETkkOe6u0sBzhqOWO0Evkf77dL62pO4juK1gB43nFX7aPukMr4WOvxTi/saZ0Y82p265fchKiouiltjI2tmsqyzTMnvnHQNHXDxTGKYN3vlVUxt6SNhNAzTmEquqESEk5mvvYFziKyvRTawsdaAI08gpPtlHRA9BtcANd5BWWuthQ83gDUoRj3lQRaoL3sCxwqvNgH0oLfiOMyPXV22ihw9Mfte3YPdwuflSbp3eIt5rB9udLhRzvqTNwV80QQsqrd3Iyzm5QC6gRXxfwgy4CvuWpAR9TzcKBcG5tPVpFVxUIgS3LomHzlK9TvOIFwJerAHL1QJdsBRrwL3Em8enn6wtKaOzOkmXwiv/+VVjZFzW+KNC/c/YTFtnuUPoMb8pE9QgmGuTawhRoDv/pzv6dscbCiocj6Lt9BIdT/tbz/w==&lt;/diagram&gt;&lt;diagram id=&quot;YYJ6otxqrv_eHtO8LE0p&quot; name=&quot;Page-2&quot;&gt;7V1bd6I6FP41Po4LCBf72LE607PsWl3LmTntI4VUOUXigdjq+fUnlEQuCUJVIF7qQ2UjsXx759uX7NAeGC7WP0J7OX9ALvR7muKue+Cup2maYmrkVyzZJBJVU/REMgs9l8pSwdT7D1KhQqUrz4VR7oMYIR97y7zQQUEAHZyT2WGIPvIfe0V+/luX9gxygqlj+7z0b8/F80Q6MJRU/hN6szn7ZlWhZxY2+zAVRHPbRR8ZERj1wDBECCfvFush9GP0GC6B930Ef/y5G6zGzsR+UAb43fiWDDb+yiXbWwhhgI87NEiGfrf9FcWL3iveMABDtApcGA+i9MD3j7mH4XRpO/HZD2IzRDbHC58cqeTtq+f7Q+Sj8PNaMBzHLyKnXwNDDNcFvVTclLpFmtgoRAuIww25bmugyTCb/OFHqmljQGXzjJY1nQptal2z7cgpguQNBfELgOrNAmqM4ld7gILaiBpNIWoIEDV9HGODyI1moTX/XSF24lv0SUW35APAXK7Tk+TdLP49Hj2xgcjflYyVnOE0RjDGebVEOERvkCkmQAEs6IqKbN+bBeTQITqBRP491phH2OmWnlh4rht/jdAO8pbSlMqNos55lVsijTelcPPoCifOhc2gVAigGb+EhjFEIbwaR3yJrheMQ2AdqtGmedxUM+yMYLMsRYRGGvYL+7hyFKQ0y+obeawsAVaaACuzKaws6d37jRjTzhAbcIgNH38rjAteQkYDRKry0n6/f3b8UKIhxg+VAZjeJjmoWrXFw8C9jROLGFnfjiLPyasHrj38lHn/HENIpnZydLemiH4ebNhBQP76p+xB5qr4ML3s82iTUwt0uRymDhWRu0Kr0IHVBIDtcAZ3jqeItZwN+wR6ZLIQ+jb23vM3IVIu/YZH5H16cnHQaYKCeSS3SS/KpjiFcaz8ONtAlY2TwMCN82lp25s+wPhEoaoMrmmQ90vqTdd+SW047zxGmmRI5plUUWqZi4uFUa7zGr92RLnK7eP9xcS6JUplE6PmvGiseqCKcp893Vfqsp4zZ6rcV+qxnnMOqxv3BWq6L/3qvQ73XjWShU68F5eCdp5WMZ6QOK9KtCmT+1I5zKabiDgAn0+jRotVPCNRwJ86P5dUoig6DACFogILMrPcBng9FknoaHpkpi4dTQxkK75o8rNE8hdJxBIazxJDEpDGc7x64nMT+vjAsFmpFWelyc9Kg8dNGzSFW42E6pLqHsxoKyPHajd5jRwrja/GouclZS1q3bQl00Zwtb69ra9GztxJQALyuGwbSyrWVZuL3BpeDTpC0S3R5c4J0S5k/HLQITW30WQ8QbYLw0upt5XpkzEQ6IPcj1mYMdyEKbsiaw1qU9YAagRZnTCNqkpGNUD+NqhEmfJQDajsc/oS1UyQ8xZhuBRVUC6De8oUfKLcI2uUs11MlIZ75A9zgGRhDjhumPMLRvinHQYwii6Yfs4q9GFLMNmMnuTPU3qIQjxHMxTY/iiVFqBMPzNBaEl1+g/EeEM3DdgrjA4vCvQaTu612i01Jf6ndtZ+mMLUU1GYrFUcFsM2r2hxWUXVCyXmlusquqCId7WgL1nQiVCFrGltceFfN3iv2O7ylN5wXnuEJT29Oq9tGTM+sR17Pow2JD1dEHlmsf/MQsAyVci6tK+bXTL+OSw71iV8vaRhrSXCl7XTS7spbqHRTX5OiLYfNjcnRKmxZIxfvYbdLmT8Dq37oE7/xomx++7GLc3sm7kfPc/1ggS/5IpWEnwWWn15DT8iZId3L+2XhOdqBVmLUwFlp2NomuAZHVz7SlrIQA2+GUxaP3XTsZ8yNOn9lFECameQ8XnvX/e/zs5PlcF+on6qRgYuCScYWtecUGM/YtecUL1zp13I+GXWyeTPw/mRQgnuJ0oKNZK02g2o+5QhtqFsK2WIvevJLUWf3IMwuP3+ewagZtsBqLTPGtFk29HNBpa468KQ7Vkjpii/2b/tYvj4e2w7GMV/2aV2XZTpuPbj4oTPNmvMADotuJ+Ap6usx5h1t1qYJVst2qm4mzUClG72KBS690RTotVWNFP+J4MkypSne888bufw2Pb9F9t5u1wnUqbgktY9Kx+KSda5Z8oawxapRxTDtjuPNPmppzqEbRey4z6TKC60XCzpVESuJ0U6Vrfdp+cQ2FLukTywtWQtxxc23wo6B1slSkt+32JVl+LbhUyUCezvW6abxQuKv/TuYlxKmUZPcwuKZXTqU86gH52RUPVzKUq6Vw71KeQw/XcZSZE+/a8jYPQ/&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><Line truncated

Before

Width:  |  Height:  |  Size: 23 KiB

-2
View File
@@ -69,5 +69,3 @@ https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX
### 创建RootFS
AArch64 host端需要一个rootfs去运行guest程序。参考以下维基页面从头开始创建一个rootfs
https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS
![FEX diagram](Diagram.svg)
+1 -1
View File
@@ -1,4 +1,4 @@
# FEX-2506
# FEX-2507.1
## FEXCore
See [FEXCore/Readme.md](../FEXCore/Readme.md) for more details
-4
View File
@@ -1,4 +0,0 @@
#pragma once
#define FEX_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
#define FEXINTERPRETER_PATH FEX_INSTALL_PREFIX "/bin/FEXInterpreter"
+31
View File
@@ -0,0 +1,31 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0xaaaa",
"RBX": "0xaaaa",
"RSI": "0xbbbb"
}
}
%endif
; FEX had a bug with mov+xchg back-to-back due to failing to account for a copy
; inserted during RA. This resulted in a hang starting the game Hades due to the
; mov+xchg code sequence found within Wine's x64 build of ucrtbase.dll.
mov rax, 0xaaaa
mov rbx, 0xbbbb
mov rsi, 0xcccc
; step 1
mov rsi,rax
; rax = 0xaaaa
; rbx = 0xbbbb
; rsi = 0xaaaa
; step 2
xchg rbx,rsi
; rax = 0xaaaa
; rbx = 0xaaaa
; rsi = 0xbbbb
hlt
+1 -1
View File
@@ -45,7 +45,7 @@ function(AddTests Tests BinDirectory Bitness)
continue()
endif()
set(THUNK_ARGS "-k" "${CMAKE_SOURCE_DIR}/CI/FEXLinuxTestsThunks.json")
set(THUNK_ARGS "-k" "${CMAKE_SOURCE_DIR}/Data/CI/FEXLinuxTestsThunks.json")
endif()
# Add jit test case
@@ -0,0 +1,2 @@
# Passes on host but now FEX.
noexec_protect.64
+3
View File
@@ -23,3 +23,6 @@ sigtest_sigmask.64
# Disabled since FEX's FaultSafeMemcpy is intentionally stub-implemented
syscalls_efault.32
syscalls_efault.64
# FEX doesn't support no-exec
noexec_protect.64
@@ -0,0 +1,97 @@
#include <catch2/catch_test_macros.hpp>
#include <signal.h>
#include <sys/mman.h>
#include <stdint.h>
#include <cstdlib>
#include <csetjmp>
bool Caught = false;
static jmp_buf LongJump {};
static void SIGSEGV_Handler(int signal, siginfo_t* siginfo, void* context) {
// Needs to be an access error.
REQUIRE(siginfo->si_code == SEGV_ACCERR);
Caught = true;
longjmp(LongJump, 1);
}
class SimpleX86Emit final {
public:
enum Reg {
RAX = 0,
RCX = 1,
RDX = 2,
RBX = 3,
RSP = 4,
RBP = 5,
RSI = 6,
RDI = 7,
// r8 and higher not implemented.
};
SimpleX86Emit(void* Ptr)
: Ptr {static_cast<uint8_t*>(Ptr)} {}
void ret() {
db<uint8_t>(0xc3);
}
void mov(Reg reg, uint32_t val) {
db<uint8_t>(0xB8 + reg);
db(val);
}
private:
uint8_t* Ptr;
template<typename T>
void db(T v) {
static_assert(sizeof(uint32_t) == 4);
for (size_t i = 0; i < sizeof(T); ++i) {
*Ptr = v >> (i * 8);
++Ptr;
}
}
};
TEST_CASE("Signals: Test No-Exec") {
struct sigaction act {};
act.sa_sigaction = SIGSEGV_Handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
auto PageSize = sysconf(_SC_PAGESIZE);
PageSize = PageSize > 0 ? PageSize : 0x1000;
void* Ptr = mmap(nullptr, PageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
REQUIRE(Ptr != MAP_FAILED);
SimpleX86Emit emit(Ptr);
emit.mov(SimpleX86Emit::Reg::RAX, 1);
emit.ret();
using func_ptr = uint32_t (*)();
func_ptr func = reinterpret_cast<func_ptr>(Ptr);
// First time should execute fine.
Caught = false;
if (setjmp(LongJump) == 0) {
int res = func();
REQUIRE(res == 1);
} else {
REQUIRE(Caught == false);
}
// Protect as non-executable
REQUIRE(mprotect(Ptr, PageSize, PROT_READ | PROT_WRITE) == 0);
// This should now fail to execute due to No-Exec.
Caught = false;
if (setjmp(LongJump) == 0) {
int res = func();
// Shouldn't get reached.
REQUIRE(res == 1);
REQUIRE(false);
} else {
REQUIRE(Caught == true);
}
}
+30 -30
View File
@@ -19,7 +19,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -33,7 +33,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -47,7 +47,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -61,7 +61,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -75,7 +75,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -89,7 +89,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -103,7 +103,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -117,7 +117,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -131,7 +131,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -145,7 +145,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -159,7 +159,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -173,7 +173,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -187,7 +187,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -201,7 +201,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -215,7 +215,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -229,7 +229,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -243,7 +243,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -257,7 +257,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -271,7 +271,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -285,7 +285,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -299,7 +299,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -313,7 +313,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -327,7 +327,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -341,7 +341,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -355,7 +355,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -369,7 +369,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -383,7 +383,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -397,7 +397,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -411,7 +411,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
},
@@ -425,7 +425,7 @@
"str x20, [x28, #24]",
"mov w1, #0x401",
"str x1, [x28, #1328]",
"ldr x0, [x28, #2680]",
"ldr x0, [x28, #2712]",
"br x0"
]
}
@@ -485,7 +485,7 @@
],
"ExpectedArm64ASM": [
"ushr v2.4s, v16.4s, #31",
"ldr q3, [x28, #2896]",
"ldr q3, [x28, #2928]",
"ushl v2.4s, v2.4s, v3.4s",
"addv s2, v2.4s",
"mov w4, v2.s[0]"
@@ -499,7 +499,7 @@
"ExpectedArm64ASM": [
"ldr q2, [x28, #32]",
"ushr v3.4s, v16.4s, #31",
"ldr q4, [x28, #2896]",
"ldr q4, [x28, #2928]",
"ushl v3.4s, v3.4s, v4.4s",
"addv s3, v3.4s",
"mov w20, v3.s[0]",
@@ -1122,7 +1122,7 @@
"Map 1 0b01 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q2, [x0, #16]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1134,7 +1134,7 @@
"Map 1 0b01 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q2, [x0, #32]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1146,7 +1146,7 @@
"Map 1 0b01 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q2, [x0, #48]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1171,7 +1171,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q3, [x0, #16]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1185,7 +1185,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q3, [x0, #32]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1199,7 +1199,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q3, [x0, #48]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1223,7 +1223,7 @@
"Map 1 0b10 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2432]",
"ldr x0, [x28, #2464]",
"ldr q2, [x0, #16]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1235,7 +1235,7 @@
"Map 1 0b10 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2432]",
"ldr x0, [x28, #2464]",
"ldr q2, [x0, #32]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1247,7 +1247,7 @@
"Map 1 0b10 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2432]",
"ldr x0, [x28, #2464]",
"ldr q2, [x0, #48]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1274,7 +1274,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2432]",
"ldr x0, [x28, #2464]",
"ldr q3, [x0, #16]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1288,7 +1288,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2432]",
"ldr x0, [x28, #2464]",
"ldr q3, [x0, #32]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1302,7 +1302,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2432]",
"ldr x0, [x28, #2464]",
"ldr q3, [x0, #48]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1326,7 +1326,7 @@
"Map 1 0b11 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2424]",
"ldr x0, [x28, #2456]",
"ldr q2, [x0, #16]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1338,7 +1338,7 @@
"Map 1 0b11 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2424]",
"ldr x0, [x28, #2456]",
"ldr q2, [x0, #32]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1350,7 +1350,7 @@
"Map 1 0b11 0x70 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2424]",
"ldr x0, [x28, #2456]",
"ldr q2, [x0, #48]",
"tbl v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -1377,7 +1377,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2424]",
"ldr x0, [x28, #2456]",
"ldr q3, [x0, #16]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1391,7 +1391,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2424]",
"ldr x0, [x28, #2456]",
"ldr q3, [x0, #32]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -1405,7 +1405,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr x0, [x28, #2424]",
"ldr x0, [x28, #2456]",
"ldr q3, [x0, #48]",
"tbl v16.16b, {v17.16b}, v3.16b",
"tbl v2.16b, {v2.16b}, v3.16b",
@@ -2288,7 +2288,7 @@
"Map 1 0b00 0xC6 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2448]",
"ldr x0, [x28, #2480]",
"ldr q2, [x0, #16]",
"tbl v16.16b, {v17.16b, v18.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -2302,7 +2302,7 @@
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr q3, [x28, #64]",
"ldr x0, [x28, #2448]",
"ldr x0, [x28, #2480]",
"ldr q4, [x0, #16]",
"tbl v16.16b, {v17.16b, v18.16b}, v4.16b",
"tbl v2.16b, {v2.16b, v3.16b}, v4.16b",
@@ -2315,7 +2315,7 @@
"Map 1 0b00 0xC6 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2448]",
"ldr x0, [x28, #2480]",
"ldr q2, [x0, #32]",
"tbl v16.16b, {v17.16b, v18.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -2329,7 +2329,7 @@
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr q3, [x28, #64]",
"ldr x0, [x28, #2448]",
"ldr x0, [x28, #2480]",
"ldr q4, [x0, #32]",
"tbl v16.16b, {v17.16b, v18.16b}, v4.16b",
"tbl v2.16b, {v2.16b, v3.16b}, v4.16b",
@@ -2342,7 +2342,7 @@
"Map 1 0b00 0xC6 128-bit"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2448]",
"ldr x0, [x28, #2480]",
"ldr q2, [x0, #48]",
"tbl v16.16b, {v17.16b, v18.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
@@ -2356,7 +2356,7 @@
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr q3, [x28, #64]",
"ldr x0, [x28, #2448]",
"ldr x0, [x28, #2480]",
"ldr q4, [x0, #48]",
"tbl v16.16b, {v17.16b, v18.16b}, v4.16b",
"tbl v2.16b, {v2.16b, v3.16b}, v4.16b",
@@ -3763,7 +3763,7 @@
"Map 1 0b01 0xd0 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2800]",
"ldr q2, [x28, #2832]",
"eor v2.16b, v18.16b, v2.16b",
"fadd v16.2d, v17.2d, v2.2d",
"stp xzr, xzr, [x28, #32]"
@@ -3777,7 +3777,7 @@
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr q3, [x28, #64]",
"ldr q4, [x28, #2800]",
"ldr q4, [x28, #2832]",
"eor v5.16b, v18.16b, v4.16b",
"fadd v16.2d, v17.2d, v5.2d",
"eor v3.16b, v3.16b, v4.16b",
@@ -3791,7 +3791,7 @@
"Map 1 0b11 0xd0 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2768]",
"ldr q2, [x28, #2800]",
"eor v2.16b, v18.16b, v2.16b",
"fadd v16.4s, v17.4s, v2.4s",
"stp xzr, xzr, [x28, #32]"
@@ -3805,7 +3805,7 @@
"ExpectedArm64ASM": [
"ldr q2, [x28, #48]",
"ldr q3, [x28, #64]",
"ldr q4, [x28, #2768]",
"ldr q4, [x28, #2800]",
"eor v5.16b, v18.16b, v4.16b",
"fadd v16.4s, v17.4s, v5.4s",
"eor v3.16b, v3.16b, v4.16b",
@@ -3976,7 +3976,7 @@
"Map 1 0b01 0xd7 256-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #3024]",
"ldr q2, [x28, #3056]",
"cmlt v3.16b, v16.16b, #0",
"and v2.16b, v3.16b, v2.16b",
"addp v2.16b, v2.16b, v2.16b",
@@ -3992,7 +3992,7 @@
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #32]",
"ldr q3, [x28, #3024]",
"ldr q3, [x28, #3056]",
"cmlt v4.16b, v16.16b, #0",
"and v4.16b, v4.16b, v3.16b",
"addp v4.16b, v4.16b, v4.16b",
@@ -1917,7 +1917,7 @@
"Map 2 0b01 0x41 256-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2736]",
"ldr q2, [x28, #2768]",
"zip1 v3.8h, v2.8h, v17.8h",
"zip2 v2.8h, v2.8h, v17.8h",
"umin v2.4s, v3.4s, v2.4s",
@@ -4485,7 +4485,7 @@
"Map 2 0b01 0x96 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2768]",
"ldr q2, [x28, #2800]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -4502,7 +4502,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2768]",
"ldr q5, [x28, #2800]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -4518,7 +4518,7 @@
"Map 2 0b01 0x96 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2800]",
"ldr q2, [x28, #2832]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -4535,7 +4535,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2800]",
"ldr q5, [x28, #2832]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -4551,7 +4551,7 @@
"Map 2 0b01 0x97 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2832]",
"ldr q2, [x28, #2864]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -4568,7 +4568,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2832]",
"ldr q5, [x28, #2864]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -4584,7 +4584,7 @@
"Map 2 0b01 0x97 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2864]",
"ldr q2, [x28, #2896]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -4601,7 +4601,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2864]",
"ldr q5, [x28, #2896]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -5541,7 +5541,7 @@
"Map 2 0b01 0xa6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2768]",
"ldr q2, [x28, #2800]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -5558,7 +5558,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2768]",
"ldr q5, [x28, #2800]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -5574,7 +5574,7 @@
"Map 2 0b01 0xa6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2800]",
"ldr q2, [x28, #2832]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -5591,7 +5591,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2800]",
"ldr q5, [x28, #2832]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -5607,7 +5607,7 @@
"Map 2 0b01 0xa7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2832]",
"ldr q2, [x28, #2864]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -5624,7 +5624,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2832]",
"ldr q5, [x28, #2864]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -5640,7 +5640,7 @@
"Map 2 0b01 0xa7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2864]",
"ldr q2, [x28, #2896]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -5657,7 +5657,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2864]",
"ldr q5, [x28, #2896]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -5673,7 +5673,7 @@
"Map 2 0b01 0xb6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2768]",
"ldr q2, [x28, #2800]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -5689,7 +5689,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2768]",
"ldr q5, [x28, #2800]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -5704,7 +5704,7 @@
"Map 2 0b01 0xb6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2800]",
"ldr q2, [x28, #2832]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -5720,7 +5720,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2800]",
"ldr q5, [x28, #2832]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -5735,7 +5735,7 @@
"Map 2 0b01 0xb7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2832]",
"ldr q2, [x28, #2864]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -5751,7 +5751,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2832]",
"ldr q5, [x28, #2864]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -5766,7 +5766,7 @@
"Map 2 0b01 0xb7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2864]",
"ldr q2, [x28, #2896]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -5782,7 +5782,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2864]",
"ldr q5, [x28, #2896]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -2843,7 +2843,7 @@
"Map 2 0b01 0x96 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2768]",
"ldr q2, [x28, #2800]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -2860,7 +2860,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2768]",
"ldr q5, [x28, #2800]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -2876,7 +2876,7 @@
"Map 2 0b01 0x96 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2800]",
"ldr q2, [x28, #2832]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -2893,7 +2893,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2800]",
"ldr q5, [x28, #2832]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -2909,7 +2909,7 @@
"Map 2 0b01 0x97 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2832]",
"ldr q2, [x28, #2864]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -2926,7 +2926,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2832]",
"ldr q5, [x28, #2864]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v16.4s, v18.4s",
@@ -2942,7 +2942,7 @@
"Map 2 0b01 0x97 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2864]",
"ldr q2, [x28, #2896]",
"eor v2.16b, v17.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -2959,7 +2959,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2864]",
"ldr q5, [x28, #2896]",
"eor v6.16b, v17.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v16.2d, v18.2d",
@@ -3879,7 +3879,7 @@
"Map 2 0b01 0xa6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2768]",
"ldr q2, [x28, #2800]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -3896,7 +3896,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2768]",
"ldr q5, [x28, #2800]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -3912,7 +3912,7 @@
"Map 2 0b01 0xa6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2800]",
"ldr q2, [x28, #2832]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -3929,7 +3929,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2800]",
"ldr q5, [x28, #2832]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -3945,7 +3945,7 @@
"Map 2 0b01 0xa7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2832]",
"ldr q2, [x28, #2864]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -3962,7 +3962,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2832]",
"ldr q5, [x28, #2864]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.4s, v17.4s, v16.4s",
@@ -3978,7 +3978,7 @@
"Map 2 0b01 0xa7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2864]",
"ldr q2, [x28, #2896]",
"eor v2.16b, v18.16b, v2.16b",
"mov v0.16b, v2.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -3995,7 +3995,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2864]",
"ldr q5, [x28, #2896]",
"eor v6.16b, v18.16b, v5.16b",
"mov v0.16b, v6.16b",
"fmla v0.2d, v17.2d, v16.2d",
@@ -4011,7 +4011,7 @@
"Map 2 0b01 0xb6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2768]",
"ldr q2, [x28, #2800]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -4027,7 +4027,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2768]",
"ldr q5, [x28, #2800]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -4042,7 +4042,7 @@
"Map 2 0b01 0xb6 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2800]",
"ldr q2, [x28, #2832]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -4058,7 +4058,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2800]",
"ldr q5, [x28, #2832]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -4073,7 +4073,7 @@
"Map 2 0b01 0xb7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2832]",
"ldr q2, [x28, #2864]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -4089,7 +4089,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2832]",
"ldr q5, [x28, #2864]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.4s, v17.4s, v18.4s",
@@ -4104,7 +4104,7 @@
"Map 2 0b01 0xb7 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2864]",
"ldr q2, [x28, #2896]",
"eor v2.16b, v16.16b, v2.16b",
"mov v16.16b, v2.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -4120,7 +4120,7 @@
"ldr q2, [x28, #32]",
"ldr q3, [x28, #48]",
"ldr q4, [x28, #64]",
"ldr q5, [x28, #2864]",
"ldr q5, [x28, #2896]",
"eor v6.16b, v16.16b, v5.16b",
"mov v16.16b, v6.16b",
"fmla v16.2d, v17.2d, v18.2d",
@@ -337,7 +337,7 @@
"Map 3 0b01 0x02 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2928]",
"ldr q2, [x28, #2960]",
"tbx v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
]
@@ -348,7 +348,7 @@
"Map 3 0b01 0x02 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2944]",
"ldr q2, [x28, #2976]",
"tbx v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
]
@@ -369,7 +369,7 @@
"Map 3 0b01 0x02 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2960]",
"ldr q2, [x28, #2992]",
"tbx v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
]
@@ -391,7 +391,7 @@
"Map 3 0b01 0x02 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2976]",
"ldr q2, [x28, #3008]",
"tbx v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
]
@@ -412,7 +412,7 @@
"Map 3 0b01 0x02 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2992]",
"ldr q2, [x28, #3024]",
"tbx v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
]
@@ -423,7 +423,7 @@
"Map 3 0b01 0x02 128-bit"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #3008]",
"ldr q2, [x28, #3040]",
"tbx v16.16b, {v17.16b}, v2.16b",
"stp xzr, xzr, [x28, #32]"
]
@@ -3488,7 +3488,7 @@
],
"ExpectedArm64ASM": [
"movi v2.2d, #0x0",
"ldr q3, [x28, #2912]",
"ldr q3, [x28, #2944]",
"mov v16.16b, v17.16b",
"aese v16.16b, v2.16b",
"tbl v16.16b, {v16.16b}, v3.16b",
@@ -3502,7 +3502,7 @@
],
"ExpectedArm64ASM": [
"movi v2.2d, #0x0",
"ldr q3, [x28, #2912]",
"ldr q3, [x28, #2944]",
"mov v16.16b, v17.16b",
"aese v16.16b, v2.16b",
"tbl v16.16b, {v16.16b}, v3.16b",
@@ -31,7 +31,7 @@
"0x66 0x0f 0x38 0xca"
],
"ExpectedArm64ASM": [
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q2, [x0, #432]",
"tbl v3.16b, {v16.16b}, v2.16b",
"tbl v4.16b, {v17.16b}, v2.16b",
+10 -10
View File
@@ -55,7 +55,7 @@
"0x66 0x0f 0x3a 0xdf"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2912]",
"ldr q2, [x28, #2944]",
"movi v3.2d, #0x0",
"mov v16.16b, v17.16b",
"aese v16.16b, v3.16b",
@@ -68,7 +68,7 @@
"0x66 0x0f 0x3a 0xdf"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #2912]",
"ldr q2, [x28, #2944]",
"movi v3.2d, #0x0",
"mov v16.16b, v17.16b",
"aese v16.16b, v3.16b",
@@ -84,9 +84,9 @@
"0x66 0x0f 0x3a 0xcc"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #3296]",
"ldr q2, [x28, #3328]",
"movi v3.2d, #0x0",
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q4, [x0, #432]",
"tbl v5.16b, {v16.16b}, v4.16b",
"tbl v6.16b, {v17.16b}, v4.16b",
@@ -101,9 +101,9 @@
"0x66 0x0f 0x3a 0xcc"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #3312]",
"ldr q2, [x28, #3344]",
"movi v3.2d, #0x0",
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q4, [x0, #432]",
"tbl v5.16b, {v16.16b}, v4.16b",
"tbl v6.16b, {v17.16b}, v4.16b",
@@ -118,9 +118,9 @@
"0x66 0x0f 0x3a 0xcc"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #3328]",
"ldr q2, [x28, #3360]",
"movi v3.2d, #0x0",
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q4, [x0, #432]",
"tbl v5.16b, {v16.16b}, v4.16b",
"tbl v6.16b, {v17.16b}, v4.16b",
@@ -135,9 +135,9 @@
"0x66 0x0f 0x3a 0xcc"
],
"ExpectedArm64ASM": [
"ldr q2, [x28, #3344]",
"ldr q2, [x28, #3376]",
"movi v3.2d, #0x0",
"ldr x0, [x28, #2440]",
"ldr x0, [x28, #2472]",
"ldr q4, [x0, #432]",
"tbl v5.16b, {v16.16b}, v4.16b",
"tbl v6.16b, {v17.16b}, v4.16b",
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff. Show more