Compare commits

...
162 Commits
Author SHA1 Message Date
Ryan Houdek ee56a2cbde Docs: Update for release FEX-2403 2024-03-04 17:55:24 -08:00
Mai d66a83a98f Merge pull request #3482 from Sonicadvance1/fix_3479
Removes steamwebhelper config
2024-03-02 22:27:28 -05:00
Ryan Houdek 067b346e1b Removes steamwebhelper config
Fixes #3479

As of the Steam update on Feb 29 2024, this is no longer necessary and
actually causes steamwebhelper to abort.

Steam has started using SLR to launch steamwebhelper, which already
passes in --no-sandbox. Adding this argument twice seemingly breaks the
application since it starts complaining that it doesn't understand the
argument and closes with a SIGABRT.

With this removed Steam starts working again.
2024-03-02 13:32:08 -08:00
Ryan Houdek ea7d1697b1 Merge pull request #3480 from alyssarosenzweig/setf
Use SETF8/16 for 8/16-bit INC/DEC
2024-03-02 13:31:44 -08:00
Alyssa Rosenzweig 5fd91d34f1 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-03-01 19:40:53 -04:00
Alyssa Rosenzweig 11880459a5 OpcodeDispatcher: use SETF for DEC
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-03-01 19:40:53 -04:00
Alyssa Rosenzweig 0ef0bb2c97 OpcodeDispatcher: use SETF for INC
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-03-01 19:40:53 -04:00
Alyssa Rosenzweig 72edee7c6f IR: add SETF8/SETF16 ir ops
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-03-01 19:40:53 -04:00
Alyssa Rosenzweig cb5644cf81 InstCountCI: add multi-inst 8-bit DEC cases
would've got a bug in an earlier version of this series

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-03-01 19:38:40 -04:00
Ryan Houdek 2dd922c3ce Merge pull request #3477 from neobrain/feature_update_vulkan
Library Forwarding: Update Vulkan definitions to v1.3.278
2024-03-01 10:38:14 -08:00
Alyssa Rosenzweig b7984e8651 Merge pull request #3471 from Sonicadvance1/another_3421_bug
ASM: Another sign extend bug in #3421
2024-03-01 10:57:32 -04:00
Tony Wasserka 31e976a5bc Library Forwarding: Update Vulkan definitions to v1.3.278 2024-02-29 19:08:38 +01:00
Ryan Houdek 009ae55ff0 Merge pull request #3475 from alyssarosenzweig/opt/lock-dec
Optimize lock dec
2024-02-29 08:44:24 -08:00
Ryan Houdek 98572b9e23 Merge pull request #3473 from Sonicadvance1/remove_mov_swap
Arm64: Stop moving source in atomic swap
2024-02-29 08:44:16 -08:00
Alyssa Rosenzweig 1ab234f6dd InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-29 09:28:21 -04:00
Alyssa Rosenzweig fed5e6d546 OpcodeDispatcher: use fetchadd for atomic DEC
Avoids a NEG.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-29 09:28:21 -04:00
Alyssa Rosenzweig f27e2246e2 Merge pull request #3468 from alyssarosenzweig/opt/miscs
Misc little opts
2024-02-29 09:18:17 -04:00
Mai 4779fb74de Merge pull request #3474 from Sonicadvance1/fix_early_exit_region_reserving
Fix reserving range check
2024-02-29 07:22:07 -05:00
Ryan Houdek eaf83aa6b4 Fix reserving range check
Fixes an issue where TestHarnessRunner was managing to reserve the space
below stack again, resulting in stack growth breaking. Would typically
only show up when using the vixl simulator under gdb for some reason.

This is likely the last bandage on this code before it gets completely
rewritten to be more readable.
2024-02-29 04:02:05 -08:00
Ryan Houdek 4f8b28e83b InstcountCI: Update for swap improvement 2024-02-29 03:07:19 -08:00
Ryan Houdek c318947695 Arm64: Stop moving source in atomic swap
ldswpal doesn't overwrite the source register and only reads the bits
required for the sized operation.
Not sure exactly why we were doing a copy here.

Removing it means improving Skyrim's hottest code block, as seen in #3472
2024-02-29 03:07:05 -08:00
Ryan Houdek b3489d7262 ASM: Another sign extend bug in #3421
This time found in MGRR. It flips the problem space on its head.
2024-02-29 02:06:57 -08:00
Alyssa Rosenzweig 44d738fa93 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 10:35:34 -04:00
Alyssa Rosenzweig 811487ad98 OpcodeDispatcher: use real branch for INT
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 10:35:12 -04:00
Alyssa Rosenzweig 4f4e38ace2 OpcodeDispatcher: use real branch for rep cmps
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 10:35:12 -04:00
Alyssa Rosenzweig edd6becc56 OpcodeDispatcher: use real branch for rep scas
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 10:35:12 -04:00
Alyssa Rosenzweig e47a94cae7 OpcodeDispatcher: skip mask with shld
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 10:18:29 -04:00
Alyssa Rosenzweig cc82dba1ca OpcodeDispatcher: use mvn for AF with constants
This reduces pointless constant usage. For now, it's no net change to
instcountci, but it should make it easier to get wins later. Hopefully.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 10:18:28 -04:00
Alyssa Rosenzweig 8232669b22 OpcodeDispatcher: simplify
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 10:00:13 -04:00
Alyssa Rosenzweig 28936073c4 OpcodeDispatcher: allow upper garbage on NEG
like SUB.

due to RA silliness, this is a loss for inst count but a win for cycles.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 09:59:57 -04:00
Alyssa Rosenzweig ef2559d911 OpcodeDispatcher: allow garbage with SCAS
it's just feeding SUB flags which allow it

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-28 09:16:08 -04:00
Ryan Houdek d24446ed13 Merge pull request #3466 from Sonicadvance1/fixed_opt
OpcodeDispatcher: Don't use AddShift with no shift
2024-02-28 04:51:10 -08:00
Ryan Houdek 67f13ba927 Adds sign extending address bug that was detected when testing #3421
Doesn't quite match the libc code directly because it uses `[gs:eax]`
with both having the sign bit set and we can't deal with that with ASM
tests. So match the behaviour in a different way.
2024-02-27 19:57:22 -08:00
Ryan Houdek dc5239c003 InstCountCI: Update for previous fix 2024-02-27 19:57:06 -08:00
Ryan Houdek f346f89678 OpcodeDispatcher: Don't use AddShift with no shift
This accidentally removed optimizations elsewhere that was only checking
for Add.
2024-02-27 19:56:12 -08:00
Ryan Houdek 2f9449cb5a Merge pull request #3465 from alyssarosenzweig/icci/pa
InstCountCI: enable preserve_all
2024-02-27 16:39:46 -08:00
Ryan Houdek 139367d248 Merge pull request #3463 from Sonicadvance1/update_xxhash
Update xxhash to v0.8.2
2024-02-27 16:39:38 -08:00
Alyssa Rosenzweig fcebad51bd InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-27 12:04:29 -04:00
Alyssa Rosenzweig b50292493a InstCountCI: enable preserve_all ABI
This is what we'll actually ship (I hope), so that's the config we want to
track long-term. It's also a lot more managable resulting asm.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-27 12:03:58 -04:00
Ryan Houdek b74de53056 Merge pull request #3449 from Sonicadvance1/syscalls_passthrough_not_json
Linux: Converts passthrough syscalls to direct passthrough handlers
2024-02-27 07:50:04 -08:00
Alyssa Rosenzweig 49e798ab2b Merge pull request #3461 from alyssarosenzweig/opt/sbc
Optimize SBC
2024-02-27 11:29:45 -04:00
Ryan Houdek 151e2279af Linux: Converts passthrough syscalls to direct passthrough handlers
Reimagining of #3355 without any json generators or new concepts.

Fixes some mislabeling of system calls. Some getting inlined when they
shouldn't be, a lot not getting inlined when they can be.

This really cleans up the syscall implementation, all syscalls that can
be passthrough implementations require a very small two line
declaration.
Additionally cleans up a bit of implementation cruft where some
passthrough syscalls were using the glibc syscall handler, and some were
using the glibc implementation. We have had multiple issues in the past
where the glibc implementation does something subtly different than the
raw syscall and breaks things. Now all passthrough handlers do a system
call directly, removing at least one indirection and some ambiguity.

This makes it significantly easier to add new passthrough syscalls as
well. Only need to do a version check and add the three lines per
syscall. Which there are new syscalls incoming that we will want to add.

Tangible improvements:
- Syscalls are lower overhead than ever.
- When I'm adding more syscalls I have less chance of mucking it up.
2024-02-27 02:40:53 -08:00
Ryan Houdek 93ada89708 Linux: Move unimplement ustat and sysfs
AArch64 doesn't implement these and will return ENOSYS.
Moving them to NotImplemented so we can get a log if an application
tries to use these.
2024-02-27 02:39:36 -08:00
Mai f41674bb7d Merge pull request #3464 from Sonicadvance1/psychonauts_block
InstcountCI: Add a monster of a game block
2024-02-27 04:59:20 -05:00
Ryan Houdek 854fd70735 InstcountCI: Add a monster of a game block
Doing very little work with a bunch of instructions.
Hottest block in the Windows version of Psychonauts, it's just doing a
matrix swizzle but in the worst possible way.
2024-02-27 01:51:20 -08:00
Ryan Houdek 78a362581d Update xxhash to v0.8.2
Switches to using upstream cmake files.
2024-02-26 23:57:25 -08:00
Mai 8c0d5c6583 Merge pull request #3462 from Sonicadvance1/update_vixl4
Update vixl
2024-02-27 02:28:39 -05:00
Ryan Houdek 1c184997e7 InstcountCI: Update for vixl update 2024-02-26 23:17:52 -08:00
Ryan Houdek ccc699444d Update vixl
Removes a commit from our fork.
2024-02-26 23:16:31 -08:00
Ryan Houdek 946c805d84 Merge pull request #3459 from Sonicadvance1/fix_591
Capture a 64-bit process trying to jump to 32-bit syscall handler
2024-02-26 21:57:03 -08:00
Ryan Houdek 118b8b200e Merge pull request #3458 from Sonicadvance1/fix_635
Track unittest dependencies through to the custom target
2024-02-26 21:56:45 -08:00
Ryan Houdek aa9d7c5629 Merge pull request #3460 from Sonicadvance1/add_unittest_for_3421_bug
Adds a unittest for a bug from #3421
2024-02-26 21:56:17 -08:00
Ryan Houdek 0b34035085 Merge pull request #3439 from Sonicadvance1/allocate_first_4gb_of_64bit
FEXLoader: Allocate the second 4GB of virtual memory when executing 32-bit
2024-02-26 18:46:22 -08:00
Alyssa Rosenzweig b6bd826014 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 16:48:15 -04:00
Alyssa Rosenzweig 12cc980603 OpcodeDispatcher: shuffle adc flag order
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 16:48:07 -04:00
Alyssa Rosenzweig f3d55dd721 OpcodeDispatcher: shuffle SBC flag order
avoids clobbering nzcv

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 16:48:07 -04:00
Alyssa Rosenzweig 91cef6b76f OpcodeDispatcher: use native ADC even for 8/16-bit
we mask off the upper bits, and they agree in the lower bits.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 16:48:07 -04:00
Alyssa Rosenzweig 270cbf39b5 OpcodeDispatcher: specialize SALC
this gets rid of the awkward non-flag SBB case, which streamlines SBB. while
getting better codegen for the demon opcode (-:

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 16:33:41 -04:00
Alyssa Rosenzweig 2e0be0a5e7 OpcodeDispatcher: allow more upper garbage with adc
missed this last series.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 15:42:13 -04:00
Alyssa Rosenzweig d60c089697 OpcodeDispatcher: allow upper garbage with sbb
for the usual reasons

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 15:35:14 -04:00
Alyssa Rosenzweig e76ebeab58 OpcodeDispatcher: use 1-op "src + CF"
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 15:35:14 -04:00
Alyssa Rosenzweig 333271d490 OpcodeDispatcher: fuse sbb when flags calculated
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 15:35:14 -04:00
Alyssa Rosenzweig a750870abf OpcodeDispatcher: use fused sbcs calculations
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 15:25:03 -04:00
Alyssa Rosenzweig 15db72ef60 IR: add Sbb, SbbWithFlags ops
For fusing sbc+sbcs

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-26 15:25:03 -04:00
Ryan Houdek 9687ac51f0 Merge pull request #3424 from Sonicadvance1/safer_clone_stack_handling
Linux: More safe stack cleanup for clone
2024-02-26 06:59:27 -08:00
Alyssa Rosenzweig 5f16f357af Merge pull request #3456 from alyssarosenzweig/opt/adc
Optimize ADC
2024-02-26 09:39:37 -04:00
Ryan Houdek 4f028b8614 Capture a 64-bit process trying to jump to 32-bit syscall handler
Fixes #591

Adds a simple unittest
2024-02-26 05:37:29 -08:00
Alyssa Rosenzweig 32a4abbea7 Merge pull request #3457 from alyssarosenzweig/bug/nzcv
RedundantFlagCalculationElimination: fix missing NEG case
2024-02-26 09:31:06 -04:00
Ryan Houdek c00c9b397e Adds a unittest for a bug from #3421
When the source arguments for LoadMem/StoreMem have bit 31 set then they
are incorrectly sign extending in some instances.

Detected this when testing #3421 but I don't have a proper fix for it.
2024-02-26 00:07:19 -08:00
Ryan Houdek 6b5d8bd8c0 Track unittest dependencies through to the custom target
Fixes #635
2024-02-25 19:27:52 -08:00
Alyssa Rosenzweig 7deb4976a3 RedundantFlagCalculationElimination: fix missing NEG case
can be predicated.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-25 17:37:16 -04:00
Alyssa Rosenzweig 2cfd71c159 InstCountCI: add dead ADC test
nothing else covers this case

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-25 10:50:25 -04:00
Alyssa Rosenzweig 0f26780de0 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-25 10:50:25 -04:00
Alyssa Rosenzweig 1e153e0c81 OpcodeDispatcher: allow garbage with adcs
for the usual reasons

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-25 10:50:02 -04:00
Alyssa Rosenzweig 6994fc3a01 IR,OpcodeDispatcher,JIT: fuse adcs flags
The usual tricks, also requires introducing a bare adc op to optimize adcs to,
but we wanted that anyway!

Also support a zero source, so we can calculate "foo + CF" in one instruction to
optimize the "lock adc" cases.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-25 10:49:32 -04:00
Alyssa Rosenzweig 0ef72bf118 Merge pull request #3451 from Sonicadvance1/fix_zero_reg_regression
Fixes zero register flag generation
2024-02-24 21:08:49 -04:00
Alyssa Rosenzweig 49ca0e2181 Merge pull request #3452 from Sonicadvance1/dxvk_mgrr_hotblock
Adds MGRR hottest block on render thread
2024-02-24 21:07:25 -04:00
Ryan Houdek 947ae1c243 Adds MGRR hottest block on render thread
Was about 7% CPU time in this looping block. Has some fairly obvious
performance improvements that can be done.
2024-02-24 16:49:46 -08:00
Ryan Houdek d703f3ccee Fixes zero register flag generation
Fixes 140976d322

Adds a unit test to ensure it keeps working.
2024-02-24 16:32:25 -08:00
Ryan Houdek d8a18687e8 Merge pull request #3443 from alyssarosenzweig/opt/add-too
Fuse add + cmn -> adds
2024-02-24 15:12:16 -08:00
Alyssa Rosenzweig 045549f166 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:55:07 -04:00
Alyssa Rosenzweig 80e632db8a OpcodeDispatcher: garbage collect
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 852e3c4e93 OpcodeDispatcher: fuse XADD
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig e86547bbcb OpcodeDispatcher: fuse INC
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 883cca2e8f OpcodeDispatcher: use AddWithFlags
give it the same treatment we just gave sub.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 8540332520 IR: add AddWithFlags
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig df5bdefb8a OpcodeDispatcher: merge secondary ALU with primary ALU
It's the same, stop copypasting. This gets our flag and arithmetic opts (current
and future) applied to secondary ALU too.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 3d1fb7701c OpcodeDispatcher: optimize sub
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 140976d322 OpcodeDispatcher: prep primary ALU for better flags
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 9a11d3b1a2 OpcodeDispatcher: fuse NEG
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 96e652879f OpcodeDispatcher: fuse DEC
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig cc1c1dd047 OpcodeDispatcher: return result from SUB flag calculate
for fusion

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig c1d572951f OpcodeDispatcher: drop unused GenerateFlags_SUB arg
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig dd9d3264dd OpcodeDispatcher: smarten SUB flag generation
we don't need the result, we can use subs and come out ahead in practice. also a
step towards better fusion

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 2aaf957ad8 RedundantFlagCalculationElimination: DCE as we go
This is required to ensure single-iteration convergence with a sequence like:

  write C
  whatever = load C
  rmif C, whatever
  invalidate C

avoids regressing the "DEC dead" case with future work in the series.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig d459b2f9b5 IR: propagate 0 into sub
now that we have to handle it, we may as well take advantage of it.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 22ab7f2b3e IR: add SubWithFlags op (arm64 subs)
with 8/16-bit handling to keep everything uniform.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig f7e32373ce JIT: allow #0 in sub
turns into neg, this will be generated via SubWithFlags -> Sub opts.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 25d422e92b JIT: use GetZeroableReg for NZCVSelect
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig cfbeece09f JIT: use GetZeroableReg for CondAddNZCV
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig a597a09825 JIT: use GetZeroableReg for SubNZCV
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 99854ff310 JIT: add GetZeroableReg helper
for inlining constant zeroes in applicable sources

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-24 15:54:49 -04:00
Alyssa Rosenzweig 44d1502b5a Merge pull request #3450 from Sonicadvance1/vector_add_loop
InstcountCI: Adds a vector addition loop from bytemark
2024-02-24 15:54:44 -04:00
Ryan Houdek c3c635e36e InstcountCI: Adds a vector addition loop from bytemark
This was interesting because it caught that we are failing loadstores
with reg+reg address generation on vectors.
2024-02-24 03:44:02 -08:00
Ryan Houdek 7e2f20cabb Merge pull request #3446 from neobrain/fix_gdb_source_map
Syscalls: Fix SourcecodeMap generation for GDB JIT integration
2024-02-24 02:46:45 -08:00
Tony Wasserka 3425b07711 Syscalls: Fix SourcecodeMap generation for GDB JIT integration
This fixes a regression from 9dd715573, which accidentally changed the
filename and set up incorrect file opening flags.
2024-02-24 11:00:23 +01:00
Ryan Houdek 9b93495d45 Merge pull request #3441 from Sonicadvance1/remove_signaldelegation_tls
Moves SignalDelegator TLS tracking to the frontend
2024-02-24 01:23:43 -08:00
Ryan Houdek cf067994f3 Merge pull request #3440 from Sonicadvance1/addressing_limits
InstcountCI: Adds addressing limitations to instcountci
2024-02-24 01:09:22 -08:00
Ryan Houdek 0a64f8a9c5 Moves SignalDelegator TLS tracking to the frontend
FEXCore doesn't need track the TLS state of the SignalDelegator, this is
a frontend concept.

Removes the tracking from the backend and keeps it in the frontend.
2024-02-24 01:07:29 -08:00
Ryan Houdek 3ac7fe3f05 Linux: More safe stack cleanup for clone
Previously: Would keep one clone thread's stack active for teardown
delaying.

With aggressive cloning and teardown, this was unsafe.
Only reap the stack when told it is safe to do so.
2024-02-24 01:05:20 -08:00
Ryan Houdek be96cb7bd0 FEXLoader: Allocate the second 4GB of virtual memory when executing 32-bit
Spurred on by #3421. To ensure that applications don't take advantage of
small address wrap around, allocate the second 4GB of virtual memory.

Some context. Linux always reserves the first 16KB of virtual address
space (unless you tinker with some settings which nobody should do).

Example of 32-bit code:
lea eax, [0xffff_0000]
mov ebx, [eax + 0x1_0000]

The address calculated by the mov will wrap around to 0x0 which will
result in SIGSEGV. If FEX messes up zero extensions then it would try to
access 0x1_0000_0000 instead.

This could result in a 32-bit application potentially accessing some FEX
memory instead of crashing.
Add this safety net which will still SIGSEGV and we will be able to see
the crash.
2024-02-24 00:54:07 -08:00
Ryan Houdek 59ec88f48d Merge pull request #3438 from Sonicadvance1/move_tls_allocation
Moves JITSymbol allocation
2024-02-23 14:49:04 -08:00
Ryan Houdek 6ec628fa31 Merge pull request #3433 from bylaws/arm64ec-pt1
Arm64Emitter: Introduce ARM64EC SRA mappings
2024-02-23 14:48:43 -08:00
Alyssa Rosenzweig 90256730c3 Merge pull request #3447 from Sonicadvance1/fix_instcountci
Fix instcountci
2024-02-22 17:24:24 -04:00
Ryan Houdek 3a0f9db512 Fix instcountci 2024-02-22 12:44:10 -08:00
Alyssa Rosenzweig 5378ae2e76 Merge pull request #3436 from alyssarosenzweig/ir/af-simplify
Simplify CalculateAF
2024-02-22 08:17:07 -04:00
Ryan Houdek cc9c80d79f Merge pull request #3445 from alyssarosenzweig/instc/fmod
InstCountCI: add FMOD block
2024-02-21 19:27:07 -08:00
Ryan Houdek 60e8da05cd Merge pull request #3442 from alyssarosenzweig/instc/witcher3
InstCountCI: add The Witcher 3 block
2024-02-21 18:56:39 -08:00
Alyssa Rosenzweig aac7fa9b58 InstCountCI: add hot scalar FMOD block
both AFP and non-AFP versions, since this is greatly affected by AFP opts.

pulled from #2563

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-21 22:36:15 -04:00
Ryan Houdek bd4a81a2a1 Moves JITSymbol allocation
This isn't actually using TLS allocations. Instead it is an allocation
tied to the InternalThreadState object.
2024-02-21 17:57:34 -08:00
Alyssa Rosenzweig a6211f29e7 InstCountCI: add The Witcher 3 block
Closes: #2688

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-21 21:44:12 -04:00
Ryan Houdek da263834f8 InstcountCI: Adds addressing limitations to instcountci
Spurred on by #3421

This does a bunch of GPR and vector loads to showcase addressing
limitations between ARM and x86.

Tests:
- 8/16/32/64-bit GPR loads
   - Both 32-bit and 64-bit addressing modes
- 32/64/128-bit Vector loads
   - Both 32-bit and 64-bit addressing modes
- Duplicate the tests for 32-bit addressing mode with a 32-bit process
   - Since it should change behaviour.

Untested:
- 8/16-bit vector loadstores since those don't exist on x86
   - 16-bit x87 integer load exists but that doesn't go through a vector
     load in FEX.
2024-02-21 16:50:00 -08:00
Ryan Houdek 3d671cba10 Allocator: Early return if past the end of the allocation range
Fixes a bug where it would eventually hit the stack region and remap the
range as RW even for ranges that don't overlap the stack.
2024-02-21 15:37:46 -08:00
Ryan Houdek d4be2dc636 Merge pull request #3434 from bylaws/arm64ec-pt3
FEXCore: Expose AbsoluteLoopTopAddress to the frontend
2024-02-21 14:31:04 -08:00
Ryan Houdek 3e5694bd06 Merge pull request #3432 from bylaws/arm64ec-pt0
CMake: Define _M_ARM_64EC when building for ARM64EC
2024-02-21 14:30:09 -08:00
Ryan Houdek 66feea9e8e Merge pull request #3422 from neobrain/feature_libfwd_wayland32
Library Forwarding: Add support for 32-bit Wayland
2024-02-21 13:41:38 -08:00
Alyssa Rosenzweig 2bcd285851 Merge pull request #3430 from Sonicadvance1/tsc_scale
Implement small TSC scaling
2024-02-21 13:16:27 -04:00
Alyssa Rosenzweig 30ff225e80 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-21 12:49:14 -04:00
Alyssa Rosenzweig 8762bc1fa3 OpcodeDispatcher: simplify CalculateAF signature
- Res is unused
- SrcSize doesn't matter since we ignore the high bits, might as well always use
  32-bit, it doesn't matter

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-21 12:48:15 -04:00
Billy Laws 5b4162b712 FEXCore: Expose AbsoluteLoopTopAddress to the frontend
ARM64EC has a shared SRA mapping between ARM64 and X64 code, so there
needs to be a public way to enter the dispatcher without refilling SRA
from the in-memory context struct.
2024-02-21 11:46:24 +00:00
Billy Laws cb5c07f4b1 Arm64Emitter: Introduce ARM64EC SRA mappings
See https://learn.microsoft.com/en-us/cpp/build/arm64ec-windows-abi-conventions?view=msvc-170
note that since mm registers are volatile there is no need to match the
mapping for them when in JIT, so they can be used as scratch regs.
Disallowed regs are also wiped on context switches, so they cannot be
taken advantage of to e.g. avoid spilling.
2024-02-21 11:18:10 +00:00
Billy Laws 3364b48f3b CMake: Define _M_ARM_64EC when building for ARM64EC 2024-02-21 11:18:07 +00:00
Tony Wasserka 67530171e6 Library Forwarding/wayland: Clean up wl_interface exchange 2024-02-21 11:44:55 +01:00
Tony Wasserka 6f00611892 Library Forwarding/wayland: Add functions required by zink + Super Meat Boy on 32-bit 2024-02-21 11:44:55 +01:00
Tony Wasserka 67941f04eb Library Forwarding/wayland: Write arguments for callbacks to guest stack 2024-02-21 11:44:55 +01:00
Tony Wasserka 42531108b7 Library Forwarding/wayland: Enable 32-bit build 2024-02-21 11:44:55 +01:00
Tony Wasserka 7edab7ee3b Library Forwarding/wayland: For 32-bit guests, support repacking wl_interface/wl_message/wl_argument 2024-02-21 11:44:55 +01:00
Tony Wasserka b9f2389d74 Library Forwarding/wayland: Avoid using global state to substitute callback tables
The pointer tracked internally by Wayland can be queried via
wl_proxy_get_listener, so we don't need our own bookkeeping.

This also changes the callback table's element type to a fixed-size uint64_t,
which makes it work for 32-bit guests.
2024-02-21 11:44:55 +01:00
Tony Wasserka 3c0b041c44 Library Forwarding/wayland: Support reading message signatures on 32-bit guests 2024-02-21 11:44:54 +01:00
Tony Wasserka 80fcd640be unittests/Library Forwarding: Test interaction between ptr_passthrough and fixed-size integer mapping 2024-02-21 11:44:54 +01:00
Tony Wasserka 0908968e87 Library Forwarding/gen: Don't register types used exclusively with ptr_passthrough annotations
This allows forwarding APIs that sparsely use non-repackable types.
2024-02-21 11:44:54 +01:00
Ryan Houdek 8e0543d9af InstcountCI 2024-02-20 12:05:44 -08:00
Ryan Houdek b902b8edab Implement small TSC scaling
Games engines are expecting >1Ghz cycle counters. Scale them to work
around the issue.

Resolves the excessive busy waiting in Unreal Engine 5 games.
2024-02-20 12:05:44 -08:00
Alyssa Rosenzweig 9c38332e7e Merge pull request #3429 from alyssarosenzweig/cleanup/nzcv-helpers
Cleanup NZCV metadata
2024-02-20 10:18:15 -04:00
Alyssa Rosenzweig 0503c89ff6 OpcodeDispatcher: use NZCV update helpers
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-19 14:12:54 -04:00
Alyssa Rosenzweig 6dd410698a OpcodeDispatcher: add helpers for updating NZCV metadata
to reduce error-prone copypaste

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-19 14:12:54 -04:00
Ryan Houdek bbac014d6d Merge pull request #3427 from Sonicadvance1/fix_vdso_crash
Fixes VDSO crash in 64-bit code
2024-02-18 18:47:41 -08:00
Ryan Houdek a723ff09c1 Fixes VDSO crash in 64-bit code
Ever since #3406 this has been crashing. Struct tail padding was saving
this before.
2024-02-17 16:43:09 -08:00
Mai 5769ffbba7 Merge pull request #3425 from Sonicadvance1/frontend_pointer
FEXCore: Add a frontend pointer to InternalThreadState
2024-02-16 19:41:41 -05:00
Mai a7c7fe4a35 Merge pull request #3426 from Sonicadvance1/thread_leak
Linux: Make sure to destroy thread object when thread shuts down
2024-02-16 19:41:23 -05:00
Ryan Houdek 80f20ad121 Linux: Make sure to destroy thread object when thread shuts down
This fixes a fairly large memor leak.
2024-02-16 15:08:29 -08:00
Ryan Houdek 808ced455d FEXCore: Add a frontend pointer to InternalThreadState
FEXCore is guaranteed to not touch this pointer and can be used by
frontends to store thread-specific data.
2024-02-15 02:06:16 -08:00
Mai 0505b30d34 Merge pull request #3423 from Sonicadvance1/fix_fd_leak
FileFormatCheck: Fixes FD leak
2024-02-14 16:01:29 -05:00
Ryan Houdek 16a5d1a6b1 FileFormatCheck: Fixes FD leak 2024-02-14 12:21:47 -08:00
Ryan Houdek 9cab746aa7 Merge pull request #3407 from neobrain/feature_libfwd_arguments_on_guest_stack
Library Forwarding: Allocate packed arguments on the guest stack if needed
2024-02-12 16:31:34 -08:00
Ryan Houdek b888bb5ce5 Merge pull request #3406 from neobrain/feature_libfwd_packed_arguments
Library Forwarding: Disable struct padding for packed arguments
2024-02-12 16:23:44 -08:00
Ryan Houdek 47218254a1 Merge pull request #3416 from alyssarosenzweig/opt/add-smol
Optimize 8/16-bit adds/subs
2024-02-12 15:52:05 -08:00
Alyssa Rosenzweig fba5678476 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-12 12:36:48 -04:00
Alyssa Rosenzweig 68232366e4 OpcodeDispatcher: don't mask add/sub sources
not needed in the new approach

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-12 12:36:28 -04:00
Alyssa Rosenzweig d7ff1b78fb IR: handle 8/16-bit AddNZCV/SubNZCV
we can do it more effectively than the current s/w lowering.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2024-02-12 12:36:09 -04:00
Tony Wasserka df3e51fc8c Library Forwarding: Allocate packed arguments on the guest stack if needed
This is required for host-side calls to guest functions on 32-bit guests.
Since the host stack is allocated before FEX blocks memory inaccessible to
the guest, the guest would otherwise fail to read the packed argument data.
2024-02-05 18:10:34 +01:00
Tony Wasserka 06c29eab88 Library Forwarding: Disable struct padding for packed arguments
ARM64, x86 (64-bit), and x86 (32-bit) each have different alignment
requirements, so this change ensures that consistent data layout is
used for packing and unpacking.
2024-02-05 17:39:34 +01:00
126 changed files with 15257 additions and 29770 deletions

No files matched your search

+1 -1
View File
@@ -41,7 +41,7 @@
url = https://github.com/FEX-Emu/drm-headers.git
[submodule "External/xxhash"]
path = External/xxhash
url = https://github.com/FEX-Emu/xxHash.git
url = https://github.com/Cyan4973/xxHash.git
[submodule "External/Catch2"]
path = External/Catch2
url = https://github.com/catchorg/Catch2.git
+9 -2
View File
@@ -122,6 +122,11 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
add_definitions(-D_M_ARM_64=1)
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm64ec")
set(_M_ARM_64EC 1)
add_definitions(-D_M_ARM_64EC=1)
endif()
if (ENABLE_CCACHE)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
@@ -232,8 +237,10 @@ endif()
find_package(PkgConfig REQUIRED)
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter)
add_subdirectory(External/xxhash/)
include_directories(External/xxhash/)
set(XXHASH_BUNDLED_MODE TRUE)
set(XXHASH_BUILD_XXHSUM FALSE)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(External/xxhash/cmake_unofficial/)
add_definitions(-Wno-trigraphs)
add_definitions(-DGLOBAL_DATA_DIRECTORY="${DATA_DIRECTORY}/")
-5
View File
@@ -1,5 +0,0 @@
{
"Config": {
"AdditionalArguments": "--no-sandbox"
}
}
+1 -1
+1 -1
+1 -1
View File
@@ -194,7 +194,7 @@ endif()
# Some defines for the softfloat library
list(APPEND DEFINES "-DSOFTFLOAT_BUILTIN_CLZ")
set (LIBS fmt::fmt vixl xxhash FEXHeaderUtils)
set (LIBS fmt::fmt vixl xxHash::xxhash FEXHeaderUtils)
if (NOT MINGW_BUILD)
list (APPEND LIBS dl)
@@ -115,6 +115,13 @@
"\toff: Default CPU features queried from CPU features",
"\t{enable,disable}sha: Will force enable or disable sha even if the host doesn't support it"
]
},
"SmallTSCScale": {
"Type": "bool",
"Default": "true",
"Desc": [
"Scales the cycle counter on systems that have low frequencies."
]
}
},
"Emulation": {
@@ -74,6 +74,8 @@ namespace FEXCore::Context {
};
using BlockDelinkerFunc = void(*)(FEXCore::Core::CpuStateFrame *Frame, FEXCore::Context::ExitFunctionLinkData *Record);
constexpr uint32_t TSC_SCALE = 128;
constexpr uint32_t TSC_SCALE_MAXIMUM = 1'000'000'000; ///< 1Ghz
class ContextImpl final : public FEXCore::Context::Context {
public:
@@ -230,6 +232,7 @@ namespace FEXCore::Context {
FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION);
FEX_CONFIG_OPT(DisableTelemetry, DISABLETELEMETRY);
FEX_CONFIG_OPT(DisableVixlIndirectCalls, DISABLE_VIXL_INDIRECT_RUNTIME_CALLS);
FEX_CONFIG_OPT(SmallTSCScale, SMALLTSCSCALE);
} Config;
@@ -29,6 +29,7 @@ namespace FEXCore::CPU {
// TODO: Allow x18 register allocation on Linux in the future to gain one more register.
namespace x64 {
#ifndef _M_ARM_64EC
// All but x19 and x29 are caller saved
constexpr std::array<FEXCore::ARMEmitter::Register, 18> SRA = {
FEXCore::ARMEmitter::Reg::r4, FEXCore::ARMEmitter::Reg::r5,
@@ -82,6 +83,54 @@ namespace x64 {
FEXCore::ARMEmitter::VReg::v12, FEXCore::ARMEmitter::VReg::v13,
FEXCore::ARMEmitter::VReg::v14, FEXCore::ARMEmitter::VReg::v15,
};
#else
constexpr std::array<FEXCore::ARMEmitter::Register, 18> SRA = {
FEXCore::ARMEmitter::Reg::r8, FEXCore::ARMEmitter::Reg::r0,
FEXCore::ARMEmitter::Reg::r1, FEXCore::ARMEmitter::Reg::r27,
// SP's register location isn't specified by the ARM64EC ABI, we choose to use r23
FEXCore::ARMEmitter::Reg::r23, FEXCore::ARMEmitter::Reg::r29,
FEXCore::ARMEmitter::Reg::r25, FEXCore::ARMEmitter::Reg::r26,
FEXCore::ARMEmitter::Reg::r2, FEXCore::ARMEmitter::Reg::r3,
FEXCore::ARMEmitter::Reg::r4, FEXCore::ARMEmitter::Reg::r5,
FEXCore::ARMEmitter::Reg::r19, FEXCore::ARMEmitter::Reg::r20,
FEXCore::ARMEmitter::Reg::r21, FEXCore::ARMEmitter::Reg::r22,
REG_PF, REG_AF,
};
constexpr std::array<FEXCore::ARMEmitter::Register, 7> RA = {
FEXCore::ARMEmitter::Reg::r6, FEXCore::ARMEmitter::Reg::r7,
FEXCore::ARMEmitter::Reg::r14,FEXCore::ARMEmitter::Reg::r15,
FEXCore::ARMEmitter::Reg::r16, FEXCore::ARMEmitter::Reg::r17,
FEXCore::ARMEmitter::Reg::r30,
};
constexpr std::array<std::pair<FEXCore::ARMEmitter::Register, FEXCore::ARMEmitter::Register>, 3> RAPair = {{
{FEXCore::ARMEmitter::Reg::r6, FEXCore::ARMEmitter::Reg::r7},
{FEXCore::ARMEmitter::Reg::r14, FEXCore::ARMEmitter::Reg::r15},
{FEXCore::ARMEmitter::Reg::r16, FEXCore::ARMEmitter::Reg::r17},
}};
constexpr std::array<FEXCore::ARMEmitter::VRegister, 16> SRAFPR = {
FEXCore::ARMEmitter::VReg::v0, FEXCore::ARMEmitter::VReg::v1,
FEXCore::ARMEmitter::VReg::v2, FEXCore::ARMEmitter::VReg::v3,
FEXCore::ARMEmitter::VReg::v4, FEXCore::ARMEmitter::VReg::v5,
FEXCore::ARMEmitter::VReg::v6, FEXCore::ARMEmitter::VReg::v7,
FEXCore::ARMEmitter::VReg::v8, FEXCore::ARMEmitter::VReg::v9,
FEXCore::ARMEmitter::VReg::v10, FEXCore::ARMEmitter::VReg::v11,
FEXCore::ARMEmitter::VReg::v12, FEXCore::ARMEmitter::VReg::v13,
FEXCore::ARMEmitter::VReg::v14, FEXCore::ARMEmitter::VReg::v15,
};
constexpr std::array<FEXCore::ARMEmitter::VRegister, 14> RAFPR = {
FEXCore::ARMEmitter::VReg::v18, FEXCore::ARMEmitter::VReg::v19,
FEXCore::ARMEmitter::VReg::v20, FEXCore::ARMEmitter::VReg::v21,
FEXCore::ARMEmitter::VReg::v22, FEXCore::ARMEmitter::VReg::v23,
FEXCore::ARMEmitter::VReg::v24, FEXCore::ARMEmitter::VReg::v25,
FEXCore::ARMEmitter::VReg::v26, FEXCore::ARMEmitter::VReg::v27,
FEXCore::ARMEmitter::VReg::v28, FEXCore::ARMEmitter::VReg::v29,
FEXCore::ARMEmitter::VReg::v30, FEXCore::ARMEmitter::VReg::v31
};
#endif
// I wish this could get constexpr generated from SRA's definition but impossible until libstdc++12, libc++15.
// SRA GPRs that need to be spilled when calling a function with `preserve_all` ABI.
@@ -370,6 +419,9 @@ Arm64Emitter::Arm64Emitter(FEXCore::Context::ContextImpl *ctx, void* EmissionPtr
GeneralPairRegisters = x64::RAPair;
StaticFPRegisters = x64::SRAFPR;
GeneralFPRegisters = x64::RAFPR;
#ifdef _M_ARM_64EC
ConfiguredDynamicRegisterBase = std::span(x64::RA.begin(), 7);
#endif
}
else {
ConfiguredDynamicRegisterBase = std::span(x32::RA.begin() + 6, 8);
@@ -931,7 +983,9 @@ void Arm64Emitter::PushDynamicRegsAndLR(FEXCore::ARMEmitter::Register TmpReg) {
// Push the general registers.
PushGeneralRegisters(TmpReg, ConfiguredDynamicRegisterBase);
#ifndef _M_ARM_64EC
str(ARMEmitter::XReg::lr, TmpReg, 0);
#endif
}
void Arm64Emitter::PopDynamicRegsAndLR() {
@@ -943,7 +997,9 @@ void Arm64Emitter::PopDynamicRegsAndLR() {
// Pop GPRs second
PopGeneralRegisters(ConfiguredDynamicRegisterBase);
#ifndef _M_ARM_64EC
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
#endif
}
void Arm64Emitter::SpillForPreserveAllABICall(FEXCore::ARMEmitter::Register TmpReg, bool FPRs) {
@@ -37,17 +37,37 @@ namespace FEXCore::CPU {
// Contains the address to the currently available CPU state
constexpr auto STATE = FEXCore::ARMEmitter::XReg::x28;
#ifndef _M_ARM_64EC
// GPR temporaries. Only x3 can be used across spill boundaries
// so if these ever need to change, be very careful about that.
constexpr auto TMP1 = FEXCore::ARMEmitter::XReg::x0;
constexpr auto TMP2 = FEXCore::ARMEmitter::XReg::x1;
constexpr auto TMP3 = FEXCore::ARMEmitter::XReg::x2;
constexpr auto TMP4 = FEXCore::ARMEmitter::XReg::x3;
constexpr bool TMP_ABIARGS = true; // TMP{1-4} map to ABI arguments 0-3
constexpr bool TMP_ABIARGS = true;
// We pin r26/r27 as PF/AF respectively, this is internal FEX ABI.
constexpr auto REG_PF = FEXCore::ARMEmitter::Reg::r26;
constexpr auto REG_AF = FEXCore::ARMEmitter::Reg::r27;
// Vector temporaries
constexpr auto VTMP1 = FEXCore::ARMEmitter::VReg::v0;
constexpr auto VTMP2 = FEXCore::ARMEmitter::VReg::v1;
#else
constexpr auto TMP1 = FEXCore::ARMEmitter::XReg::x10;
constexpr auto TMP2 = FEXCore::ARMEmitter::XReg::x11;
constexpr auto TMP3 = FEXCore::ARMEmitter::XReg::x12;
constexpr auto TMP4 = FEXCore::ARMEmitter::XReg::x13;
constexpr bool TMP_ABIARGS = false;
// We pin r11/r12 as PF/AF respectively for arm64ec, as r26/r27 are used for SRA.
constexpr auto REG_PF = FEXCore::ARMEmitter::Reg::r9;
constexpr auto REG_AF = FEXCore::ARMEmitter::Reg::r24;
// Vector temporaries
constexpr auto VTMP1 = FEXCore::ARMEmitter::VReg::v16;
constexpr auto VTMP2 = FEXCore::ARMEmitter::VReg::v17;
#endif
// Predicate register temporaries (used when AVX support is enabled)
// PRED_TMP_16B indicates a predicate register that indicates the first 16 bytes set to 1.
@@ -55,9 +75,6 @@ constexpr auto VTMP2 = FEXCore::ARMEmitter::VReg::v1;
constexpr FEXCore::ARMEmitter::PRegister PRED_TMP_16B = FEXCore::ARMEmitter::PReg::p6;
constexpr FEXCore::ARMEmitter::PRegister PRED_TMP_32B = FEXCore::ARMEmitter::PReg::p7;
// We pin r26/r27 as PF/AF respectively, this is internal FEX ABI.
constexpr auto REG_PF = FEXCore::ARMEmitter::Reg::r26;
constexpr auto REG_AF = FEXCore::ARMEmitter::Reg::r27;
// This class contains common emitter utility functions that can
// be used by both Arm64 JIT and ARM64 Dispatcher
+3 -3
View File
@@ -98,7 +98,7 @@ constexpr uint32_t FAMILY_IDENTIFIER =
#endif
#ifdef _M_ARM_64
static uint32_t GetCycleCounterFrequency() {
uint32_t GetCycleCounterFrequency() {
uint64_t Result{};
__asm("mrs %[Res], CNTFRQ_EL0"
: [Res] "=r" (Result));
@@ -349,7 +349,7 @@ void CPUIDEmu::SetupHostHybridFlag() {
}
#else
static uint32_t GetCycleCounterFrequency() {
uint32_t GetCycleCounterFrequency() {
return 0;
}
@@ -803,7 +803,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_15h(uint32_t Leaf) const {
uint32_t FrequencyHz = GetCycleCounterFrequency();
if (FrequencyHz) {
Res.eax = 1;
Res.ebx = 1;
Res.ebx = CTX->Config.SmallTSCScale() ? FEXCore::Context::TSC_SCALE : 1;
Res.ecx = FrequencyHz;
}
return Res;
+3
View File
@@ -14,6 +14,8 @@ namespace Context {
class ContextImpl;
}
uint32_t GetCycleCounterFrequency();
// Debugging define to switch what family of CPU we execute as.
// Might be useful if an application makes an assumption about a CPU.
// #define CPUID_AMD
@@ -115,6 +117,7 @@ private:
bool Hybrid{};
uint32_t Cores{};
FEX_CONFIG_OPT(HideHypervisorBit, HIDEHYPERVISORBIT);
FEX_CONFIG_OPT(SmallTSCScale, SMALLTSCSCALE);
// XFEATURE_ENABLED_MASK
// Mask that configures what features are enabled on the CPU.
+12 -10
View File
@@ -99,6 +99,10 @@ namespace FEXCore::Context {
Symbols.InitFile();
}
if (FEXCore::GetCycleCounterFrequency() >= FEXCore::Context::TSC_SCALE_MAXIMUM) {
Config.SmallTSCScale = false;
}
// Track atomic TSO emulation configuration.
UpdateAtomicTSOEmulationConfig();
}
@@ -277,6 +281,7 @@ namespace FEXCore::Context {
.DispatcherBegin = Dispatcher->Start,
.DispatcherEnd = Dispatcher->End,
.AbsoluteLoopTopAddress = Dispatcher->AbsoluteLoopTopAddress,
.AbsoluteLoopTopAddressFillSRA = Dispatcher->AbsoluteLoopTopAddressFillSRA,
.SignalHandlerReturnAddress = Dispatcher->SignalHandlerReturnAddress,
.SignalHandlerReturnAddressRT = Dispatcher->SignalHandlerReturnAddressRT,
@@ -343,14 +348,6 @@ namespace FEXCore::Context {
Thread->ThreadManager.TID = FHU::Syscalls::gettid();
Thread->ThreadManager.PID = ::getpid();
if (Config.BlockJITNaming() ||
Config.GlobalJITNaming() ||
Config.LibraryJITNaming()) {
// Allocate a TLS JIT symbol buffer only if enabled.
Thread->SymbolBuffer = JITSymbols::AllocateBuffer();
}
SignalDelegation->RegisterTLSState(Thread);
if (ThunkHandler) {
ThunkHandler->RegisterTLSState(Thread);
}
@@ -415,6 +412,13 @@ namespace FEXCore::Context {
Thread->CurrentFrame->State.DeferredSignalRefCount.Store(0);
Thread->CurrentFrame->State.DeferredSignalFaultAddress = reinterpret_cast<Core::NonAtomicRefCounter<uint64_t>*>(FEXCore::Allocator::VirtualAlloc(4096));
if (Config.BlockJITNaming() ||
Config.GlobalJITNaming() ||
Config.LibraryJITNaming()) {
// Allocate a JIT symbol buffer only if enabled.
Thread->SymbolBuffer = JITSymbols::AllocateBuffer();
}
return Thread;
}
@@ -423,7 +427,6 @@ namespace FEXCore::Context {
#ifndef _WIN32
Alloc::OSAllocator::UninstallTLSData(Thread);
#endif
SignalDelegation->UninstallTLSState(Thread);
}
FEXCore::Allocator::VirtualFree(reinterpret_cast<void*>(Thread->CurrentFrame->State.DeferredSignalFaultAddress), 4096);
@@ -910,7 +913,6 @@ namespace FEXCore::Context {
#ifndef _WIN32
Alloc::OSAllocator::UninstallTLSData(Thread);
#endif
SignalDelegation->UninstallTLSState(Thread);
}
static void InvalidateGuestThreadCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length) {
@@ -85,6 +85,21 @@ DEF_OP(Add) {
}
}
DEF_OP(AddWithFlags) {
auto Op = IROp->C<IR::IROp_AddWithFlags>();
const uint8_t OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == 4 || OpSize == 8, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
uint64_t Const;
if (IsInlineConstant(Op->Src2, &Const)) {
adds(EmitSize, GetReg(Node), GetReg(Op->Src1.ID()), Const);
} else {
adds(EmitSize, GetReg(Node), GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()));
}
}
DEF_OP(AddShift) {
auto Op = IROp->C<IR::IROp_AddShift>();
const uint8_t OpSize = IROp->Size;
@@ -97,16 +112,24 @@ DEF_OP(AddShift) {
DEF_OP(AddNZCV) {
auto Op = IROp->C<IR::IROp_AddNZCV>();
const auto OpSize = IROp->Size;
const uint8_t OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == IR::i32Bit || OpSize == IR::i64Bit, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
auto Src1 = GetReg(Op->Src1.ID());
uint64_t Const;
if (IsInlineConstant(Op->Src2, &Const)) {
cmn(EmitSize, GetReg(Op->Src1.ID()), Const);
LOGMAN_THROW_AA_FMT(OpSize >= 4, "Constant not allowed here");
cmn(EmitSize, Src1, Const);
} else {
cmn(EmitSize, GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()));
unsigned Shift = OpSize < 4 ? (32 - (8 * OpSize)) : 0;
if (OpSize < 4) {
lsl(ARMEmitter::Size::i32Bit, TMP1, Src1, Shift);
cmn(EmitSize, TMP1, GetReg(Op->Src2.ID()), ARMEmitter::ShiftType::LSL, Shift);
} else {
cmn(EmitSize, Src1, GetReg(Op->Src2.ID()));
}
}
}
@@ -120,6 +143,36 @@ DEF_OP(AdcNZCV) {
adcs(EmitSize, ARMEmitter::Reg::zr, GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()));
}
DEF_OP(AdcWithFlags) {
auto Op = IROp->C<IR::IROp_AdcWithFlags>();
const auto OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == IR::i32Bit || OpSize == IR::i64Bit, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
adcs(EmitSize, GetReg(Node), GetZeroableReg(Op->Src1), GetReg(Op->Src2.ID()));
}
DEF_OP(Adc) {
auto Op = IROp->C<IR::IROp_Adc>();
const auto OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == IR::i32Bit || OpSize == IR::i64Bit, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
adc(EmitSize, GetReg(Node), GetZeroableReg(Op->Src1), GetReg(Op->Src2.ID()));
}
DEF_OP(SbbWithFlags) {
auto Op = IROp->C<IR::IROp_SbbWithFlags>();
const auto OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == IR::i32Bit || OpSize == IR::i64Bit, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
sbcs(EmitSize, GetReg(Node), GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()));
}
DEF_OP(SbbNZCV) {
auto Op = IROp->C<IR::IROp_SbbNZCV>();
const auto OpSize = IROp->Size;
@@ -130,6 +183,16 @@ DEF_OP(SbbNZCV) {
sbcs(EmitSize, ARMEmitter::Reg::zr, GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()));
}
DEF_OP(Sbb) {
auto Op = IROp->C<IR::IROp_Sbb>();
const auto OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == IR::i32Bit || OpSize == IR::i64Bit, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
sbc(EmitSize, GetReg(Node), GetZeroableReg(Op->Src1), GetReg(Op->Src2.ID()));
}
DEF_OP(TestNZ) {
auto Op = IROp->C<IR::IROp_TestNZ>();
const uint8_t OpSize = IROp->Size;
@@ -178,7 +241,7 @@ DEF_OP(Sub) {
if (IsInlineConstant(Op->Src2, &Const)) {
sub(EmitSize, GetReg(Node), GetReg(Op->Src1.ID()), Const);
} else {
sub(EmitSize, GetReg(Node), GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()));
sub(EmitSize, GetReg(Node), GetZeroableReg(Op->Src1), GetReg(Op->Src2.ID()));
}
}
@@ -192,21 +255,47 @@ DEF_OP(SubShift) {
sub(EmitSize, GetReg(Node), GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()), ConvertIRShiftType(Op->Shift), Op->ShiftAmount);
}
DEF_OP(SubNZCV) {
auto Op = IROp->C<IR::IROp_SubNZCV>();
const auto OpSize = IROp->Size;
DEF_OP(SubWithFlags) {
auto Op = IROp->C<IR::IROp_SubWithFlags>();
const uint8_t OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == IR::i32Bit || OpSize == IR::i64Bit, "Unsupported {} size: {}", __func__, OpSize);
LOGMAN_THROW_AA_FMT(OpSize == 4 || OpSize == 8, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
uint64_t Const;
if (IsInlineConstant(Op->Src2, &Const)) {
cmp(EmitSize, GetReg(Op->Src1.ID()), Const);
} else if (IsInlineConstant(Op->Src1, &Const)) {
LOGMAN_THROW_AA_FMT(Const == 0, "Only valid constant");
cmp(EmitSize, ARMEmitter::Reg::zr, GetReg(Op->Src2.ID()));
subs(EmitSize, GetReg(Node), GetZeroableReg(Op->Src1), Const);
} else {
cmp(EmitSize, GetReg(Op->Src1.ID()), GetReg(Op->Src2.ID()));
subs(EmitSize, GetReg(Node), GetZeroableReg(Op->Src1), GetReg(Op->Src2.ID()));
}
}
DEF_OP(SubNZCV) {
auto Op = IROp->C<IR::IROp_SubNZCV>();
const uint8_t OpSize = IROp->Size;
const auto EmitSize = OpSize == IR::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
uint64_t Const;
if (IsInlineConstant(Op->Src2, &Const)) {
LOGMAN_THROW_AA_FMT(OpSize >= 4, "Constant not allowed here");
cmp(EmitSize, GetReg(Op->Src1.ID()), Const);
} else {
unsigned Shift = OpSize < 4 ? (32 - (8 * OpSize)) : 0;
ARMEmitter::Register ShiftedSrc1 = GetZeroableReg(Op->Src1);
// Shift to fix flags for <32-bit ops.
// Any shift of zero is still zero so optimize out silly zero shifts.
if (OpSize < 4 && ShiftedSrc1 != ARMEmitter::Reg::zr) {
lsl(ARMEmitter::Size::i32Bit, TMP1, ShiftedSrc1, Shift);
ShiftedSrc1 = TMP1;
}
if (OpSize < 4) {
cmp(EmitSize, ShiftedSrc1, GetReg(Op->Src2.ID()), ARMEmitter::ShiftType::LSL, Shift);
} else {
cmp(EmitSize, ShiftedSrc1, GetReg(Op->Src2.ID()));
}
}
}
@@ -222,6 +311,20 @@ DEF_OP(RmifNZCV) {
rmif(GetReg(Op->Src.ID()).X(), Op->Rotate, Op->Mask);
}
DEF_OP(SetSmallNZV) {
auto Op = IROp->C<IR::IROp_SetSmallNZV>();
LOGMAN_THROW_A_FMT(CTX->HostFeatures.SupportsFlagM, "Unsupported flagm op");
const uint8_t OpSize = IROp->Size;
LOGMAN_THROW_AA_FMT(OpSize == 1 || OpSize == 2, "Unsupported {} size: {}", __func__, OpSize);
if (OpSize == 1) {
setf8(GetReg(Op->Src.ID()).W());
} else {
setf16(GetReg(Op->Src.ID()).W());
}
}
DEF_OP(AXFlag) {
LOGMAN_THROW_A_FMT(CTX->HostFeatures.SupportsFlagM2, "Unsupported flagm2 op");
axflag();
@@ -264,9 +367,7 @@ DEF_OP(CondAddNZCV) {
ARMEmitter::StatusFlags Flags = (ARMEmitter::StatusFlags)Op->FalseNZCV;
uint64_t Const = 0;
auto Src1 = IsInlineConstant(Op->Src1, &Const) ? ARMEmitter::Reg::zr :
GetReg(Op->Src1.ID());
LOGMAN_THROW_A_FMT(Const == 0, "Unsupported inline constant");
auto Src1 = GetZeroableReg(Op->Src1);
if (IsInlineConstant(Op->Src2, &Const)) {
ccmn(EmitSize, Src1, Const, Flags, MapSelectCC(Op->Cond));
@@ -1470,11 +1571,8 @@ DEF_OP(NZCVSelect) {
csetm(EmitSize, Dst, cc);
else
cset(EmitSize, Dst, cc);
} else if (is_const_false) {
LOGMAN_THROW_A_FMT(const_false == 0, "NZCVSelect: unsupported constant");
csel(EmitSize, Dst, GetReg(Op->TrueVal.ID()), ARMEmitter::Reg::zr, cc);
} else {
csel(EmitSize, Dst, GetReg(Op->TrueVal.ID()), GetReg(Op->FalseVal.ID()), cc);
csel(EmitSize, Dst, GetReg(Op->TrueVal.ID()), GetZeroableReg(Op->FalseVal), cc);
}
}
@@ -310,8 +310,7 @@ DEF_OP(AtomicSwap) {
OpSize == 1 ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
if (CTX->HostFeatures.SupportsAtomics) {
mov(EmitSize, TMP2, Src);
ldswpal(SubEmitSize, TMP2, GetReg(Node), MemSrc);
ldswpal(SubEmitSize, Src, GetReg(Node), MemSrc);
}
else {
ARMEmitter::BackwardLabel LoopTop;
@@ -116,6 +116,16 @@ private:
return PhyReg;
}
[[nodiscard]] FEXCore::ARMEmitter::Register GetZeroableReg(IR::OrderedNodeWrapper Src) const {
uint64_t Const;
if (IsInlineConstant(Src, &Const)) {
LOGMAN_THROW_AA_FMT(Const == 0, "Only valid constant");
return ARMEmitter::Reg::zr;
} else {
return GetReg(Src.ID());
}
}
// Converts IR-base shift type to ARMEmitter shift type.
// Will be a no-op, only a type conversion since the two definitions match.
[[nodiscard]] ARMEmitter::ShiftType ConvertIRShiftType(IR::ShiftType Shift) const {
+193 -262
View File
@@ -338,129 +338,48 @@ void OpDispatchBuilder::CallbackReturnOp(OpcodeArgs) {
}
void OpDispatchBuilder::SecondaryALUOp(OpcodeArgs) {
FEXCore::IR::IROps IROp;
FEXCore::IR::IROps IROp, AtomicIROp;
#define OPD(group, prefix, Reg) (((group - FEXCore::X86Tables::TYPE_GROUP_1) << 6) | (prefix) << 3 | (Reg))
switch (Op->OP) {
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 0):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 0):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 0):
IROp = FEXCore::IR::IROps::OP_ADD;
AtomicIROp = FEXCore::IR::IROps::OP_ATOMICFETCHADD;
break;
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 1):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 1):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 1):
IROp = FEXCore::IR::IROps::OP_OR;
AtomicIROp = FEXCore::IR::IROps::OP_ATOMICFETCHOR;
break;
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 4):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 4):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 4):
IROp = FEXCore::IR::IROps::OP_ANDWITHFLAGS;
AtomicIROp = FEXCore::IR::IROps::OP_ATOMICFETCHAND;
break;
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 5):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 5):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 5):
IROp = FEXCore::IR::IROps::OP_SUB;
AtomicIROp = FEXCore::IR::IROps::OP_ATOMICFETCHSUB;
break;
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 6):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 6):
case OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 6):
IROp = FEXCore::IR::IROps::OP_XOR;
AtomicIROp = FEXCore::IR::IROps::OP_ATOMICFETCHXOR;
break;
default:
IROp = FEXCore::IR::IROps::OP_LAST;
AtomicIROp = FEXCore::IR::IROps::OP_LAST;
LOGMAN_MSG_A_FMT("Unknown ALU Op: 0x{:x}", Op->OP);
break;
};
#undef OPD
// Logical ops can tolerate garbage in the upper bits, so don't mask.
bool AllowUpperGarbage = IROp == FEXCore::IR::IROps::OP_ANDWITHFLAGS ||
IROp == FEXCore::IR::IROps::OP_XOR ||
IROp == FEXCore::IR::IROps::OP_OR;
// X86 basic ALU ops just do the operation between the destination and a single source
uint8_t Size = GetDstSize(Op);
auto Src = LoadSource(GPRClass, Op, Op->Src[1], Op->Flags, {.AllowUpperGarbage = AllowUpperGarbage || Size >= 4});
OrderedNode *Result{};
OrderedNode *Dest{};
if (DestIsLockedMem(Op)) {
HandledLock = true;
auto DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
switch (IROp) {
case FEXCore::IR::IROps::OP_ADD: {
Dest = _AtomicFetchAdd(IR::SizeToOpSize(Size), Src, DestMem);
Result = _Add(IR::SizeToOpSize(std::max<uint8_t>(4u, std::max(GetOpSize(Dest), GetOpSize(Src)))), Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_SUB: {
Dest = _AtomicFetchSub(IR::SizeToOpSize(Size), Src, DestMem);
Result = _Sub(IR::SizeToOpSize(std::max<uint8_t>(4u, std::max(GetOpSize(Dest), GetOpSize(Src)))), Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_OR: {
Dest = _AtomicFetchOr(IR::SizeToOpSize(Size), Src, DestMem);
Result = _Or(IR::SizeToOpSize(std::max<uint8_t>(4u, std::max(GetOpSize(Dest), GetOpSize(Src)))), Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_ANDWITHFLAGS: {
Dest = _AtomicFetchAnd(IR::SizeToOpSize(Size), Src, DestMem);
Result = _AndWithFlags(IR::SizeToOpSize(std::max(GetOpSize(Dest), GetOpSize(Src))), Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_XOR: {
Dest = _AtomicFetchXor(IR::SizeToOpSize(Size), Src, DestMem);
Result = _Xor(IR::SizeToOpSize(std::max<uint8_t>(4u, std::max(GetOpSize(Dest), GetOpSize(Src)))), Dest, Src);
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Atomic IR Op: {}", ToUnderlying(IROp));
break;
}
}
else {
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = AllowUpperGarbage || Size >= 4});
if (IROp != FEXCore::IR::IROps::OP_ANDWITHFLAGS)
Size = std::max<uint8_t>(4u, Size);
DeriveOp(ALUOp, IROp, _AndWithFlags(IR::SizeToOpSize(Size), Dest, Src));
Result = ALUOp;
StoreResult(GPRClass, Op, Result, -1);
}
// Flags set
{
switch (IROp) {
case FEXCore::IR::IROps::OP_ADD:
GenerateFlags_ADD(Op, Result, Dest, Src);
break;
case FEXCore::IR::IROps::OP_SUB:
GenerateFlags_SUB(Op, Result, Dest, Src);
break;
case FEXCore::IR::IROps::OP_XOR:
case FEXCore::IR::IROps::OP_OR: {
GenerateFlags_Logical(Op, Result, Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_ANDWITHFLAGS: {
InvalidateDeferredFlags();
// SF/ZF/CF/OF
CachedNZCV = nullptr;
PossiblySetNZCVBits = (1u << 31) | (1u << 30);
NZCVDirty = false;
// PF/AF
CalculatePF(Result);
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
break;
}
default: break;
}
}
ALUOpImpl(Op, IROp, AtomicIROp, 1);
}
template<uint32_t SrcIndex>
@@ -468,66 +387,64 @@ void OpDispatchBuilder::ADCOp(OpcodeArgs) {
// Calculate flags early.
CalculateDeferredFlags();
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags);
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags, {.AllowUpperGarbage = true});
uint8_t Size = GetDstSize(Op);
const auto OpSize = IR::SizeToOpSize(std::max<uint8_t>(4u, Size));
auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
auto ALUOp = _Add(OpSize, Src, CF);
OrderedNode *Result{};
OrderedNode *Before{};
if (DestIsLockedMem(Op)) {
auto ALUOp = _Adc(OpSize, _Constant(0), Src);
HandledLock = true;
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
Before = _AtomicFetchAdd(IR::SizeToOpSize(Size), ALUOp, DestMem);
Result = _Add(OpSize, Before, ALUOp);
}
else {
Before = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
Result = _Add(OpSize, Before, ALUOp);
StoreResult(GPRClass, Op, Result, -1);
Before = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true});
}
if (Size < 4)
Result = _Bfe(IR::SizeToOpSize(std::max<uint8_t>(4u, Size)), Size * 8, 0, Result);
GenerateFlags_ADC(Op, Result, Before, Src, CF);
OrderedNode *Result = CalculateFlags_ADC(Size, Before, Src);
if (!DestIsLockedMem(Op))
StoreResult(GPRClass, Op, Result, -1);
}
template<uint32_t SrcIndex, bool SetFlags>
template<uint32_t SrcIndex>
void OpDispatchBuilder::SBBOp(OpcodeArgs) {
// Calculate flags early.
CalculateDeferredFlags();
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags);
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags, {.AllowUpperGarbage = true});
auto Size = GetDstSize(Op);
const auto OpSize = IR::SizeToOpSize(std::max<uint8_t>(4u, Size));
auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
auto ALUOp = _Add(OpSize, Src, CF);
OrderedNode *Result{};
OrderedNode *Before{};
if (DestIsLockedMem(Op)) {
HandledLock = true;
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
Before = _AtomicFetchSub(IR::SizeToOpSize(Size), ALUOp, DestMem);
Result = _Sub(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Before, ALUOp);
auto SrcPlusCF = _Adc(OpSize, _Constant(0), Src);
Before = _AtomicFetchSub(IR::SizeToOpSize(Size), SrcPlusCF, DestMem);
}
else {
Before = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
Result = _Sub(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Before, ALUOp);
StoreResult(GPRClass, Op, Result, -1);
Before = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true});
}
if (SetFlags) {
if (Size < 4) {
Result = _Bfe(IR::SizeToOpSize(std::max<uint8_t>(4u, Size)), Size * 8, 0, Result);
}
GenerateFlags_SBB(Op, Result, Before, Src, CF);
}
Result = CalculateFlags_SBB(Size, Before, Src);
if (!DestIsLockedMem(Op))
StoreResult(GPRClass, Op, Result, -1);
}
void OpDispatchBuilder::SALCOp(OpcodeArgs) {
CalculateDeferredFlags();
auto Result = _NZCVSelect(OpSize::i32Bit, CondClassType{COND_UGE} /* CF = 1 */,
_Constant(0xffffffff), _Constant(0));
StoreResult(GPRClass, Op, Result, -1);
}
void OpDispatchBuilder::PUSHOp(OpcodeArgs) {
@@ -1303,16 +1220,8 @@ void OpDispatchBuilder::TESTOp(OpcodeArgs) {
Src = Dest;
}
InvalidateDeferredFlags();
// SF/ZF/CF/OF
OrderedNode *ALUOp = _AndWithFlags(IR::SizeToOpSize(Size), Dest, Src);
CachedNZCV = nullptr;
PossiblySetNZCVBits = (1u << 31) | (1u << 30);
NZCVDirty = false;
// PF/AF
CalculatePF(ALUOp);
HandleNZ00Write();
CalculatePF(_AndWithFlags(IR::SizeToOpSize(Size), Dest, Src));
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
}
@@ -1363,14 +1272,9 @@ template<uint32_t SrcIndex>
void OpDispatchBuilder::CMPOp(OpcodeArgs) {
// CMP is an instruction that does a SUB between the sources
// Result isn't stored in result, only writes to flags
auto Size = GetDstSize(Op);
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags, {.AllowUpperGarbage = Size >= 4});
OrderedNode *Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = Size >= 4});
auto ALUOp = _Sub(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Dest, Src);
OrderedNode *Result = ALUOp;
GenerateFlags_SUB(Op, Result, Dest, Src);
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags, {.AllowUpperGarbage = true});
OrderedNode *Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true});
GenerateFlags_SUB(Op, Dest, Src);
}
void OpDispatchBuilder::CQOOp(OpcodeArgs) {
@@ -1808,9 +1712,7 @@ void OpDispatchBuilder::SHLDOp(OpcodeArgs) {
StoreResult(GPRClass, Op, Res, -1);
if (Size != 64) {
Res = _Bfe(OpSize::i64Bit, Size, 0, Res);
}
// No need to mask result, upper garbage is ignored in the flag calc
GenerateFlags_ShiftLeft(Op, Res, Dest, Shift);
}
@@ -3196,29 +3098,23 @@ void OpDispatchBuilder::XADDOp(OpcodeArgs) {
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
OrderedNode *Result;
const auto Size = GetSrcBitSize(Op);
const auto OpSize = Size == 64 ? OpSize::i64Bit : OpSize::i32Bit;
if (Op->Dest.IsGPR()) {
// If this is a GPR then we can just do an Add
Result = _Add(OpSize, Dest, Src);
Result = CalculateFlags_ADD(GetSrcSize(Op), Dest, Src);
// Previous value in dest gets stored in src
StoreResult(GPRClass, Op, Op->Src[0], Dest, -1);
// Calculated value gets stored in dst (order is important if dst is same as src)
StoreResult(GPRClass, Op, Result, -1);
GenerateFlags_ADD(Op, Result, Dest, Src);
}
else {
HandledLock = Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK;
Dest = AppendSegmentOffset(Dest, Op->Flags);
auto Before = _AtomicFetchAdd(OpSizeFromSrc(Op), Src, Dest);
StoreResult(GPRClass, Op, Op->Src[0], Before, -1);
Result = _Add(OpSize, Before, Src); // Seperate result just for flags
GenerateFlags_ADD(Op, Result, Before, Src);
CalculateFlags_ADD(GetSrcSize(Op), Before, Src);
}
}
@@ -3596,12 +3492,31 @@ void OpDispatchBuilder::SGDTOp(OpcodeArgs) {
_StoreMemAutoTSO(GPRClass, GDTStoreSize, _Add(OpSize::i64Bit, DestAddress, _Constant(2)), _Constant(GDTAddress));
}
void OpDispatchBuilder::RDTSCOp(OpcodeArgs) {
OpDispatchBuilder::CycleCounterPair OpDispatchBuilder::CycleCounter() {
OrderedNode *CounterLow{};
OrderedNode *CounterHigh{};
auto Counter = _CycleCounter();
auto CounterLow = _Bfe(OpSize::i64Bit, 32, 0, Counter);
auto CounterHigh = _Bfe(OpSize::i64Bit, 32, 32, Counter);
StoreGPRRegister(X86State::REG_RAX, CounterLow);
StoreGPRRegister(X86State::REG_RDX, CounterHigh);
if (CTX->Config.SmallTSCScale()) {
const auto ShiftAmount = FEXCore::ilog2(FEXCore::Context::TSC_SCALE);
CounterLow = _Lshl(OpSize::i32Bit, Counter, _Constant(ShiftAmount));
CounterHigh = _Lshr(OpSize::i64Bit, Counter, _Constant(32 - ShiftAmount));
}
else {
CounterLow = _Bfe(OpSize::i64Bit, 32, 0, Counter);
CounterHigh = _Bfe(OpSize::i64Bit, 32, 32, Counter);
}
return {
.CounterLow = CounterLow,
.CounterHigh = CounterHigh,
};
}
void OpDispatchBuilder::RDTSCOp(OpcodeArgs) {
auto Counter = CycleCounter();
StoreGPRRegister(X86State::REG_RAX, Counter.CounterLow);
StoreGPRRegister(X86State::REG_RDX, Counter.CounterHigh);
}
void OpDispatchBuilder::INCOp(OpcodeArgs) {
@@ -3628,12 +3543,28 @@ void OpDispatchBuilder::INCOp(OpcodeArgs) {
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = Size >= 32});
}
Result = _Add(Size == 64 ? OpSize::i64Bit : OpSize::i32Bit, Dest, OneConst);
CalculateDeferredFlags();
if (Size < 32 && CTX->HostFeatures.SupportsFlagM) {
// Addition producing upper garbage
Result = _Add(OpSize::i32Bit, Dest, OneConst);
CalculatePF(Result);
CalculateAF(Dest, OneConst);
// Correctly set NZ flags, preserving C
HandleNZCV_RMW();
_SetSmallNZV(OpSizeFromSrc(Op), Result);
// Fix up V flag. INC overflows only when incrementing a positive and
// getting a negative. So compare the sign bits to calculate V.
_RmifNZCV(_Andn(OpSize::i32Bit, Result, Dest), Size - 1, 1);
} else {
Result = CalculateFlags_ADD(OpSizeFromSrc(Op), Dest, OneConst, false);
}
if (!IsLocked) {
StoreResult(GPRClass, Op, Result, -1);
}
GenerateFlags_ADD(Op, Result, Dest, OneConst, false);
}
void OpDispatchBuilder::DECOp(OpcodeArgs) {
@@ -3654,17 +3585,35 @@ void OpDispatchBuilder::DECOp(OpcodeArgs) {
HandledLock = true;
auto DestAddress = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
DestAddress = AppendSegmentOffset(DestAddress, Op->Flags);
Dest = _AtomicFetchSub(OpSizeFromSrc(Op), OneConst, DestAddress);
// Use Add instead of Sub to avoid a NEG
Dest = _AtomicFetchAdd(OpSizeFromSrc(Op), _Constant(Size, -1), DestAddress);
} else {
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = Size >= 32});
}
Result = _Sub(Size == 64 ? OpSize::i64Bit : OpSize::i32Bit, Dest, OneConst);
CalculateDeferredFlags();
if (Size < 32 && CTX->HostFeatures.SupportsFlagM) {
// Subtraction producing upper garbage
Result = _Sub(OpSize::i32Bit, Dest, OneConst);
CalculatePF(Result);
CalculateAF(Dest, OneConst);
// Correctly set NZ flags, preserving C
HandleNZCV_RMW();
_SetSmallNZV(OpSizeFromSrc(Op), Result);
// Fix up V flag. DEC overflows only when decrementing a negative and
// getting a positive. So compare the sign bits to calculate V.
_RmifNZCV(_Andn(OpSize::i32Bit, Dest, Result), Size - 1, 1);
} else {
Result = CalculateFlags_SUB(OpSizeFromSrc(Op), Dest, OneConst, false);
}
if (!IsLocked) {
StoreResult(GPRClass, Op, Result, -1);
}
GenerateFlags_SUB(Op, Result, Dest, OneConst, false);
}
void OpDispatchBuilder::STOSOp(OpcodeArgs) {
@@ -3794,8 +3743,7 @@ void OpDispatchBuilder::CMPSOp(OpcodeArgs) {
auto Src1 = _LoadMemAutoTSO(GPRClass, Size, Dest_RDI, Size);
auto Src2 = _LoadMemAutoTSO(GPRClass, Size, Dest_RSI, Size);
OrderedNode* Result = _Sub(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Src2, Src1);
GenerateFlags_SUB(Op, Result, Src2, Src1);
GenerateFlags_SUB(Op, Src2, Src1);
auto DF = GetRFLAG(FEXCore::X86State::RFLAG_DF_LOC);
auto SizeConst = _Constant(Size);
@@ -3851,8 +3799,7 @@ void OpDispatchBuilder::CMPSOp(OpcodeArgs) {
auto Src1 = _LoadMemAutoTSO(GPRClass, Size, Dest_RDI, Size);
auto Src2 = _LoadMem(GPRClass, Size, Dest_RSI, Size);
OrderedNode* Result = _Sub(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Src2, Src1);
GenerateFlags_SUB(Op, Result, Src2, Src1);
GenerateFlags_SUB(Op, Src2, Src1);
// Calculate flags early.
CalculateDeferredFlags();
@@ -3873,9 +3820,8 @@ void OpDispatchBuilder::CMPSOp(OpcodeArgs) {
Dest_RSI = _Add(OpSize::i64Bit, Dest_RSI, PtrDir);
StoreGPRRegister(X86State::REG_RSI, Dest_RSI);
OrderedNode *ZF = GetRFLAG(FEXCore::X86State::RFLAG_ZF_RAW_LOC);
CalculateDeferredFlags();
InternalCondJump = CondJump(ZF, {REPE ? COND_NEQ : COND_EQ});
InternalCondJump = CondJumpNZCV({REPE ? COND_EQ : COND_NEQ});
// Jump back to the start if we have more work to do
SetTrueJumpTarget(InternalCondJump, LoopStart);
@@ -4002,11 +3948,10 @@ void OpDispatchBuilder::SCASOp(OpcodeArgs) {
OrderedNode *Dest_RDI = LoadGPRRegister(X86State::REG_RDI);
Dest_RDI = AppendSegmentOffset(Dest_RDI, 0, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
auto Src1 = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
auto Src1 = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, {.AllowUpperGarbage = true});
auto Src2 = _LoadMemAutoTSO(GPRClass, Size, Dest_RDI, Size);
OrderedNode* Result = _Sub(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Src1, Src2);
GenerateFlags_SUB(Op, Result, Src1, Src2);
GenerateFlags_SUB(Op, Src1, Src2);
auto DF = GetRFLAG(FEXCore::X86State::RFLAG_DF_LOC);
auto SizeConst = _Constant(Size);
@@ -4054,11 +3999,10 @@ void OpDispatchBuilder::SCASOp(OpcodeArgs) {
Dest_RDI = AppendSegmentOffset(Dest_RDI, 0, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
auto Src1 = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
auto Src1 = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, {.AllowUpperGarbage = true});
auto Src2 = _LoadMemAutoTSO(GPRClass, Size, Dest_RDI, Size);
OrderedNode* Result = _Sub(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Src1, Src2);
GenerateFlags_SUB(Op, Result, Src1, Src2);
GenerateFlags_SUB(Op, Src1, Src2);
// Calculate flags early.
CalculateDeferredFlags();
@@ -4076,9 +4020,8 @@ void OpDispatchBuilder::SCASOp(OpcodeArgs) {
TailDest_RDI = _Add(OpSize::i64Bit, TailDest_RDI, PtrDir);
StoreGPRRegister(X86State::REG_RDI, TailDest_RDI);
OrderedNode *ZF = GetRFLAG(FEXCore::X86State::RFLAG_ZF_RAW_LOC);
CalculateDeferredFlags();
InternalCondJump = CondJump(ZF, {REPE ? COND_NEQ : COND_EQ});
InternalCondJump = CondJumpNZCV({REPE ? COND_EQ : COND_NEQ});
// Jump back to the start if we have more work to do
SetTrueJumpTarget(InternalCondJump, LoopStart);
@@ -4151,24 +4094,19 @@ void OpDispatchBuilder::NEGOp(OpcodeArgs) {
auto Size = GetSrcSize(Op);
auto ZeroConst = _Constant(0);
OrderedNode *Dest{};
OrderedNode *Result{};
if (DestIsLockedMem(Op)) {
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
Dest = _AtomicFetchNeg(IR::SizeToOpSize(Size), DestMem);
Result = _Neg(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Dest);
OrderedNode *Dest = _AtomicFetchNeg(IR::SizeToOpSize(Size), DestMem);
CalculateFlags_SUB(Size, ZeroConst, Dest);
}
else {
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
Result = _Neg(Size == 8 ? OpSize::i64Bit : OpSize::i32Bit, Dest);
OrderedNode *Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true});
OrderedNode *Result = CalculateFlags_SUB(Size, ZeroConst, Dest);
StoreResult(GPRClass, Op, Result, -1);
}
GenerateFlags_SUB(Op, Result, ZeroConst, Dest);
}
void OpDispatchBuilder::DIVOp(OpcodeArgs) {
@@ -4378,8 +4316,7 @@ void OpDispatchBuilder::CMPXCHGOp(OpcodeArgs) {
}
// Compare RAX with the destination, setting flags accordingly.
OrderedNode *Result = _Sub(IR::SizeToOpSize(GPRSize), Src3Lower, Src1Lower);
GenerateFlags_SUB(Op, Result, Src3Lower, Src1Lower);
GenerateFlags_SUB(Op, Src3Lower, Src1Lower);
CalculateDeferredFlags();
if (!Trivial) {
@@ -4445,11 +4382,7 @@ void OpDispatchBuilder::CMPXCHGOp(OpcodeArgs) {
// RAX gets the result of the CAS op
StoreGPRRegister(X86State::REG_RAX, RAXResult, Size);
const auto Size = GetDstBitSize(Op);
OrderedNode *Result = _Sub(Size == 64 ? OpSize::i64Bit : OpSize::i32Bit, Src3Lower, CASResult);
GenerateFlags_SUB(Op, Result, Src3Lower, CASResult);
GenerateFlags_SUB(Op, Src3Lower, CASResult);
}
}
@@ -4853,7 +4786,12 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(RegisterClassType Class, X
if (!IsVSIB && Operand.Data.SIB.Index != FEXCore::X86State::REG_INVALID && Operand.Data.SIB.Base != FEXCore::X86State::REG_INVALID) {
auto Base = LoadGPRRegister(Operand.Data.SIB.Base, GPRSize);
auto Index = LoadGPRRegister(Operand.Data.SIB.Index, GPRSize);
Tmp = _AddShift(IR::SizeToOpSize(GPRSize), Base, Index, ShiftType::LSL, FEXCore::ilog2(Operand.Data.SIB.Scale));
if (Operand.Data.SIB.Scale == 1) {
Tmp = _Add(IR::SizeToOpSize(GPRSize), Base, Index);
}
else {
Tmp = _AddShift(IR::SizeToOpSize(GPRSize), Base, Index, ShiftType::LSL, FEXCore::ilog2(Operand.Data.SIB.Scale));
}
}
else {
// NOTE: VSIB cannot have the index * scale portion calculated ahead of time,
@@ -5107,7 +5045,12 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
if (Operand.Data.SIB.Index != FEXCore::X86State::REG_INVALID && Operand.Data.SIB.Base != FEXCore::X86State::REG_INVALID) {
auto Base = LoadGPRRegister(Operand.Data.SIB.Base, GPRSize);
auto Index = LoadGPRRegister(Operand.Data.SIB.Index, GPRSize);
Tmp = _AddShift(IR::SizeToOpSize(GPRSize), Base, Index, ShiftType::LSL, FEXCore::ilog2(Operand.Data.SIB.Scale));
if (Operand.Data.SIB.Scale == 1) {
Tmp = _Add(IR::SizeToOpSize(GPRSize), Base, Index);
}
else {
Tmp = _AddShift(IR::SizeToOpSize(GPRSize), Base, Index, ShiftType::LSL, FEXCore::ilog2(Operand.Data.SIB.Scale));
}
}
else {
if (Operand.Data.SIB.Index != FEXCore::X86State::REG_INVALID) {
@@ -5220,7 +5163,24 @@ void OpDispatchBuilder::MOVGPRNTOp(OpcodeArgs) {
StoreResult(GPRClass, Op, Src, 1, MemoryAccessType::STREAM);
}
void OpDispatchBuilder::ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp) {
void OpDispatchBuilder::ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, unsigned SrcIdx) {
/* On x86, the canonical way to zero a register is XOR with itself... because
* modern x86 detects this pattern in hardware. arm64 does not detect this
* pattern, we should do it like the x86 hardware would. On arm64, "mov x0,
* #0" is faster than "eor x0, x0, x0". Additionally this lets more constant
* folding kick in for flags.
*/
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) {
auto Result = _Constant(0);
StoreResult(GPRClass, Op, Result, -1);
GenerateFlags_Logical(Op, Result, Result, Result);
return;
}
auto Size = GetDstSize(Op);
auto RoundedSize = Size;
@@ -5229,14 +5189,8 @@ void OpDispatchBuilder::ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCor
const auto OpSize = IR::SizeToOpSize(RoundedSize);
// Logical ops can tolerate garbage in the upper bits, so don't mask.
bool AllowUpperGarbage = ALUIROp == FEXCore::IR::IROps::OP_ANDWITHFLAGS ||
ALUIROp == FEXCore::IR::IROps::OP_XOR ||
ALUIROp == FEXCore::IR::IROps::OP_OR;
// X86 basic ALU ops just do the operation between the destination and a single source
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags,
{.AllowUpperGarbage = AllowUpperGarbage || Size >= 4});
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[SrcIdx], Op->Flags, {.AllowUpperGarbage = true});
OrderedNode *Result{};
OrderedNode *Dest{};
@@ -5248,68 +5202,43 @@ void OpDispatchBuilder::ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCor
DeriveOp(FetchOp, AtomicFetchOp, _AtomicFetchAdd(IR::SizeToOpSize(Size), Src, DestMem));
Dest = FetchOp;
DeriveOp(ALUOp, ALUIROp, _AndWithFlags(OpSize, Dest, Src));
Result = ALUOp;
}
else {
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags,
{.AllowUpperGarbage = AllowUpperGarbage || Size >= 4});
/* On x86, the canonical way to zero a register is XOR with itself...
* because modern x86 detects this pattern in hardware. arm64 does not
* detect this pattern, we should do it like the x86 hardware would. On
* arm64, "mov x0, #0" is faster than "eor x0, x0, x0". Additionally this
* lets more constant folding kick in for flags.
*/
if (ALUIROp == FEXCore::IR::IROps::OP_XOR &&
Op->Dest.IsGPR() && Op->Src[0].IsGPR() &&
Op->Dest.Data.GPR == Op->Src[0].Data.GPR) {
Result = _Constant(0);
} else {
DeriveOp(ALUOp, ALUIROp, _AndWithFlags(OpSize, Dest, Src));
Result = ALUOp;
}
StoreResult(GPRClass, Op, Result, -1);
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = true});
}
DeriveOp(ALUOp, ALUIROp, _AndWithFlags(OpSize, Dest, Src));
Result = ALUOp;
// Flags set
{
switch (ALUIROp) {
case FEXCore::IR::IROps::OP_ADD:
GenerateFlags_ADD(Op, Result, Dest, Src);
break;
case FEXCore::IR::IROps::OP_SUB:
GenerateFlags_SUB(Op, Result, Dest, Src);
break;
case FEXCore::IR::IROps::OP_XOR:
case FEXCore::IR::IROps::OP_OR: {
GenerateFlags_Logical(Op, Result, Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_ANDWITHFLAGS: {
InvalidateDeferredFlags();
// SF/ZF/CF/OF
CachedNZCV = nullptr;
PossiblySetNZCVBits = (1u << 31) | (1u << 30);
NZCVDirty = false;
// PF/AF
CalculatePF(Result);
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
break;
}
default: break;
}
switch (ALUIROp) {
case FEXCore::IR::IROps::OP_ADD:
Result = CalculateFlags_ADD(Size, Dest, Src);
break;
case FEXCore::IR::IROps::OP_SUB:
Result = CalculateFlags_SUB(Size, Dest, Src);
break;
case FEXCore::IR::IROps::OP_XOR:
case FEXCore::IR::IROps::OP_OR: {
GenerateFlags_Logical(Op, Result, Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_ANDWITHFLAGS: {
HandleNZ00Write();
CalculatePF(Result);
_InvalidateFlags(1 << X86State::RFLAG_AF_RAW_LOC);
break;
}
default: break;
}
if (!DestIsLockedMem(Op))
StoreResult(GPRClass, Op, Result, -1);
}
template<FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp>
void OpDispatchBuilder::ALUOp(OpcodeArgs) {
ALUOpImpl(Op, ALUIROp, AtomicFetchOp);
ALUOpImpl(Op, ALUIROp, AtomicFetchOp, 0);
}
void OpDispatchBuilder::INTOp(OpcodeArgs) {
@@ -5326,6 +5255,9 @@ void OpDispatchBuilder::INTOp(OpcodeArgs) {
constexpr uint8_t SYSCALL_LITERAL = 0x2E;
#endif
if (Literal == SYSCALL_LITERAL) {
if (CTX->Config.Is64BitMode()) [[unlikely]] {
ERROR_AND_DIE_FMT("[Unsupported] Trying to execute 32-bit syscall from a 64-bit process.");
}
// Syscall on linux
SyscallOp<false>(Op);
return;
@@ -5391,11 +5323,11 @@ void OpDispatchBuilder::INTOp(OpcodeArgs) {
}
if (Op->OP == 0xCE) { // Conditional to only break if Overflow == 1
auto Flag = GetRFLAG(FEXCore::X86State::RFLAG_OF_RAW_LOC);
CalculateDeferredFlags();
// If condition doesn't hold then keep going
auto CondJump_ = CondJump(Flag, {COND_EQ});
// COND_FNU means OF == 0
auto CondJump_ = CondJumpNZCV({COND_FNU});
auto FalseBlock = CreateNewCodeBlockAfter(GetCurrentBlock());
SetFalseJumpTarget(CondJump_, FalseBlock);
SetCurrentCodeBlock(FalseBlock);
@@ -5517,13 +5449,12 @@ void OpDispatchBuilder::RDTSCPOp(OpcodeArgs) {
// - Explicitly use an LFENCE after RDTSCP if you want to block this behaviour
_Fence({FEXCore::IR::Fence_Load});
auto Counter = _CycleCounter();
auto CounterLow = _Bfe(OpSize::i64Bit, 32, 0, Counter);
auto CounterHigh = _Bfe(OpSize::i64Bit, 32, 32, Counter);
auto Counter = CycleCounter();
auto ID = _ProcessorID();
StoreGPRRegister(X86State::REG_RAX, CounterLow);
StoreGPRRegister(X86State::REG_RAX, Counter.CounterLow);
StoreGPRRegister(X86State::REG_RCX, ID);
StoreGPRRegister(X86State::REG_RDX, CounterHigh);
StoreGPRRegister(X86State::REG_RDX, Counter.CounterHigh);
}
void OpDispatchBuilder::CRC32(OpcodeArgs) {
@@ -6106,7 +6037,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{0x10, 6, &OpDispatchBuilder::ADCOp<0>},
{0x18, 6, &OpDispatchBuilder::SBBOp<0, true>},
{0x18, 6, &OpDispatchBuilder::SBBOp<0>},
{0x20, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_ANDWITHFLAGS, FEXCore::IR::IROps::OP_ATOMICFETCHAND>},
@@ -6188,7 +6119,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{0xCE, 1, &OpDispatchBuilder::INTOp},
{0xD4, 1, &OpDispatchBuilder::AAMOp},
{0xD5, 1, &OpDispatchBuilder::AADOp},
{0xD6, 1, &OpDispatchBuilder::SBBOp<0, false>},
{0xD6, 1, &OpDispatchBuilder::SALCOp},
};
constexpr std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr> BaseOpTable_64[] = {
@@ -6350,7 +6281,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 0), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 1), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 2), 1, &OpDispatchBuilder::ADCOp<1>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 3), 1, &OpDispatchBuilder::SBBOp<1, true>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 3), 1, &OpDispatchBuilder::SBBOp<1>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 4), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 5), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 6), 1, &OpDispatchBuilder::SecondaryALUOp},
@@ -6359,7 +6290,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 0), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 1), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 2), 1, &OpDispatchBuilder::ADCOp<1>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 3), 1, &OpDispatchBuilder::SBBOp<1, true>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 3), 1, &OpDispatchBuilder::SBBOp<1>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 4), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 5), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 6), 1, &OpDispatchBuilder::SecondaryALUOp},
@@ -6368,7 +6299,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 0), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 1), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 2), 1, &OpDispatchBuilder::ADCOp<1>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 3), 1, &OpDispatchBuilder::SBBOp<1, true>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 3), 1, &OpDispatchBuilder::SBBOp<1>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 4), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 5), 1, &OpDispatchBuilder::SecondaryALUOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 6), 1, &OpDispatchBuilder::SecondaryALUOp},
@@ -78,10 +78,7 @@ friend class FEXCore::IR::PassManager;
public:
enum class FlagsGenerationType : uint8_t {
TYPE_NONE,
TYPE_ADC,
TYPE_SBB,
TYPE_SUB,
TYPE_ADD,
TYPE_MUL,
TYPE_UMUL,
TYPE_LOGICAL,
@@ -271,8 +268,9 @@ public:
void SecondaryALUOp(OpcodeArgs);
template<uint32_t SrcIndex>
void ADCOp(OpcodeArgs);
template<uint32_t SrcIndex, bool SetFlags>
template<uint32_t SrcIndex>
void SBBOp(OpcodeArgs);
void SALCOp(OpcodeArgs);
void PUSHOp(OpcodeArgs);
void PUSHREGOp(OpcodeArgs);
void PUSHAOp(OpcodeArgs);
@@ -352,6 +350,11 @@ public:
void PUSHFOp(OpcodeArgs);
void POPFOp(OpcodeArgs);
struct CycleCounterPair {
OrderedNode *CounterLow;
OrderedNode *CounterHigh;
};
CycleCounterPair CycleCounter();
void RDTSCOp(OpcodeArgs);
void INCOp(OpcodeArgs);
void DECOp(OpcodeArgs);
@@ -997,7 +1000,7 @@ private:
// Used during new op bringup
bool ShouldDump{false};
void ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp);
void ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, unsigned SrcIdx);
// Opcode helpers for generalizing behavior across VEX and non-VEX variants.
@@ -1263,6 +1266,30 @@ private:
return NZCVMask;
}
// Set flag tracking to prepare for an operation that directly writes NZCV. If
// some bits are known to be zeroed, the PossiblySetNZCVBits mask can be
// passed. Otherwise, it defaults to assuming all bits may be set after
// (this is conservative).
void HandleNZCVWrite(uint32_t _PossiblySetNZCVBits = ~0) {
InvalidateDeferredFlags();
CachedNZCV = nullptr;
PossiblySetNZCVBits = _PossiblySetNZCVBits;
NZCVDirty = false;
}
// Set flag tracking to prepare for a read-modify-write operation on NZCV.
void HandleNZCV_RMW(uint32_t _PossiblySetNZCVBits = ~0) {
if (NZCVDirty && CachedNZCV)
_StoreNZCV(CachedNZCV);
HandleNZCVWrite(_PossiblySetNZCVBits);
}
// Special case of the above where we are known to zero C/V
void HandleNZ00Write() {
HandleNZCVWrite((1u << 31) | (1u << 30));
}
OrderedNode *GetNZCV() {
if (!CachedNZCV) {
CachedNZCV = _LoadNZCV();
@@ -1618,13 +1645,6 @@ private:
OrderedNode *Src2;
} TwoSource;
// ADC, SBB
struct {
OrderedNode *Src1;
OrderedNode *Src2;
OrderedNode *Src3;
} ThreeSource;
// LSHLI, LSHRI, ASHRI, RORI, ROLI
struct {
OrderedNode *Src1;
@@ -1715,13 +1735,13 @@ private:
OrderedNode *LoadAF();
void FixupAF();
void CalculatePF(OrderedNode *Res);
void CalculateAF(OpSize OpSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2);
void CalculateAF(OrderedNode *Src1, OrderedNode *Src2);
void CalculateOF(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, bool Sub);
void CalculateFlags_ADC(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, OrderedNode *CF);
void CalculateFlags_SBB(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, OrderedNode *CF);
void CalculateFlags_SUB(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF = true);
void CalculateFlags_ADD(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF = true);
OrderedNode *CalculateFlags_ADC(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2);
OrderedNode *CalculateFlags_SBB(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2);
OrderedNode *CalculateFlags_SUB(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF = true);
OrderedNode *CalculateFlags_ADD(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF = true);
void CalculateFlags_MUL(uint8_t SrcSize, OrderedNode *Res, OrderedNode *High);
void CalculateFlags_UMUL(OrderedNode *High);
void CalculateFlags_Logical(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2);
@@ -1752,37 +1772,7 @@ private:
*
* Depending on the operation it may force a RFLAGs calculation before storing the new deferred state.
* @{ */
void GenerateFlags_ADC(FEXCore::X86Tables::DecodedOp Op, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, OrderedNode *CF) {
CurrentDeferredFlags = DeferredFlagData {
.Type = FlagsGenerationType::TYPE_ADC,
.SrcSize = GetSrcSize(Op),
.Res = Res,
.Sources = {
.ThreeSource = {
.Src1 = Src1,
.Src2 = Src2,
.Src3 = CF,
},
},
};
}
void GenerateFlags_SBB(FEXCore::X86Tables::DecodedOp Op, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, OrderedNode *CF) {
CurrentDeferredFlags = DeferredFlagData {
.Type = FlagsGenerationType::TYPE_SBB,
.SrcSize = GetSrcSize(Op),
.Res = Res,
.Sources = {
.ThreeSource = {
.Src1 = Src1,
.Src2 = Src2,
.Src3 = CF,
},
},
};
}
void GenerateFlags_SUB(FEXCore::X86Tables::DecodedOp Op, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF = true) {
void GenerateFlags_SUB(FEXCore::X86Tables::DecodedOp Op, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF = true) {
if (!UpdateCF) {
// If we aren't updating CF then we need to calculate flags. Invalidation mask would make this not required.
CalculateDeferredFlags();
@@ -1790,26 +1780,6 @@ private:
CurrentDeferredFlags = DeferredFlagData {
.Type = FlagsGenerationType::TYPE_SUB,
.SrcSize = GetSrcSize(Op),
.Res = Res,
.Sources = {
.TwoSrcImmediate = {
.Src1 = Src1,
.Src2 = Src2,
.UpdateCF = UpdateCF,
},
},
};
}
void GenerateFlags_ADD(FEXCore::X86Tables::DecodedOp Op, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF = true) {
if (!UpdateCF) {
// If we aren't updating CF then we need to calculate flags. Invalidation mask would make this not required.
CalculateDeferredFlags();
}
CurrentDeferredFlags = DeferredFlagData {
.Type = FlagsGenerationType::TYPE_ADD,
.SrcSize = GetSrcSize(Op),
.Res = Res,
.Sources = {
.TwoSrcImmediate = {
.Src1 = Src1,
@@ -282,19 +282,25 @@ void OpDispatchBuilder::CalculatePF(OrderedNode *Res) {
SetRFLAG<FEXCore::X86State::RFLAG_PF_RAW_LOC>(Res);
}
void OpDispatchBuilder::CalculateAF(OpSize OpSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2) {
void OpDispatchBuilder::CalculateAF(OrderedNode *Src1, OrderedNode *Src2) {
// We only care about bit 4 in the subsequent XOR. If we'll XOR with 0,
// there's no sense XOR'ing at all. This affects INC.
// there's no sense XOR'ing at all. If we'll XOR with 1, that's just
// inverting.
uint64_t Const;
if (IsValueConstant(WrapNode(Src2), &Const) && (Const & (1u << 4)) == 0) {
SetRFLAG<FEXCore::X86State::RFLAG_AF_RAW_LOC>(Src1);
if (IsValueConstant(WrapNode(Src2), &Const)) {
if (Const & (1u << 4)) {
SetRFLAG<FEXCore::X86State::RFLAG_AF_RAW_LOC>(_Not(OpSize::i32Bit, Src1));
} else {
SetRFLAG<FEXCore::X86State::RFLAG_AF_RAW_LOC>(Src1);
}
return;
}
// We store the XOR of the arguments. At read time, we XOR with the
// appropriate bit of the result (available as the PF flag) and extract the
// appropriate bit.
OrderedNode *XorRes = _Xor(OpSize, Src1, Src2);
OrderedNode *XorRes = _Xor(OpSize::i32Bit, Src1, Src2);
SetRFLAG<FEXCore::X86State::RFLAG_AF_RAW_LOC>(XorRes);
}
@@ -310,34 +316,9 @@ void OpDispatchBuilder::CalculateDeferredFlags(uint32_t FlagsToCalculateMask) {
}
switch (CurrentDeferredFlags.Type) {
case FlagsGenerationType::TYPE_ADC:
CalculateFlags_ADC(
CurrentDeferredFlags.SrcSize,
CurrentDeferredFlags.Res,
CurrentDeferredFlags.Sources.ThreeSource.Src1,
CurrentDeferredFlags.Sources.ThreeSource.Src2,
CurrentDeferredFlags.Sources.ThreeSource.Src3);
break;
case FlagsGenerationType::TYPE_SBB:
CalculateFlags_SBB(
CurrentDeferredFlags.SrcSize,
CurrentDeferredFlags.Res,
CurrentDeferredFlags.Sources.ThreeSource.Src1,
CurrentDeferredFlags.Sources.ThreeSource.Src2,
CurrentDeferredFlags.Sources.ThreeSource.Src3);
break;
case FlagsGenerationType::TYPE_SUB:
CalculateFlags_SUB(
CurrentDeferredFlags.SrcSize,
CurrentDeferredFlags.Res,
CurrentDeferredFlags.Sources.TwoSrcImmediate.Src1,
CurrentDeferredFlags.Sources.TwoSrcImmediate.Src2,
CurrentDeferredFlags.Sources.TwoSrcImmediate.UpdateCF);
break;
case FlagsGenerationType::TYPE_ADD:
CalculateFlags_ADD(
CurrentDeferredFlags.SrcSize,
CurrentDeferredFlags.Res,
CurrentDeferredFlags.Sources.TwoSrcImmediate.Src1,
CurrentDeferredFlags.Sources.TwoSrcImmediate.Src2,
CurrentDeferredFlags.Sources.TwoSrcImmediate.UpdateCF);
@@ -486,150 +467,126 @@ void OpDispatchBuilder::CalculateDeferredFlags(uint32_t FlagsToCalculateMask) {
NZCVDirty = false;
}
void OpDispatchBuilder::CalculateFlags_ADC(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, OrderedNode *CF) {
OrderedNode *OpDispatchBuilder::CalculateFlags_ADC(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2) {
auto Zero = _Constant(0);
auto One = _Constant(1);
auto OpSize = SrcSize == 8 ? OpSize::i64Bit : OpSize::i32Bit;
OrderedNode *Res;
CalculateAF(OpSize, Res, Src1, Src2);
CalculatePF(Res);
CalculateAF(Src1, Src2);
if (SrcSize >= 4) {
if (NZCVDirty && CachedNZCV)
_StoreNZCV(CachedNZCV);
CachedNZCV = nullptr;
_AdcNZCV(OpSize, Src1, Src2);
PossiblySetNZCVBits = ~0;
HandleNZCV_RMW();
Res = _AdcWithFlags(OpSize, Src1, Src2);
} else {
// SF/ZF
auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
Res = _Adc(OpSize, Src1, Src2);
Res = _Bfe(OpSize, SrcSize * 8, 0, Res);
auto SelectOpLT = _Select(FEXCore::IR::COND_ULT, Res, Src2, One, Zero);
auto SelectOpLE = _Select(FEXCore::IR::COND_ULE, Res, Src2, One, Zero);
auto SelectCF = _Select(FEXCore::IR::COND_EQ, CF, One, SelectOpLE, SelectOpLT);
SetNZ_ZeroCV(SrcSize, Res);
// CF
// Unsigned
{
auto SelectOpLT = _Select(FEXCore::IR::COND_ULT, Res, Src2, One, Zero);
auto SelectOpLE = _Select(FEXCore::IR::COND_ULE, Res, Src2, One, Zero);
auto SelectCF = _Select(FEXCore::IR::COND_EQ, CF, One, SelectOpLE, SelectOpLT);
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(SelectCF);
}
// Signed
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(SelectCF);
CalculateOF(SrcSize, Res, Src1, Src2, false);
}
CalculatePF(Res);
return Res;
}
void OpDispatchBuilder::CalculateFlags_SBB(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, OrderedNode *CF) {
OrderedNode *OpDispatchBuilder::CalculateFlags_SBB(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2) {
auto Zero = _Constant(0);
auto One = _Constant(1);
auto OpSize = SrcSize == 8 ? OpSize::i64Bit : OpSize::i32Bit;
CalculateAF(OpSize, Res, Src1, Src2);
CalculatePF(Res);
CalculateAF(Src1, Src2);
OrderedNode *Res;
if (SrcSize >= 4) {
// Rectify input carry
CarryInvert();
if (NZCVDirty && CachedNZCV)
_StoreNZCV(CachedNZCV);
CachedNZCV = nullptr;
NZCVDirty = false;
_SbbNZCV(OpSize, Src1, Src2);
PossiblySetNZCVBits = ~0;
HandleNZCV_RMW();
Res = _SbbWithFlags(OpSize, Src1, Src2);
// Rectify output carry
CarryInvert();
} else {
// SF/ZF
auto CF = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
Res = _Sub(OpSize, Src1, _Add(OpSize, Src2, CF));
Res = _Bfe(OpSize, SrcSize * 8, 0, Res);
auto SelectOpLT = _Select(FEXCore::IR::COND_UGT, Res, Src1, One, Zero);
auto SelectOpLE = _Select(FEXCore::IR::COND_UGE, Res, Src1, One, Zero);
auto SelectCF = _Select(FEXCore::IR::COND_EQ, CF, One, SelectOpLE, SelectOpLT);
SetNZ_ZeroCV(SrcSize, Res);
// CF
// Unsigned
{
auto SelectOpLT = _Select(FEXCore::IR::COND_UGT, Res, Src1, One, Zero);
auto SelectOpLE = _Select(FEXCore::IR::COND_UGE, Res, Src1, One, Zero);
auto SelectCF = _Select(FEXCore::IR::COND_EQ, CF, One, SelectOpLE, SelectOpLT);
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(SelectCF);
}
// Signed
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(SelectCF);
CalculateOF(SrcSize, Res, Src1, Src2, true);
}
CalculatePF(Res);
return Res;
}
void OpDispatchBuilder::CalculateFlags_SUB(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF) {
auto OpSize = SrcSize == 8 ? OpSize::i64Bit : OpSize::i32Bit;
CalculateAF(OpSize, Res, Src1, Src2);
CalculatePF(Res);
OrderedNode *OpDispatchBuilder::CalculateFlags_SUB(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF) {
// Stash CF before stomping over it
auto OldCF = UpdateCF ? nullptr : GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
// TODO: Could do this path for small sources if we have FEAT_FlagM
HandleNZCVWrite();
CalculateAF(Src1, Src2);
OrderedNode *Res;
if (SrcSize >= 4) {
_SubNZCV(OpSize, Src1, Src2);
CachedNZCV = nullptr;
NZCVDirty = false;
PossiblySetNZCVBits = ~0;
// We only bother inverting CF if we're actually going to update CF.
if (UpdateCF)
CarryInvert();
Res = _SubWithFlags(IR::SizeToOpSize(SrcSize), Src1, Src2);
} else {
// SF/ZF
SetNZ_ZeroCV(SrcSize, Res);
// CF
if (UpdateCF) {
// Grab carry bit from unmasked output.
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(Res, SrcSize * 8, true);
}
CalculateOF(SrcSize, Res, Src1, Src2, true);
_SubNZCV(IR::SizeToOpSize(SrcSize), Src1, Src2);
Res = _Sub(OpSize::i32Bit, Src1, Src2);
}
CalculatePF(Res);
// If we're updating CF, we need to invert it for correctness. If we're not
// updating CF, we need to restore the CF since we stomped over it.
if (UpdateCF)
CarryInvert();
else
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(OldCF);
return Res;
}
OrderedNode *OpDispatchBuilder::CalculateFlags_ADD(uint8_t SrcSize, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF) {
// Stash CF before stomping over it
auto OldCF = UpdateCF ? nullptr : GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
HandleNZCVWrite();
CalculateAF(Src1, Src2);
OrderedNode *Res;
if (SrcSize >= 4) {
Res = _AddWithFlags(IR::SizeToOpSize(SrcSize), Src1, Src2);
} else {
_AddNZCV(IR::SizeToOpSize(SrcSize), Src1, Src2);
Res = _Add(OpSize::i32Bit, Src1, Src2);
}
CalculatePF(Res);
// We stomped over CF while calculation flags, restore it.
if (!UpdateCF)
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(OldCF);
}
void OpDispatchBuilder::CalculateFlags_ADD(uint8_t SrcSize, OrderedNode *Res, OrderedNode *Src1, OrderedNode *Src2, bool UpdateCF) {
auto OpSize = SrcSize == 8 ? OpSize::i64Bit : OpSize::i32Bit;
CalculateAF(OpSize, Res, Src1, Src2);
CalculatePF(Res);
// Stash CF before stomping over it
auto OldCF = UpdateCF ? nullptr : GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
// TODO: Could do this path for small sources if we have FEAT_FlagM
if (SrcSize >= 4) {
_AddNZCV(OpSize, Src1, Src2);
CachedNZCV = nullptr;
NZCVDirty = false;
PossiblySetNZCVBits = ~0;
} else {
// SF/ZF
SetNZ_ZeroCV(SrcSize, Res);
// CF
if (UpdateCF) {
// Grab carry bit from unmasked output
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(Res, SrcSize * 8, true);
}
CalculateOF(SrcSize, Res, Src1, Src2, false);
}
// We stomped over CF while calculation flags, restore it.
if (!UpdateCF)
SetRFLAG<FEXCore::X86State::RFLAG_CF_RAW_LOC>(OldCF);
return Res;
}
void OpDispatchBuilder::CalculateFlags_MUL(uint8_t SrcSize, OrderedNode *Res, OrderedNode *High) {
HandleNZCVWrite();
// PF/AF/ZF/SF
// Undefined
{
@@ -648,13 +605,12 @@ void OpDispatchBuilder::CalculateFlags_MUL(uint8_t SrcSize, OrderedNode *Res, Or
// undefined, this does what we need.
auto Zero = _Constant(0);
_CondAddNZCV(OpSize::i64Bit, Zero, Zero, CondClassType{COND_EQ}, 0x3 /* nzCV */);
CachedNZCV = nullptr;
NZCVDirty = false;
PossiblySetNZCVBits = ~0;
}
}
void OpDispatchBuilder::CalculateFlags_UMUL(OrderedNode *High) {
HandleNZCVWrite();
auto Zero = _Constant(0);
OpSize Size = IR::SizeToOpSize(GetOpSize(High));
@@ -674,9 +630,6 @@ void OpDispatchBuilder::CalculateFlags_UMUL(OrderedNode *High) {
// If High = 0, then sets to nZcv. Else sets to nzCV. Since SF/ZF undefined,
// this does what we need.
_CondAddNZCV(Size, Zero, Zero, CondClassType{COND_EQ}, 0x3 /* nzCV */);
CachedNZCV = nullptr;
NZCVDirty = false;
PossiblySetNZCVBits = ~0;
}
}
@@ -3422,15 +3422,12 @@ void OpDispatchBuilder::VPALIGNROp(OpcodeArgs) {
template<size_t ElementSize>
void OpDispatchBuilder::UCOMISxOp(OpcodeArgs) {
InvalidateDeferredFlags();
const auto SrcSize = Op->Src[0].IsGPR() ? GetGuestVectorLength() : GetSrcSize(Op);
OrderedNode *Src1 = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, GetGuestVectorLength(), Op->Flags);
OrderedNode *Src2 = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], SrcSize, Op->Flags);
CachedNZCV = nullptr;
HandleNZCVWrite();
_FCmp(ElementSize, Src1, Src2);
PossiblySetNZCVBits = ~0;
ConvertNZCVToSSE();
// Zero AF. Note that the comparison sets the raw PF to 0/1 above, so PF[4] is
+24 -16
View File
@@ -181,23 +181,11 @@ namespace FEXCore {
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0;
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1;
} else {
// The argument pointer comes from the host stack, so it's not
// accessible by the guest. Allocate a thread-local chunk of memory
// to relocate the argument data.
// TODO: Directly store the arguments in a guest-accessible location instead
// TODO: FEXCore::Allocator::malloc() still returns pointers inaccessible from 32-bit guests here
thread_local void* local_args =
mmap( 0, 128, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// We don't know how much argument data is on the stack, so we
// unconditionally copy a fixed amount and leave everything else
// uninitialized.
// TODO: This breaks functions with large argument counts.
memcpy(local_args, arg1, 128);
if ((reinterpret_cast<uintptr_t>(arg1) >> 32) != 0) {
ERROR_AND_DIE_FMT("Tried to call guest function with arguments packed to a 64-bit address");
}
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RCX] = (uintptr_t)arg0;
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDX] = (uintptr_t)local_args;
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDX] = (uintptr_t)arg1;
}
Thread->CTX->HandleCallback(Thread, (uintptr_t)callback);
@@ -499,6 +487,26 @@ namespace FEXCore {
}
}
FEX_DEFAULT_VISIBILITY void* GetGuestStack() {
if (!Thread) {
ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously");
}
return (void*)(uintptr_t)((Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP]));
}
FEX_DEFAULT_VISIBILITY void MoveGuestStack(uintptr_t NewAddress) {
if (!Thread) {
ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously");
}
if (NewAddress >> 32) {
ERROR_AND_DIE_FMT("Tried to set stack pointer for 32-bit guest to a 64-bit address");
}
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = NewAddress;
}
#else
fextl::unique_ptr<ThunkHandler> ThunkHandler::Create() {
ERROR_AND_DIE_FMT("Unsupported");
+57 -5
View File
@@ -951,6 +951,24 @@
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = Adc OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": [ "Integer Add with carry",
"Will truncate to 64 or 32bits"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = Sbb OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": [ "Integer Subtract with carry/borrow",
"Will truncate to 64 or 32bits"
],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = AddShift OpSize:#Size, GPR:$Src1, GPR:$Src2, ShiftType:$Shift{ShiftType::LSL}, u8:$ShiftAmount{0}": {
"Desc": [ "Integer Add with shifted register",
"Will truncate to 64 or 32bits"
@@ -961,12 +979,25 @@
"_Shift != ShiftType::ROR"
]
},
"GPR = AddWithFlags OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": [ "Integer add. Truncates and sets NZCV per AddNZCV"],
"DestSize": "Size",
"HasSideEffects": true,
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"AddNZCV OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Set NZCV for the sum of two GPRs"],
"HasSideEffects": true,
"DestSize": "Size"
},
"SetSmallNZV OpSize:#Size, GPR:$Src": {
"Desc": ["Set NZV with a SETF instruction. Preserves CF."],
"HasSideEffects": true,
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit"
]
},
"CarryInvert": {
@@ -989,6 +1020,22 @@
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = AdcWithFlags OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Adds and set NZCV for the sum of two GPRs and carry-in given as NZCV"],
"HasSideEffects": true,
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = SbbWithFlags OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Subtracts and set NZCV for the difference of two GPRs and carry-in given as NZCV"],
"HasSideEffects": true,
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"AdcNZCV OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Set NZCV for the sum of two GPRs and carry-in given as NZCV"],
"HasSideEffects": true,
@@ -1024,16 +1071,21 @@
"_Shift != ShiftType::ROR"
]
},
"SubNZCV OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Set NZCV for the difference of two GPRs. ",
"Carry flag uses arm64 definition, inverted x86.",
""],
"GPR = SubWithFlags OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": [ "Integer Sub. Truncates and sets NZCV per SubNZCV"],
"DestSize": "Size",
"HasSideEffects": true,
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"SubNZCV OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Set NZCV for the difference of two GPRs. ",
"Carry flag uses arm64 definition, inverted x86.",
""],
"DestSize": "Size",
"HasSideEffects": true
},
"GPR = Or OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": ["Integer binary or"
],
@@ -965,20 +965,24 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
case OP_SUB:
case OP_ADDNZCV:
case OP_SUBNZCV:
case OP_ADDWITHFLAGS:
case OP_SUBWITHFLAGS:
{
auto Op = IROp->C<IR::IROp_Add>();
uint64_t Constant2{};
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
if (IsImmAddSub(Constant2)) {
// We don't allow 8/16-bit operations to have constants, since no
// constant would be in bounds after the JIT's 24/16 shift.
if (IsImmAddSub(Constant2) && Op->Header.Size >= 4) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
} else if (IROp->Op == OP_SUBNZCV) {
// If the first source is zero, we can use a NEGS instruction.
} else if (IROp->Op == OP_SUBNZCV || IROp->Op == OP_SUBWITHFLAGS || IROp->Op == OP_SUB) {
// TODO: Generalize this
uint64_t Constant1{};
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1)) {
if (Constant1 == 0) {
@@ -991,6 +995,22 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
break;
}
case OP_ADC:
case OP_ADCWITHFLAGS:
{
auto Op = IROp->C<IR::IROp_Adc>();
uint64_t Constant1{};
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1)) {
if (Constant1 == 0) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[0]));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, 0));
Changed = true;
}
}
break;
}
case OP_CONDADDNZCV:
{
auto Op = IROp->C<IR::IROp_CondAddNZCV>();
@@ -128,6 +128,36 @@ DeadFlagCalculationEliminination::Classify(IROp_Header *IROp)
.Replacement = OP_AND,
};
case OP_ADDWITHFLAGS:
return {
.Write = FLAG_NZCV,
.CanReplace = true,
.Replacement = OP_ADD,
};
case OP_SUBWITHFLAGS:
return {
.Write = FLAG_NZCV,
.CanReplace = true,
.Replacement = OP_SUB,
};
case OP_ADCWITHFLAGS:
return {
.Read = FLAG_C,
.Write = FLAG_NZCV,
.CanReplace = true,
.Replacement = OP_ADC,
};
case OP_SBBWITHFLAGS:
return {
.Read = FLAG_C,
.Write = FLAG_NZCV,
.CanReplace = true,
.Replacement = OP_SBB,
};
case OP_ADDNZCV:
case OP_SUBNZCV:
case OP_TESTNZ:
@@ -153,9 +183,19 @@ DeadFlagCalculationEliminination::Classify(IROp_Header *IROp)
.CanEliminate = true,
};
case OP_SETSMALLNZV:
return {
.Write = FLAG_N | FLAG_Z | FLAG_V,
.CanEliminate = true,
};
case OP_LOADNZCV:
return {.Read = FLAG_NZCV};
case OP_ADC:
case OP_SBB:
return {.Read = FLAG_C};
case OP_ADCNZCV:
case OP_SBBNZCV:
return {
@@ -169,6 +209,11 @@ DeadFlagCalculationEliminination::Classify(IROp_Header *IROp)
return {.Read = FlagsForCondClassType(Op->Cond)};
}
case OP_NEG: {
auto Op = IROp->CW<IR::IROp_Neg>();
return {.Read = FlagsForCondClassType(Op->Cond)};
}
case OP_CONDJUMP: {
auto Op = IROp->CW<IR::IROp_CondJump>();
if (!Op->FromNZCV)
@@ -286,42 +331,53 @@ bool DeadFlagCalculationEliminination::Run(IREmitter *IREmit) {
while (1) {
auto [CodeNode, IROp] = CodeLast();
// Optimiation algorithm: For each flag written...
// Optimizing flags can cause earlier flag reads to become dead but dead
// flag reads should not impede optimiation of earlier dead flag writes.
// We must DCE as we go to ensure we converge in a single iteration.
//
// If the flag has a later read (per FlagsRead), remove the flag from
// FlagsRead, since the reader is covered by this write.
//
// Else, there is no later read, so remove the flag write (if we can).
// This is the active part of the optimization.
//
// Then, add each flag read to FlagsRead.
//
// This order is important: instructions that read-modify-write flags
// (like adcs) first read flags, then write flags. Since we're iterating
// the block backwards, that means we handle the write first.
struct FlagInfo Info = Classify(IROp);
// TODO: This whole pass could be merged with DCE?
bool HasSideEffects = IR::HasSideEffects(IROp->Op);
if (!HasSideEffects && CodeNode->GetUses() == 0) {
Changed = true;
IREmit->Remove(CodeNode);
} else {
// Optimiation algorithm: For each flag written...
//
// If the flag has a later read (per FlagsRead), remove the flag from
// FlagsRead, since the reader is covered by this write.
//
// Else, there is no later read, so remove the flag write (if we can).
// This is the active part of the optimization.
//
// Then, add each flag read to FlagsRead.
//
// This order is important: instructions that read-modify-write flags
// (like adcs) first read flags, then write flags. Since we're iterating
// the block backwards, that means we handle the write first.
struct FlagInfo Info = Classify(IROp);
if (!Info.Trivial) {
bool Eliminated = false;
if (!Info.Trivial) {
bool Eliminated = false;
if ((FlagsRead & Info.Write) == 0) {
if (Info.CanEliminate) {
IREmit->Remove(CodeNode);
Eliminated = true;
Changed = true;
} else if (Info.CanReplace) {
IROp->Op = Info.Replacement;
Changed = true;
if ((FlagsRead & Info.Write) == 0) {
if (Info.CanEliminate) {
IREmit->Remove(CodeNode);
Eliminated = true;
Changed = true;
} else if (Info.CanReplace) {
IROp->Op = Info.Replacement;
Changed = true;
}
} else {
FlagsRead &= ~Info.Write;
}
} else {
FlagsRead &= ~Info.Write;
}
// If we eliminated the instruction, we eliminate its read too. This
// check is required to ensure the pass converges locally in a single
// iteration.
if (!Eliminated)
FlagsRead |= Info.Read;
// If we eliminated the instruction, we eliminate its read too. This
// check is required to ensure the pass converges locally in a single
// iteration.
if (!Eliminated)
FlagsRead |= Info.Read;
}
}
// Iterate in reverse
+6
View File
@@ -297,6 +297,12 @@ namespace FEXCore::Allocator {
if (c == ' ') {
STEAL_LOG("[%d] ParseEnd; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
if (RegionEnd > End) {
// Early return if we are completely beyond the allocation space.
close(MapsFD);
return Regions;
}
State = ScanEnd;
// If the previous map's ending and the region we just parsed overlap the stack then we need to save the stack mapping.
@@ -39,14 +39,6 @@ namespace Core {
public:
virtual ~SignalDelegator() = default;
/**
* @brief Registers an emulated thread's object to a TLS object
*
* Required to know which thread has received the signal when it occurs
*/
virtual void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) = 0;
virtual void UninstallTLSState(FEXCore::Core::InternalThreadState *Thread) = 0;
struct SignalDelegatorConfig {
bool SupportsAVX{};
@@ -55,6 +47,7 @@ namespace Core {
uint64_t DispatcherEnd;
// Dispatcher entrypoint.
uint64_t AbsoluteLoopTopAddress{};
uint64_t AbsoluteLoopTopAddressFillSRA{};
// Signal return pointers.
@@ -139,6 +139,9 @@ namespace FEXCore::Core {
// Async signals aren't guaranteed to be delivered in any particular order, but FEX treats them as FILO.
fextl::vector<DeferredSignalState> DeferredSignalFrames;
///< Data pointer for exclusive use by the frontend
void* FrontendPtr;
// BaseFrameState should always be at the end, directly before the interrupt fault page
alignas(16) FEXCore::Core::CpuStateFrame BaseFrameState{};
+2
View File
@@ -74,6 +74,8 @@ namespace FEX::FormatCheck {
return false;
}
close(fd);
return Header.Magic == COOKIE_MAGIC_V1;
}
}
+2 -2
View File
@@ -551,8 +551,8 @@ int main(int argc, char **argv, char **const envp) {
HostFeatureControl |= static_cast<uint64_t>(FEXCore::Config::HostFeatures::DISABLECRYPTO);
}
// Always disable preserve_all abi.
HostFeatureControl |= static_cast<uint64_t>(FEXCore::Config::HostFeatures::DISABLEPRESERVEALLABI);
// Always enable preserve_all abi.
HostFeatureControl |= static_cast<uint64_t>(FEXCore::Config::HostFeatures::ENABLEPRESERVEALLABI);
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_HOSTFEATURES, fextl::fmt::format("{}", HostFeatureControl));
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_FORCESVEWIDTH, fextl::fmt::format("{}", SVEWidth));
+2 -2
View File
@@ -32,8 +32,8 @@ class DummySignalDelegator final : public FEXCore::SignalDelegator, public FEXCo
}
protected:
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) override;
void UninstallTLSState(FEXCore::Core::InternalThreadState *Thread) override;
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread);
void UninstallTLSState(FEXCore::Core::InternalThreadState *Thread);
private:
FEXCore::Core::InternalThreadState *GetTLSThread();
+10
View File
@@ -417,11 +417,17 @@ int main(int argc, char **argv, char **const envp) {
fextl::unique_ptr<FEX::HLE::MemAllocator> Allocator;
fextl::vector<FEXCore::Allocator::MemoryRegion> Base48Bit;
fextl::vector<FEXCore::Allocator::MemoryRegion> Low4GB;
if (Loader.Is64BitMode()) {
// Destroy the 48th bit if it exists
Base48Bit = FEXCore::Allocator::Steal48BitVA();
} else {
// Reserve [0x1_0000_0000, 0x2_0000_0000).
// Safety net if 32-bit address calculation overflows in to 64-bit range.
constexpr uint64_t First64BitAddr = 0x1'0000'0000ULL;
Low4GB = FEXCore::Allocator::StealMemoryRegion(First64BitAddr, First64BitAddr + First64BitAddr);
// Setup our userspace allocator
FEXCore::Allocator::SetupHooks();
Allocator = FEX::HLE::CreatePassthroughAllocator();
@@ -484,6 +490,7 @@ int main(int argc, char **argv, char **const envp) {
auto ParentThread = SyscallHandler->TM.CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer());
SyscallHandler->TM.TrackThread(ParentThread);
SignalDelegation->RegisterTLSState(ParentThread);
// Pass in our VDSO thunks
CTX->AppendThunkDefinitions(FEX::VDSO::GetVDSOThunkDefinitions());
@@ -561,6 +568,7 @@ int main(int argc, char **argv, char **const envp) {
auto ProgramStatus = ParentThread->StatusCode;
SignalDelegation->UninstallTLSState(ParentThread);
CTX->DestroyThread(ParentThread);
DebugServer.reset();
@@ -578,6 +586,8 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Allocator::ClearHooks();
FEXCore::Allocator::ReclaimMemoryRegion(Base48Bit);
FEXCore::Allocator::ReclaimMemoryRegion(Low4GB);
// Allocator is now original system allocator
FEXCore::Telemetry::Shutdown(Program.ProgramName);
FEXCore::Profiler::Shutdown();
+1 -1
View File
@@ -3,7 +3,7 @@ set(SRCS Main.cpp
XXFileHash.cpp)
add_executable(${NAME} ${SRCS})
list(APPEND LIBS FEXCore Common)
list(APPEND LIBS FEXCore Common xxHash::xxhash)
target_include_directories(${NAME} PRIVATE ${CMAKE_SOURCE_DIR}/Source/)
+1 -14
View File
@@ -34,36 +34,23 @@ set (SRCS
LinuxSyscalls/x32/IoctlEmulation.cpp
LinuxSyscalls/x64/EPoll.cpp
LinuxSyscalls/x64/FD.cpp
LinuxSyscalls/x64/IO.cpp
LinuxSyscalls/x64/Ioctl.cpp
LinuxSyscalls/x64/Info.cpp
LinuxSyscalls/x64/Memory.cpp
LinuxSyscalls/x64/Msg.cpp
LinuxSyscalls/x64/NotImplemented.cpp
LinuxSyscalls/x64/Semaphore.cpp
LinuxSyscalls/x64/Sched.cpp
LinuxSyscalls/x64/Signals.cpp
LinuxSyscalls/x64/Socket.cpp
LinuxSyscalls/x64/Thread.cpp
LinuxSyscalls/x64/Syscalls.cpp
LinuxSyscalls/x64/Time.cpp
LinuxSyscalls/Syscalls/EPoll.cpp
LinuxSyscalls/Syscalls/FD.cpp
LinuxSyscalls/Syscalls/FS.cpp
LinuxSyscalls/Syscalls/Passthrough.cpp
LinuxSyscalls/Syscalls/Info.cpp
LinuxSyscalls/Syscalls/IO.cpp
LinuxSyscalls/Syscalls/IOUring.cpp
LinuxSyscalls/Syscalls/Key.cpp
LinuxSyscalls/Syscalls/Memory.cpp
LinuxSyscalls/Syscalls/Msg.cpp
LinuxSyscalls/Syscalls/Namespace.cpp
LinuxSyscalls/Syscalls/Sched.cpp
LinuxSyscalls/Syscalls/Semaphore.cpp
LinuxSyscalls/Syscalls/SHM.cpp
LinuxSyscalls/Syscalls/Signals.cpp
LinuxSyscalls/Syscalls/Socket.cpp
LinuxSyscalls/Syscalls/Thread.cpp
LinuxSyscalls/Syscalls/Time.cpp
LinuxSyscalls/Syscalls/Timer.cpp
LinuxSyscalls/Syscalls/NotImplemented.cpp
LinuxSyscalls/Syscalls/Stubs.cpp)
@@ -54,8 +54,8 @@ namespace FEX::HLE {
// Called from the signal trampoline function.
void HandleSignal(int Signal, void *Info, void *UContext);
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) override;
void UninstallTLSState(FEXCore::Core::InternalThreadState *Thread) override;
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread);
void UninstallTLSState(FEXCore::Core::InternalThreadState *Thread);
/**
* @brief Registers a signal handler for the host to handle a signal
@@ -393,22 +393,27 @@ struct StackFramePlusRet {
uint64_t Pad;
};
[[noreturn]]
static void CloneBody(StackFrameData *Data, bool NeedsDataFree) {
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
auto Stack = Data->GuestArgs.NewStack;
if (NeedsDataFree) {
FEXCore::Allocator::free(Data);
}
FEX::LinuxEmulation::Threads::DeallocateStackObjectAndExit(Stack, Result);
FEX_UNREACHABLE;
}
[[noreturn]]
static void Clone3HandlerRet() {
StackFrameData *Data = (StackFrameData*)alloca(0);
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
FEX::LinuxEmulation::Threads::DeallocateStackObject(Data->GuestArgs.NewStack);
// To behave like a real clone, we now just need to call exit here
exit(Result);
FEX_UNREACHABLE;
CloneBody(Data, false);
}
static int Clone2HandlerRet(void *arg) {
StackFrameData *Data = (StackFrameData*)arg;
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
FEX::LinuxEmulation::Threads::DeallocateStackObject(Data->GuestArgs.NewStack);
FEXCore::Allocator::free(arg);
return Result;
CloneBody(Data, true);
}
// Clone3 flags
@@ -975,7 +980,7 @@ fextl::unique_ptr<FEXCore::HLE::SourcecodeMap> SyscallHandler::GenerateMap(const
fextl::istringstream Stream(SourceData);
constexpr int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO;
int IndexStream = ::open(GuestSourceFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, USER_PERMS);
int IndexStream = ::open(GuestIndexFile.c_str(), O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, USER_PERMS);
if (IndexStream == -1) {
LogMan::Msg::DFmt("GenerateMap: Failed to open '{}' for writing", GuestIndexFile);
@@ -38,9 +38,11 @@ $end_info$
#define SYSCALL_ARCH_NAME Arm64
#endif
#include "LinuxSyscalls/x64/SyscallsEnum.h"
#define CONCAT_(a, b) a ## b
#define CONCAT(a, b) CONCAT_(a, b)
#define SYSCALL_DEF(name) ( SYSCALL_ARCH_NAME::CONCAT(CONCAT(SYSCALL_, SYSCALL_ARCH_NAME), _##name))
#define SYSCALL_DEF(name) ( HLE::SYSCALL_ARCH_NAME::CONCAT(CONCAT(SYSCALL_, SYSCALL_ARCH_NAME), _##name))
// #define DEBUG_STRACE
@@ -63,19 +65,10 @@ class SignalDelegator;
void RegisterFS(FEX::HLE::SyscallHandler *Handler);
void RegisterInfo(FEX::HLE::SyscallHandler *Handler);
void RegisterIO(FEX::HLE::SyscallHandler *Handler);
void RegisterIOUring(FEX::HLE::SyscallHandler *Handler);
void RegisterKey(FEX::HLE::SyscallHandler *Handler);
void RegisterMemory(FEX::HLE::SyscallHandler *Handler);
void RegisterMsg(FEX::HLE::SyscallHandler *Handler);
void RegisterNamespace(FEX::HLE::SyscallHandler *Handler);
void RegisterNuma(FEX::HLE::SyscallHandler *Handler);
void RegisterSched(FEX::HLE::SyscallHandler *Handler);
void RegisterSemaphore(FEX::HLE::SyscallHandler *Handler);
void RegisterSHM(FEX::HLE::SyscallHandler *Handler);
void RegisterSignals(FEX::HLE::SyscallHandler *Handler);
void RegisterSocket(FEX::HLE::SyscallHandler *Handler);
void RegisterThread(FEX::HLE::SyscallHandler *Handler);
void RegisterTime(FEX::HLE::SyscallHandler *Handler);
void RegisterTimer(FEX::HLE::SyscallHandler *Handler);
void RegisterNotImplemented(FEX::HLE::SyscallHandler *Handler);
void RegisterStubs(FEX::HLE::SyscallHandler *Handler);
@@ -634,9 +627,6 @@ bool IsFaultLocation(uint64_t PC);
#define REGISTER_SYSCALL_IMPL(name, lambda) \
REGISTER_SYSCALL_IMPL_INTERNAL(name, ~0, FEXCore::IR::SyscallFlags::DEFAULT, lambda)
#define REGISTER_SYSCALL_IMPL_PASS(name, lambda) \
REGISTER_SYSCALL_IMPL_INTERNAL(name, SYSCALL_DEF(name), FEXCore::IR::SyscallFlags::DEFAULT, lambda)
#define REGISTER_SYSCALL_IMPL_FLAGS(name, flags, lambda) \
REGISTER_SYSCALL_IMPL_INTERNAL(name, ~0, flags, lambda)
@@ -19,16 +19,10 @@ namespace FEX::HLE {
void RegisterEpoll(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(epoll_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(epoll_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int size) -> uint64_t {
uint64_t Result = epoll_create(size);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(epoll_create1, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
uint64_t Result = epoll_create1(flags);
SYSCALL_ERRNO();
});
}
}
@@ -32,18 +32,6 @@ namespace FEX::HLE {
void RegisterFD(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(read, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, size_t count) -> uint64_t {
uint64_t Result = ::read(fd, buf, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(write, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, size_t count) -> uint64_t {
uint64_t Result = ::write(fd, buf, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(open, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, int flags, uint32_t mode) -> uint64_t {
flags = FEX::HLE::RemapFromX86Flags(flags);
@@ -57,37 +45,25 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(chown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::chown(pathname, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::fchown(fd, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(lchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(lchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::lchown(pathname, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(lseek, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, uint64_t offset, int whence) -> uint64_t {
uint64_t Result = ::lseek(fd, offset, whence);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(access, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, int mode) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Access(pathname, mode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pipe, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(pipe, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pipefd[2]) -> uint64_t {
uint64_t Result = ::pipe(pipefd);
SYSCALL_ERRNO();
@@ -100,60 +76,12 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(flock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, int operation) -> uint64_t {
uint64_t Result = ::flock(fd, operation);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd) -> uint64_t {
uint64_t Result = ::fsync(fd);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fdatasync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd) -> uint64_t {
uint64_t Result = ::fdatasync(fd);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ftruncate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, off_t length) -> uint64_t {
uint64_t Result = ::ftruncate(fd, length);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchmod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, int mode) -> uint64_t {
uint64_t Result = ::fchmod(fd, mode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fadvise64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, off_t offset, off_t len, int advice) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(fadvise64), fd, offset, len, advice);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_init, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(inotify_init, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::inotify_init();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_add_watch, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *pathname, uint32_t mask) -> uint64_t {
uint64_t Result = ::inotify_add_watch(fd, pathname, mask);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_rm_watch, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, int wd) -> uint64_t {
uint64_t Result = ::inotify_rm_watch(fd, wd);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(openat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfs, const char *pathname, int flags, uint32_t mode) -> uint64_t {
flags = FEX::HLE::RemapFromX86Flags(flags);
@@ -161,63 +89,12 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mkdirat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, mode_t mode) -> uint64_t {
uint64_t Result = ::mkdirat(dirfd, pathname, mode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mknodat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, mode_t mode, dev_t dev) -> uint64_t {
uint64_t Result = ::mknodat(dirfd, pathname, mode, dev);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchownat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::fchownat(dirfd, pathname, owner, group, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(unlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::unlinkat(dirfd, pathname, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(renameat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int olddirfd, const char *oldpath, int newdirfd, const char *newpath) -> uint64_t {
uint64_t Result = ::renameat(olddirfd, oldpath, newdirfd, newpath);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(linkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::linkat(olddirfd, oldpath, newdirfd, newpath, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(symlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *target, int newdirfd, const char *linkpath) -> uint64_t {
uint64_t Result = ::symlinkat(target, newdirfd, linkpath);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(readlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, char *buf, size_t bufsiz) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Readlinkat(dirfd, pathname, buf, bufsiz);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchmodat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, mode_t mode) -> uint64_t {
uint64_t Result = syscall(SYSCALL_DEF(fchmodat), dirfd, pathname, mode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(faccessat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, int mode) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.FAccessat(dirfd, pathname, mode);
@@ -232,12 +109,6 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pidfd_getfd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pidfd, int fd, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(pidfd_getfd), pidfd, fd, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(openat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfs, const char *pathname, struct open_how *how, size_t usize) -> uint64_t {
open_how HostHow{};
@@ -251,32 +122,10 @@ namespace FEX::HLE {
}
else {
REGISTER_SYSCALL_IMPL(faccessat2, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(pidfd_getfd, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(openat2, UnimplementedSyscallSafe);
}
REGISTER_SYSCALL_IMPL_PASS_FLAGS(splice, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::splice(fd_in, off_in, fd_out, off_out, len, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tee, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd_in, int fd_out, size_t len, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::tee(fd_in, fd_out, len, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timerfd_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int32_t clockid, int32_t flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::timerfd_create(clockid, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(eventfd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(eventfd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint32_t count) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(eventfd2), count, 0);
SYSCALL_ERRNO();
@@ -289,27 +138,6 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_init1, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::inotify_init1(flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(renameat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = FHU::Syscalls::renameat2(olddirfd, oldpath, newdirfd, newpath, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(memfd_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *name, uint32_t flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::syscall(SYSCALL_DEF(memfd_create), name, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(statx, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf) -> uint64_t {
// Flags don't need remapped
@@ -317,45 +145,6 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(name_to_handle_at, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::syscall(SYSCALL_DEF(name_to_handle_at), dirfd, pathname, handle, mount_id, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(open_by_handle_at, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int mount_fd, struct file_handle *handle, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::syscall(SYSCALL_DEF(open_by_handle_at), mount_fd, handle, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(eventfd2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int count, int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::syscall(SYSCALL_DEF(eventfd2), count, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(copy_file_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::syscall(SYSCALL_DEF(copy_file_range), fd_in, off_in, fd_out, off_out, len, flags);
SYSCALL_ERRNO();
});
if (Handler->IsHostKernelVersionAtLeast(5, 3, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pidfd_open, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(pidfd_open), pid, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(pidfd_open, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 9, 0)) {
REGISTER_SYSCALL_IMPL_FLAGS(close_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int first, unsigned int last, unsigned int flags) -> uint64_t {
@@ -366,72 +155,5 @@ namespace FEX::HLE {
else {
REGISTER_SYSCALL_IMPL(close_range, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 13, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_create_ruleset, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *const rule_attr, size_t size, uint32_t flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(landlock_create_ruleset), rule_attr, size, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_add_rule, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint32_t ruleset_fd, uint64_t rule_type, void *const rule_attr, uint32_t flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(landlock_add_rule), ruleset_fd, rule_type, rule_attr, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_restrict_self, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint32_t ruleset_fd, uint32_t flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(landlock_restrict_self), ruleset_fd, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(landlock_create_ruleset, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(landlock_add_rule, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(landlock_restrict_self, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 14, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(memfd_secret, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint32_t flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(memfd_secret), flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(memfd_secret, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 15, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(process_mrelease, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pidfd, uint32_t flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(process_mrelease), pidfd, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(process_mrelease, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(6, 5, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(cachestat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, void* cstat_range, void* cstat, uint32_t flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(cachestat), fd, cstat_range, cstat, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(cachestat, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(6, 6, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchmodat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, mode_t mode, uint32_t flags) -> uint64_t {
uint64_t Result = syscall(SYSCALL_DEF(fchmodat2), dirfd, pathname, mode, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(fchmodat2, UnimplementedSyscallSafe);
}
}
}
@@ -25,55 +25,37 @@ namespace FEX::HLE {
void RegisterFS(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getcwd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, char *buf, size_t size) -> uint64_t {
uint64_t Result = syscall(SYSCALL_DEF(getcwd), buf, size);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path) -> uint64_t {
uint64_t Result = ::chdir(path);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd) -> uint64_t {
uint64_t Result = ::fchdir(fd);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(rename, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(rename, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *oldpath, const char *newpath) -> uint64_t {
uint64_t Result = ::rename(oldpath, newpath);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mkdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(mkdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, mode_t mode) -> uint64_t {
uint64_t Result = ::mkdir(pathname, mode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(rmdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(rmdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname) -> uint64_t {
uint64_t Result = ::rmdir(pathname);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(link, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(link, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *oldpath, const char *newpath) -> uint64_t {
uint64_t Result = ::link(oldpath, newpath);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(unlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(unlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname) -> uint64_t {
uint64_t Result = ::unlink(pathname);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(symlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(symlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *target, const char *linkpath) -> uint64_t {
uint64_t Result = ::symlink(target, linkpath);
SYSCALL_ERRNO();
@@ -85,103 +67,24 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chmod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(chmod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, mode_t mode) -> uint64_t {
uint64_t Result = ::chmod(pathname, mode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(umask, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, mode_t mask) -> uint64_t {
uint64_t Result = ::umask(mask);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(mknod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, mode_t mode, dev_t dev) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Mknod(pathname, mode, dev);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ustat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, dev_t dev, struct ustat *ubuf) -> uint64_t {
// Doesn't exist on AArch64, will return -ENOSYS
// Since version 2.28 of GLIBC it has stopped providing a wrapper for this syscall
uint64_t Result = syscall(SYSCALL_DEF(ustat), dev, ubuf);
SYSCALL_ERRNO();
});
/*
arg1 is one of: void, unsigned int fs_index, const char *fsname
arg2 is one of: void, char *buf
*/
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sysfs, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int option, uint64_t arg1, uint64_t arg2) -> uint64_t {
// Doesn't exist on AArch64, will return -ENOSYS
uint64_t Result = syscall(SYSCALL_DEF(sysfs), option, arg1, arg2);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(truncate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path, off_t length) -> uint64_t {
uint64_t Result = ::truncate(path, length);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(creat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(creat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, mode_t mode) -> uint64_t {
uint64_t Result = ::creat(pathname, mode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chroot, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path) -> uint64_t {
uint64_t Result = ::chroot(path);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
sync();
return 0; // always successful
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(acct, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *filename) -> uint64_t {
uint64_t Result = ::acct(filename);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) -> uint64_t {
uint64_t Result = ::mount(source, target, filesystemtype, mountflags, data);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(umount2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *target, int flags) -> uint64_t {
uint64_t Result = ::umount2(target, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(swapon, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path, int swapflags) -> uint64_t {
uint64_t Result = ::swapon(path, swapflags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(swapoff, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path) -> uint64_t {
uint64_t Result = ::swapoff(path);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(syncfs, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(syncfs), fd);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(setxattr,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, const void *value, size_t size, int flags) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Setxattr(path, name, value, size, flags);
@@ -194,12 +97,6 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsetxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name, const void *value, size_t size, int flags) -> uint64_t {
uint64_t Result = ::fsetxattr(fd, name, value, size, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(getxattr,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, void *value, size_t size) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Getxattr(path, name, value, size);
@@ -212,12 +109,6 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fgetxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name, void *value, size_t size) -> uint64_t {
uint64_t Result = ::fgetxattr(fd, name, value, size);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(listxattr,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path, char *list, size_t size) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Listxattr(path, list, size);
@@ -230,12 +121,6 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(flistxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, char *list, size_t size) -> uint64_t {
uint64_t Result = ::flistxattr(fd, list, size);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL(removexattr,
[](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name) -> uint64_t {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Removexattr(path, name);
@@ -247,40 +132,5 @@ namespace FEX::HLE {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.LRemovexattr(path, name);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fremovexattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name) -> uint64_t {
uint64_t Result = ::fremovexattr(fd, name);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fanotify_init, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int flags, unsigned int event_f_flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(fanotify_init), flags, event_f_flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fanotify_mark, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fanotify_fd, unsigned int flags, uint64_t mask, int dirfd, const char *pathname) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(fanotify_mark), fanotify_fd, flags, mask, dirfd, pathname);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pivot_root, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *new_root, const char *put_old) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(pivot_root), new_root, put_old);
SYSCALL_ERRNO();
});
if (Handler->IsHostKernelVersionAtLeast(5, 14, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(quotactl_fd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint32_t fd, uint32_t cmd, uint32_t id, void* addr) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(quotactl_fd), fd, cmd, id, addr);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(quotactl_fd, UnimplementedSyscallSafe);
}
}
}
@@ -19,52 +19,16 @@ namespace FEX::HLE {
void RegisterIO(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(iopl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(iopl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int level) -> uint64_t {
// Just claim we don't have permission
return -EPERM;
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ioperm, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(ioperm, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned long from, unsigned long num, int turn_on) -> uint64_t {
// ioperm not available on our architecture
return -EPERM;
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_setup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned nr_events, aio_context_t *ctx_idp) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_setup), nr_events, ctx_idp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_destroy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, aio_context_t ctx_id) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_destroy), ctx_id);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_submit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, aio_context_t ctx_id, long nr, struct iocb **iocbpp) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_submit), ctx_id, nr, iocbpp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_cancel, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, aio_context_t ctx_id, struct iocb *iocb, struct io_event *result) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_cancel), ctx_id, iocb, result);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ioprio_set, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int which, int who) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(ioprio_set), which, who);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ioprio_get, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int which, int who, int ioprio) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(ioprio_get), which, who, ioprio);
SYSCALL_ERRNO();
});
}
}
@@ -1,53 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/Syscalls/Thread.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <signal.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace SignalDelegator {
struct GuestSigAction;
}
namespace FEX::HLE {
void RegisterIOUring(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
if (Handler->IsHostKernelVersionAtLeast(5, 1, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_setup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint32_t entries, void* params) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_uring_setup), entries, params);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_enter, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, uint32_t to_submit, uint32_t min_complete, uint32_t flags, void *argp, size_t argsz) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_uring_enter), fd, to_submit, min_complete, flags, argp, argsz);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_register, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, unsigned int opcode, void *arg, uint32_t nr_args) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_uring_register), fd, opcode, arg, nr_args);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(io_uring_setup, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(io_uring_enter, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(io_uring_register, UnimplementedSyscallSafe);
}
}
}
@@ -63,44 +63,6 @@ namespace FEX::HLE {
return 0;
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(syslog, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int type, char *bufp, int len) -> uint64_t {
uint64_t Result = ::klogctl(type, bufp, len);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getrandom, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *buf, size_t buflen, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(getrandom), buf, buflen, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(capget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, cap_user_header_t hdrp, cap_user_data_t datap) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(capget), hdrp, datap);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(capset, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, cap_user_header_t hdrp, const cap_user_data_t datap) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(capset), hdrp, datap);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(getcpu, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned *cpu, unsigned *node, struct getcpu_cache *tcache) -> uint64_t {
// tcache is ignored
uint64_t Result = ::syscall(SYSCALL_DEF(getcpu), cpu, node, nullptr);
SYSCALL_ERRNO();
});
//compare two processes to determine if they share a kernel resource
REGISTER_SYSCALL_IMPL_PASS_FLAGS(kcmp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(kcmp), pid1, pid2, type, idx1, idx2);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(seccomp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int operation, unsigned int flags, void *args) -> uint64_t {
// FEX doesn't support seccomp
@@ -1,62 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/Types.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace FEX::HLE {
void RegisterKey(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(add_key, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *type, const char *description, const void *payload, size_t plen, key_serial_t keyring) -> uint64_t {
uint64_t Result = syscall(SYS_add_key, type, description, payload, plen, keyring);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(request_key, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *type, const char *description, const char *callout_info, key_serial_t dest_keyring) -> uint64_t {
uint64_t Result = syscall(SYS_request_key, type, description, callout_info, dest_keyring);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(keyctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int operation, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) -> uint64_t {
uint64_t Result = syscall(SYS_keyctl, operation, arg2, arg3, arg4, arg5);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_mprotect, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t len, int prot, int pkey) -> uint64_t {
// Added in Linux 4.9
uint64_t Result = ::syscall(SYSCALL_DEF(pkey_mprotect), addr, len, prot, pkey);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_alloc, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int flags, unsigned int access_rights) -> uint64_t {
// Added in Linux 4.9
uint64_t Result = ::syscall(SYSCALL_DEF(pkey_alloc), flags, access_rights);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_free, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pkey) -> uint64_t {
// Added in Linux 4.9
uint64_t Result = ::syscall(SYSCALL_DEF(pkey_free), pkey);
SYSCALL_ERRNO();
});
}
}
@@ -28,18 +28,6 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t length, int32_t flags) -> uint64_t {
uint64_t Result = ::msync(addr, length, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mincore, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t length, uint8_t *vec) -> uint64_t {
uint64_t Result = ::mincore(addr, length, vec);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(madvise, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t length, int32_t advice) -> uint64_t {
uint64_t Result = ::madvise(addr, length, advice);
@@ -49,77 +37,5 @@ namespace FEX::HLE {
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mlock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const void *addr, size_t len) -> uint64_t {
uint64_t Result = ::mlock(addr, len);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(munlock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const void *addr, size_t len) -> uint64_t {
uint64_t Result = ::munlock(addr, len);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mlock2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const void *addr, size_t len, int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mlock2), addr, len, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(remap_file_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t size, int prot, size_t pgoff, int flags) -> uint64_t {
// This syscall is deprecated, not sure when it will end up being removed
uint64_t Result = ::syscall(SYSCALL_DEF(remap_file_pages), addr, size, prot, pgoff, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mbind, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *addr, unsigned long len, int mode, const unsigned long *nodemask, unsigned long maxnode, unsigned flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mbind), addr, len, mode, nodemask, maxnode, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(get_mempolicy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int *mode, unsigned long *nodemask, unsigned long maxnode, void *addr, unsigned long flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(get_mempolicy), mode, nodemask, maxnode, addr, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_mempolicy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int mode, const unsigned long *nodemask, unsigned long maxnode) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(set_mempolicy), mode, nodemask, maxnode);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(migrate_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pid, unsigned long maxnode, const unsigned long *old_nodes, const unsigned long *new_nodes) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(migrate_pages), pid, maxnode, old_nodes, new_nodes);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(move_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(move_pages), pid, count, pages, nodes, status, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(membarrier, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int cmd, int flags) -> uint64_t {
uint64_t Result = syscall(SYSCALL_DEF(membarrier), cmd, flags);
SYSCALL_ERRNO();
});
if (Handler->IsHostKernelVersionAtLeast(5, 17, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_mempolicy_home_node, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint64_t start, uint64_t len, uint64_t home_node, uint64_t flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(set_mempolicy_home_node), start, len, home_node, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(set_mempolicy_home_node, UnimplementedSyscallSafe);
}
}
}
@@ -1,58 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace FEX::HLE {
void RegisterMsg(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, key_t key, int msgflg) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(msgget), key, msgflg);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgsnd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int msqid, const void *msgp, size_t msgsz, int msgflg) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(msgsnd), msqid, msgp, msgsz, msgflg);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgrcv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(msgrcv), msqid, msgp, msgsz, msgtyp, msgflg);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int msqid, int cmd, struct msqid_ds *buf) -> uint64_t {
// A quirk of this syscall
// On 32-bit this syscall ONLY supports IPC_64 msqid_ds encoding
// If an application want to use the old style encoding then it needs to use the ipc syscall with MSGCTL command
// ipc syscall supports both IPC_64 and old encoding
uint64_t Result = ::syscall(SYSCALL_DEF(msgctl), msqid, cmd, buf);
SYSCALL_ERRNO();
});
// last two parameters are optional
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mq_unlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const char *name) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_unlink), name);
SYSCALL_ERRNO();
});
}
}
@@ -1,85 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/Syscalls/Thread.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <signal.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace SignalDelegator {
struct GuestSigAction;
}
namespace FEX::HLE {
void RegisterNamespace(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 1, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(open_tree, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dfd, const char *filename, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(open_tree), dfd, filename, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(move_mount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int from_dfd, const char *from_pathname, int to_dfd, const char *to_pathname, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(move_mount), from_dfd, from_pathname, to_dfd, to_pathname, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsopen, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dfd, const char *path, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(fsopen), dfd, path, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsconfig, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, unsigned int cmd, const char *key, const void *value, int aux) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(fsconfig), fd, cmd, key, value, aux);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsmount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fs_fd, uint32_t flags, uint32_t attr_flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(fsmount), fs_fd, flags, attr_flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fspick, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dfd, const char *path, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(fspick), dfd, path, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(open_tree, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(move_mount, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fsopen, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fsconfig, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fsmount, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fspick, UnimplementedSyscallSafe);
}
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 12, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mount_setattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int dfd, const char *path, unsigned int flags, void *uattr, size_t usize) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mount_setattr), dfd, path, flags, uattr, usize);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(mount_setattr, UnimplementedSyscallSafe);
}
}
}
@@ -30,6 +30,8 @@ namespace FEX::HLE {
// these are removed/not implemented in the linux kernel we present
void RegisterNotImplemented(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_NOT_IMPL(ustat);
REGISTER_SYSCALL_NOT_IMPL(sysfs);
REGISTER_SYSCALL_NOT_IMPL(uselib);
REGISTER_SYSCALL_NOT_IMPL(create_module);
REGISTER_SYSCALL_NOT_IMPL(get_kernel_syms);
@@ -0,0 +1,889 @@
// SPDX-License-Identifier: MIT
/*
$info$
meta: LinuxSyscalls|syscalls-shared ~ Syscall implementations shared between x86 and x86-64
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <stdint.h>
#include <sys/epoll.h>
namespace FEX::HLE {
#ifdef _M_ARM_64
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough0(FEXCore::Core::CpuStateFrame *Frame) {
register uint64_t x0 asm ("x0");
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
: "memory");
return x0;
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough1(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1) {
register uint64_t x0 asm ("x0") = arg1;
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
, "r" (x0)
: "memory");
return x0;
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough2(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2) {
register uint64_t x0 asm ("x0") = arg1;
register uint64_t x1 asm ("x1") = arg2;
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
, "r" (x0)
, "r" (x1)
: "memory");
return x0;
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough3(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3) {
register uint64_t x0 asm ("x0") = arg1;
register uint64_t x1 asm ("x1") = arg2;
register uint64_t x2 asm ("x2") = arg3;
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
, "r" (x0)
, "r" (x1)
, "r" (x2)
: "memory");
return x0;
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough4(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) {
register uint64_t x0 asm ("x0") = arg1;
register uint64_t x1 asm ("x1") = arg2;
register uint64_t x2 asm ("x2") = arg3;
register uint64_t x3 asm ("x3") = arg4;
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
, "r" (x0)
, "r" (x1)
, "r" (x2)
, "r" (x3)
: "memory");
return x0;
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough5(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
register uint64_t x0 asm ("x0") = arg1;
register uint64_t x1 asm ("x1") = arg2;
register uint64_t x2 asm ("x2") = arg3;
register uint64_t x3 asm ("x3") = arg4;
register uint64_t x4 asm ("x4") = arg5;
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
, "r" (x0)
, "r" (x1)
, "r" (x2)
, "r" (x3)
, "r" (x4)
: "memory");
return x0;
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough6(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6) {
register uint64_t x0 asm ("x0") = arg1;
register uint64_t x1 asm ("x1") = arg2;
register uint64_t x2 asm ("x2") = arg3;
register uint64_t x3 asm ("x3") = arg4;
register uint64_t x4 asm ("x4") = arg5;
register uint64_t x5 asm ("x5") = arg6;
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
, "r" (x0)
, "r" (x1)
, "r" (x2)
, "r" (x3)
, "r" (x4)
, "r" (x5)
: "memory");
return x0;
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough7(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7) {
register uint64_t x0 asm ("x0") = arg1;
register uint64_t x1 asm ("x1") = arg2;
register uint64_t x2 asm ("x2") = arg3;
register uint64_t x3 asm ("x3") = arg4;
register uint64_t x4 asm ("x4") = arg5;
register uint64_t x5 asm ("x5") = arg6;
register uint64_t x6 asm ("x6") = arg7;
register int x8 asm ("x8") = syscall_num;
__asm volatile(R"(
svc #0;
)"
: "=r" (x0)
: "r" (x8)
, "r" (x0)
, "r" (x1)
, "r" (x2)
, "r" (x3)
, "r" (x4)
, "r" (x5)
, "r" (x6)
: "memory");
return x0;
}
#else
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough0(FEXCore::Core::CpuStateFrame *Frame) {
uint64_t Result = ::syscall(syscall_num);
SYSCALL_ERRNO();
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough1(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1) {
uint64_t Result = ::syscall(syscall_num, arg1);
SYSCALL_ERRNO();
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough2(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2) {
uint64_t Result = ::syscall(syscall_num, arg1, arg2);
SYSCALL_ERRNO();
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough3(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3) {
uint64_t Result = ::syscall(syscall_num, arg1, arg2, arg3);
SYSCALL_ERRNO();
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough4(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) {
uint64_t Result = ::syscall(syscall_num, arg1, arg2, arg3, arg4);
SYSCALL_ERRNO();
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough5(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
uint64_t Result = ::syscall(syscall_num, arg1, arg2, arg3, arg4, arg5);
SYSCALL_ERRNO();
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough6(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6) {
uint64_t Result = ::syscall(syscall_num, arg1, arg2, arg3, arg4, arg5, arg6);
SYSCALL_ERRNO();
}
template<int syscall_num>
requires (syscall_num != -1)
uint64_t SyscallPassthrough7(FEXCore::Core::CpuStateFrame *Frame, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7) {
uint64_t Result = ::syscall(syscall_num, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
SYSCALL_ERRNO();
}
#endif
void RegisterCommon(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(read, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(read)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(write, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(write)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(lseek, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(lseek)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_yield, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(sched_yield)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(msync)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mincore, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(mincore)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(_shmget)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(_shmctl)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getpid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(socket, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(socket)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(connect, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(connect)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sendto, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(sendto)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(recvfrom, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(recvfrom)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shutdown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(shutdown)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(bind, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(bind)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(listen, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(listen)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getsockname, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getsockname)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpeername, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getpeername)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(socketpair, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(socketpair)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(kill, SyscallFlags::DEFAULT,
SyscallPassthrough2<SYSCALL_DEF(kill)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(semget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(semget)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(msgget)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgsnd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(msgsnd)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgrcv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(msgrcv)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(msgctl)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(flock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(flock)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(fsync)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fdatasync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(fdatasync)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(truncate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(truncate)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ftruncate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(ftruncate)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getcwd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(getcwd)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(chdir)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(fchdir)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchmod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(fchmod)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(fchown)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(umask, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(umask)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getuid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(syslog, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(syslog)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getgid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setuid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setgid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(geteuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(geteuid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getegid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getegid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setpgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setpgid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getppid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getppid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setsid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(setsid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setreuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setreuid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setregid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setregid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(getgroups)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setgroups)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(setresuid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getresuid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(setresgid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getresgid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(getpgid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setfsuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setfsuid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setfsgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setfsgid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getsid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(getsid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(capget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(capget)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(capset, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(capset)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(personality, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(personality)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpriority, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(getpriority)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setpriority, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(setpriority)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setparam, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(sched_setparam)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getparam, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(sched_getparam)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setscheduler, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(sched_setscheduler)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getscheduler, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(sched_getscheduler)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_get_priority_max, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(sched_get_priority_max)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_get_priority_min, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(sched_get_priority_min)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mlock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(mlock)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(munlock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(munlock)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pivot_root, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(pivot_root)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chroot, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(chroot)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(sync)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(acct, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(acct)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(mount)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(umount2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(umount2)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(swapon, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(swapon)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(swapoff, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(swapoff)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(gettid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(gettid)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsetxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(fsetxattr)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fgetxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(fgetxattr)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(flistxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(flistxattr)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fremovexattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(fremovexattr)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tkill, SyscallFlags::DEFAULT,
SyscallPassthrough2<SYSCALL_DEF(tkill)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setaffinity, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(sched_setaffinity)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getaffinity, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(sched_getaffinity)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_setup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(io_setup)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_destroy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(io_destroy)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_submit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(io_submit)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_cancel, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(io_cancel)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(remap_file_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(remap_file_pages)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fadvise64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(fadvise64)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timer_getoverrun, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(timer_getoverrun)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timer_delete, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(timer_delete)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tgkill, SyscallFlags::DEFAULT,
SyscallPassthrough3<SYSCALL_DEF(tgkill)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mbind, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(mbind)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_mempolicy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(set_mempolicy)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(get_mempolicy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(get_mempolicy)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mq_unlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(mq_unlink)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(add_key, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(add_key)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(request_key, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(request_key)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(keyctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(keyctl)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ioprio_set, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(ioprio_set)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ioprio_get, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(ioprio_get)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_add_watch, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(inotify_add_watch)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_rm_watch, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(inotify_rm_watch)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(migrate_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(migrate_pages)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mkdirat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(mkdirat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mknodat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(mknodat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchownat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(fchownat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(unlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(unlinkat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(renameat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(renameat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(linkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(linkat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(symlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(symlinkat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchmodat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(fchmodat)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(unshare, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(unshare)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(splice, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(splice)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tee, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(tee)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(move_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(move_pages)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timerfd_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(timerfd_create)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(accept4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(accept4)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(eventfd2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(eventfd2)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(epoll_create1, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(epoll_create1)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_init1, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(inotify_init1)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fanotify_init, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(fanotify_init)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fanotify_mark, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(fanotify_mark)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(prlimit_64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(prlimit_64)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(name_to_handle_at, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(name_to_handle_at)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(open_by_handle_at, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(open_by_handle_at)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(syncfs, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(syncfs)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setns, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setns)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getcpu, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getcpu)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(kcmp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(kcmp)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(sched_setattr)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(sched_getattr)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(renameat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(renameat2)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getrandom, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getrandom)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(memfd_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(memfd_create)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(membarrier, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(membarrier)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mlock2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(mlock2)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(copy_file_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(copy_file_range)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_mprotect, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(pkey_mprotect)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_alloc, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(pkey_alloc)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_free, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(pkey_free)>);
if (Handler->IsHostKernelVersionAtLeast(5, 1, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_setup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(io_uring_setup)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_enter, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(io_uring_enter)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_register, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(io_uring_register)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(open_tree, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(open_tree)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(move_mount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(move_mount)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsopen, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(fsopen)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsconfig, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(fsconfig)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsmount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(fsmount)>);
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fspick, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(fspick)>);
}
else {
REGISTER_SYSCALL_IMPL(io_uring_setup, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(io_uring_enter, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(io_uring_register, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(open_tree, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(move_mount, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fsopen, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fsconfig, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fsmount, UnimplementedSyscallSafe);
REGISTER_SYSCALL_IMPL(fspick, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 3, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pidfd_open, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(pidfd_open)>);
}
else {
REGISTER_SYSCALL_IMPL(pidfd_open, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 8, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pidfd_getfd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(pidfd_getfd)>);
}
else {
REGISTER_SYSCALL_IMPL(pidfd_getfd, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 12, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mount_setattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(mount_setattr)>);
}
else {
REGISTER_SYSCALL_IMPL(mount_setattr, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 14, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(quotactl_fd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(quotactl_fd)>);
}
else {
REGISTER_SYSCALL_IMPL(quotactl_fd, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 13, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_create_ruleset, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(landlock_create_ruleset)>);
}
else {
REGISTER_SYSCALL_IMPL(landlock_create_ruleset, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 13, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_add_rule, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(landlock_add_rule)>);
}
else {
REGISTER_SYSCALL_IMPL(landlock_add_rule, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 13, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_restrict_self, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(landlock_restrict_self)>);
}
else {
REGISTER_SYSCALL_IMPL(landlock_restrict_self, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 14, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(memfd_secret, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(memfd_secret)>);
}
else {
REGISTER_SYSCALL_IMPL(memfd_secret, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 15, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(process_mrelease, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(process_mrelease)>);
}
else {
REGISTER_SYSCALL_IMPL(process_mrelease, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 16, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(futex_waitv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(futex_waitv)>);
}
else {
REGISTER_SYSCALL_IMPL(futex_waitv, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 17, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_mempolicy_home_node, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(set_mempolicy_home_node)>);
}
else {
REGISTER_SYSCALL_IMPL(set_mempolicy_home_node, UnimplementedSyscallSafe);
}
}
namespace x64 {
void RegisterPassthrough(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
RegisterCommon(Handler);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(ioctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(ioctl)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pread_64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(pread_64)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pwrite_64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(pwrite_64)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(readv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(readv)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(writev, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(writev)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(dup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(dup)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(nanosleep, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(nanosleep)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getitimer, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(getitimer)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(setitimer, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(setitimer)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sendfile, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(sendfile)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(accept, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(accept)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sendmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(sendmsg)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(recvmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(recvmsg)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(setsockopt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(setsockopt)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getsockopt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(getsockopt)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(wait4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(wait4)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(semop, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(semop)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(gettimeofday, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(gettimeofday)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getrlimit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(getrlimit)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getrusage, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(getrusage)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sysinfo, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(sysinfo)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(times, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(times)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(rt_sigqueueinfo, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(rt_sigqueueinfo)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(fstatfs, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(fstatfs)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sched_rr_get_interval, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(sched_rr_get_interval)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mlockall, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(mlockall)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(munlockall, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(munlockall)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(adjtimex, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(adjtimex)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(setrlimit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setrlimit)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(settimeofday, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(settimeofday)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(readahead, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(readahead)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(futex, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(futex)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(io_getevents, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(io_getevents)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getdents64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getdents64)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(semtimedop, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(semtimedop)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timer_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(timer_create)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timer_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(timer_settime)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timer_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(timer_gettime)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_settime)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_gettime)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_getres, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_getres)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_nanosleep, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(clock_nanosleep)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_open, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(mq_open)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_timedsend, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(mq_timedsend)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_timedreceive, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(mq_timedreceive)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_notify, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(mq_notify)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_getsetattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(mq_getsetattr)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(waitid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(waitid)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pselect6, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(pselect6)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(ppoll, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(ppoll)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(set_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(set_robust_list)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(get_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(get_robust_list)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sync_file_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(sync_file_range)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(vmsplice, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(vmsplice)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(utimensat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(utimensat)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(fallocate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(fallocate)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timerfd_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(timerfd_settime)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timerfd_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(timerfd_gettime)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(preadv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(preadv)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pwritev, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(pwritev)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(rt_tgsigqueueinfo, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(rt_tgsigqueueinfo)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(recvmmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(recvmmsg)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_adjtime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_adjtime)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sendmmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(sendmmsg)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(process_vm_readv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(process_vm_readv)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(process_vm_writev, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(process_vm_writev)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(preadv2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(preadv2)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pwritev2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(pwritev2)>);
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(io_pgetevents, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(io_pgetevents)>);
if (Handler->IsHostKernelVersionAtLeast(5, 1, 0)) {
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pidfd_send_signal, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(pidfd_send_signal)>);
}
else {
REGISTER_SYSCALL_IMPL_X64(pidfd_send_signal, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(5, 10, 0)) {
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(process_madvise, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(process_madvise)>);
}
else {
REGISTER_SYSCALL_IMPL_X64(process_madvise, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(6, 5, 0)) {
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(cachestat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(cachestat)>);
}
else {
REGISTER_SYSCALL_IMPL_X64(cachestat, UnimplementedSyscallSafe);
}
if (Handler->IsHostKernelVersionAtLeast(6, 6, 0)) {
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(fchmodat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(fchmodat2)>);
}
else {
REGISTER_SYSCALL_IMPL_X64(fchmodat2, UnimplementedSyscallSafe);
}
}
}
namespace x32 {
void RegisterPassthrough(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
RegisterCommon(Handler);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getuid32, getuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getuid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getgid32, getgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getgid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(geteuid32, geteuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(geteuid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getegid32, getegid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough0<SYSCALL_DEF(getegid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setreuid32, setreuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setreuid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setregid32, setregid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setregid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getgroups32, getgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(getgroups)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setgroups32, setgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(setgroups)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(fchown32, fchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(fchown)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setresuid32, setresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(setresuid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getresuid32, getresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getresuid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setresgid32, setresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(setresgid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getresgid32, getresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough3<SYSCALL_DEF(getresgid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setuid32, setuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setuid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setgid32, setgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setgid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setfsuid32, setfsuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setfsuid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setfsgid32, setfsgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough1<SYSCALL_DEF(setfsgid)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(sendfile64, sendfile, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(sendfile)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_gettime64, clock_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_gettime)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_settime64, clock_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_settime)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_adjtime64, clock_adjtime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_adjtime)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_getres_time64, clock_getres, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(clock_getres)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_nanosleep_time64, clock_nanosleep, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(clock_nanosleep)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timer_gettime64, timer_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(timer_gettime)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timer_settime64, timer_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(timer_settime)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timerfd_gettime64, timerfd_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(timerfd_gettime)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timerfd_settime64, timerfd_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(timerfd_settime)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(utimensat_time64, utimensat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(utimensat)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(ppoll_time64, ppoll, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(ppoll)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(io_pgetevents_time64, io_pgetevents, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(io_pgetevents)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(mq_timedsend_time64, mq_timedsend, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(mq_timedsend)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(mq_timedreceive_time64, mq_timedreceive, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough5<SYSCALL_DEF(mq_timedreceive)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(semtimedop_time64, semtimedop, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough4<SYSCALL_DEF(semtimedop)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(futex_time64, futex, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough6<SYSCALL_DEF(futex)>);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(sched_rr_get_interval_time64, sched_rr_get_interval, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
SyscallPassthrough2<SYSCALL_DEF(sched_rr_get_interval)>);
}
}
}
@@ -1,35 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/shm.h>
namespace FEX::HLE {
void RegisterSHM(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, key_t key, size_t size, int shmflg) -> uint64_t {
uint64_t Result = shmget(key, size, shmflg);
SYSCALL_ERRNO();
});
// XXX: shmid_ds is definitely not correct for 32-bit
REGISTER_SYSCALL_IMPL_PASS_FLAGS(_shmctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int shmid, int cmd, struct shmid_ds *buf) -> uint64_t {
uint64_t Result = ::shmctl(shmid, cmd, buf);
SYSCALL_ERRNO();
});
}
}
@@ -1,105 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/IR/IR.h>
#include <stdint.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace FEX::HLE {
void RegisterSched(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_yield, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::sched_yield();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpriority, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int which, int who) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(getpriority), which, who);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setpriority, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int which, int who, int prio) -> uint64_t {
uint64_t Result = ::setpriority(which, who, prio);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setparam, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct sched_param *param) -> uint64_t {
uint64_t Result = ::sched_setparam(pid, param);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getparam, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, struct sched_param *param) -> uint64_t {
uint64_t Result = ::sched_getparam(pid, param);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setscheduler, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int policy, const struct sched_param *param) -> uint64_t {
uint64_t Result = ::sched_setscheduler(pid, policy, param);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getscheduler, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
uint64_t Result = ::sched_getscheduler(pid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_get_priority_max, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int policy) -> uint64_t {
uint64_t Result = ::sched_get_priority_max(policy);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_get_priority_min, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int policy) -> uint64_t {
uint64_t Result = ::sched_get_priority_min(policy);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(sched_setaffinity, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY | SyscallFlags::NOSIDEEFFECTS,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, size_t cpusetsize, const unsigned long *mask) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(sched_setaffinity), pid, cpusetsize, mask);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(sched_getaffinity, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, size_t cpusetsize, unsigned char *mask) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(sched_getaffinity), pid, cpusetsize, mask);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, struct sched_attr *attr, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(sched_setattr), pid, attr, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(sched_getattr), pid, attr, size, flags);
SYSCALL_ERRNO();
});
}
}
@@ -1,27 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <sys/types.h>
namespace FEX::HLE {
void RegisterSemaphore(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(semget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, key_t key, int nsems, int semflg) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(semget), key, nsems, semflg);
SYSCALL_ERRNO();
});
}
}
@@ -1,88 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
namespace FEX::HLE {
void RegisterSocket(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(socket, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int domain, int type, int protocol) -> uint64_t {
uint64_t Result = ::socket(domain, type, protocol);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(connect, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, const struct sockaddr *addr, socklen_t addrlen) -> uint64_t {
uint64_t Result = ::connect(sockfd, addr, addrlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(accept4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) -> uint64_t {
uint64_t Result = ::accept4(sockfd, addr, addrlen, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sendto, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) -> uint64_t {
uint64_t Result = ::sendto(sockfd, buf, len, flags, dest_addr, addrlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(recvfrom, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) -> uint64_t {
uint64_t Result = ::recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shutdown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, int how) -> uint64_t {
uint64_t Result = ::shutdown(sockfd, how);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(bind, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, const struct sockaddr *addr, socklen_t addrlen) -> uint64_t {
uint64_t Result = ::bind(sockfd, addr, addrlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(listen, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, int backlog) -> uint64_t {
uint64_t Result = ::listen(sockfd, backlog);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getsockname, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct sockaddr *addr, socklen_t *addrlen) -> uint64_t {
uint64_t Result = ::getsockname(sockfd, addr, addrlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpeername, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct sockaddr *addr, socklen_t *addrlen) -> uint64_t {
uint64_t Result = ::getpeername(sockfd, addr, addrlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(socketpair, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int domain, int type, int protocol, int sv[2]) -> uint64_t {
uint64_t Result = ::socketpair(domain, type, protocol, sv);
SYSCALL_ERRNO();
});
}
}
@@ -52,7 +52,10 @@ namespace FEX::HLE {
auto CTX = Handler->CTX;
auto Thread = Handler->Thread;
FEXCore::Allocator::free(Handler);
FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterTLSState(Thread);
CTX->ExecutionThread(Thread);
FEX::HLE::_SyscallHandler->GetSignalDelegator()->UninstallTLSState(Thread);
FEX::HLE::_SyscallHandler->TM.DestroyThread(Thread);
return nullptr;
}
@@ -220,10 +223,14 @@ namespace FEX::HLE {
FEX::HLE::_SyscallHandler->TM.TrackThread(Thread);
}
FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterTLSState(Thread);
// Start exuting the thread directly
// Our host clone starts in a new stack space, so it can't return back to the JIT space
CTX->ExecutionThread(Thread);
FEX::HLE::_SyscallHandler->GetSignalDelegator()->UninstallTLSState(Thread);
// The rest of the context remains as is and the thread will continue executing
return Thread->StatusCode;
}
@@ -356,12 +363,6 @@ namespace FEX::HLE {
FEX_UNREACHABLE;
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getpid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(fork, SyscallFlags::DEFAULT, [](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
return ForkGuest(Frame->Thread, Frame, 0, 0, 0, 0, 0, 0);
});
@@ -370,6 +371,12 @@ namespace FEX::HLE {
return ForkGuest(Frame->Thread, Frame, CLONE_VFORK, 0, 0, 0, 0, 0);
});
REGISTER_SYSCALL_IMPL_FLAGS(getpgrp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getpgrp();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(clone3, SyscallFlags::DEFAULT, ([](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::kernel_clone3_args *cl_args, size_t size) -> uint64_t {
FEX::HLE::clone3_args args{};
args.Type = TypeOfClone::TYPE_CLONE3;
@@ -403,130 +410,6 @@ namespace FEX::HLE {
return 0;
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(kill, SyscallFlags::DEFAULT, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int sig) -> uint64_t {
uint64_t Result = ::kill(pid, sig);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tkill, SyscallFlags::DEFAULT, [](FEXCore::Core::CpuStateFrame *Frame, int tid, int sig) -> uint64_t {
// Can't actually use tgkill here, kernel rejects tgkill of tgid == 0
uint64_t Result = ::syscall(SYSCALL_DEF(tkill), tid, sig);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tgkill, SyscallFlags::DEFAULT, [](FEXCore::Core::CpuStateFrame *Frame, int tgid, int tid, int sig) -> uint64_t {
uint64_t Result = FHU::Syscalls::tgkill(tgid, tid, sig);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getuid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getgid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uid_t uid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(setuid), uid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, gid_t gid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(setgid), gid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(geteuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::geteuid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getegid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getegid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getppid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getppid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpgrp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getpgrp();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setsid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::setsid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setreuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uid_t ruid, uid_t euid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(setreuid), ruid, euid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setregid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, gid_t rgid, gid_t egid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(setregid), rgid, egid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int size, gid_t list[]) -> uint64_t {
uint64_t Result = ::getgroups(size, list);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, size_t size, const gid_t *list) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(setgroups), size, list);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uid_t ruid, uid_t euid, uid_t suid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(setresuid), ruid, euid, suid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uid_t *ruid, uid_t *euid, uid_t *suid) -> uint64_t {
uint64_t Result = ::getresuid(ruid, euid, suid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, gid_t rgid, gid_t egid, gid_t sgid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(setresgid), rgid, egid, sgid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, gid_t *rgid, gid_t *egid, gid_t *sgid) -> uint64_t {
uint64_t Result = ::getresgid(rgid, egid, sgid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(personality, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint64_t persona) -> uint64_t {
uint64_t Result = ::personality(persona);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_FLAGS(prctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) -> uint64_t {
uint64_t Result{};
@@ -610,20 +493,14 @@ namespace FEX::HLE {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(gettid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = FHU::Syscalls::gettid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_tid_address, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(set_tid_address, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int *tidptr) -> uint64_t {
auto Thread = Frame->Thread;
Thread->ThreadManager.clear_child_tid = tidptr;
return Thread->ThreadManager.GetTID();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(exit_group, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY | SyscallFlags::NORETURN,
REGISTER_SYSCALL_IMPL_FLAGS(exit_group, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY | SyscallFlags::NORETURN,
[](FEXCore::Core::CpuStateFrame *Frame, int status) -> uint64_t {
// Save telemetry if we're exiting.
@@ -633,64 +510,5 @@ namespace FEX::HLE {
// This will never be reached
std::terminate();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(prlimit_64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(prlimit_64), pid, resource, new_limit, old_limit);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setpgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, pid_t pgid) -> uint64_t {
uint64_t Result = ::setpgid(pid, pgid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
uint64_t Result = ::getpgid(pid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setfsuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uid_t fsuid) -> uint64_t {
uint64_t Result = ::setfsuid(fsuid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setfsgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uid_t fsgid) -> uint64_t {
uint64_t Result = ::setfsgid(fsgid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getsid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
uint64_t Result = ::getsid(pid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(unshare, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
uint64_t Result = ::unshare(flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setns, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int fd, int nstype) -> uint64_t {
uint64_t Result = ::setns(fd, nstype);
SYSCALL_ERRNO();
});
if (Handler->IsHostKernelVersionAtLeast(5, 16, 0)) {
REGISTER_SYSCALL_IMPL_PASS_FLAGS(futex_waitv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, void *waiters, uint32_t nr_futexes, uint32_t flags, struct timespec *timeout, clockid_t clockid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(futex_waitv), waiters, nr_futexes, flags, timeout, clockid);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL(futex_waitv, UnimplementedSyscallSafe);
}
}
}
@@ -1,26 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include <FEXCore/IR/IR.h>
#include <unistd.h>
namespace FEX::HLE {
void RegisterTime(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pause, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::pause();
SYSCALL_ERRNO();
});
}
}
@@ -24,21 +24,15 @@ namespace FEX::HLE {
void RegisterTimer(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(alarm, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(alarm, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, unsigned int seconds) -> uint64_t {
uint64_t Result = ::alarm(seconds);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timer_getoverrun, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, kernel_timer_t timerid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(timer_getoverrun), timerid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timer_delete, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, kernel_timer_t timerid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(timer_delete), timerid);
REGISTER_SYSCALL_IMPL_FLAGS(pause, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::pause();
SYSCALL_ERRNO();
});
}
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
#include "LinuxSyscalls/Utils/Threads.h"
#include "LinuxSyscalls/Syscalls.h"
#include <FEXCore/Core/Context.h>
#include <FEXCore/Utils/Threads.h>
@@ -12,33 +13,53 @@ namespace FEX::LinuxEmulation::Threads {
void *Ptr;
size_t Size;
};
struct DeadStackPoolItem {
void *Ptr;
size_t Size;
bool ReadyToBeReaped;
};
std::mutex DeadStackPoolMutex{};
std::mutex LiveStackPoolMutex{};
static fextl::deque<StackPoolItem> DeadStackPool{};
static fextl::deque<DeadStackPoolItem> DeadStackPool{};
static fextl::deque<StackPoolItem> LiveStackPool{};
void *AllocateStackObject() {
std::lock_guard lk{DeadStackPoolMutex};
if (DeadStackPool.size() == 0) {
// Nothing in the pool, just allocate
return FEXCore::Allocator::mmap(nullptr, FEX::LinuxEmulation::Threads::STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
// Keep the first item in the stack pool
auto Result = DeadStackPool.front().Ptr;
DeadStackPool.pop_front();
void *Ptr{};
// Erase the rest as a garbage collection step
for (auto &Item : DeadStackPool) {
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
for (auto it = DeadStackPool.begin(); it != DeadStackPool.end();) {
auto Ready = std::atomic_ref<bool>(it->ReadyToBeReaped);
bool ReadyToBeReaped = Ready.load();
if (Ptr == nullptr && ReadyToBeReaped) {
Ptr = it->Ptr;
it = DeadStackPool.erase(it);
continue;
}
if (ReadyToBeReaped) {
FEXCore::Allocator::munmap(it->Ptr, it->Size);
it = DeadStackPool.erase(it);
continue;
}
++it;
}
return Result;
if (Ptr == nullptr) {
Ptr = FEXCore::Allocator::mmap(nullptr, FEX::LinuxEmulation::Threads::STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
return Ptr;
}
void AddStackToDeadPool(void *Ptr) {
bool *AddStackToDeadPool(void *Ptr) {
std::lock_guard lk{DeadStackPoolMutex};
DeadStackPool.emplace_back(StackPoolItem{Ptr, FEX::LinuxEmulation::Threads::STACK_SIZE});
auto &it = DeadStackPool.emplace_back(DeadStackPoolItem{Ptr, FEX::LinuxEmulation::Threads::STACK_SIZE, false});
return &it.ReadyToBeReaped;
}
void AddStackToLivePool(void *Ptr) {
@@ -61,6 +82,31 @@ namespace FEX::LinuxEmulation::Threads {
AddStackToDeadPool(Ptr);
}
[[noreturn]]
void DeallocateStackObjectAndExit(void *Ptr, int Status) {
RemoveStackFromLivePool(Ptr);
auto ReadyToBeReaped = AddStackToDeadPool(Ptr);
*ReadyToBeReaped = true;
#ifdef _M_ARM_64
__asm volatile(
"mov x8, %[SyscallNum];"
"mov w0, %w[Result];"
"svc #0;"
:: [SyscallNum] "i" (SYSCALL_DEF(exit))
, [Result] "r" (Status)
: "memory", "x0", "x8");
#else
__asm volatile(
"mov %[Result], %%edi;"
"syscall;"
:: "a" (SYSCALL_DEF(exit))
, [Result] "r" (Status)
: "memory", "rdi");
#endif
FEX_UNREACHABLE;
}
namespace PThreads {
void *InitializeThread(void *Ptr);
@@ -154,7 +200,7 @@ namespace FEX::LinuxEmulation::Threads {
auto ClearStackPool = [&](auto &StackPool) {
for (auto it = StackPool.begin(); it != StackPool.end(); ) {
StackPoolItem &Item = *it;
auto &Item = *it;
uintptr_t ItemStack = reinterpret_cast<uintptr_t>(Item.Ptr);
if (ItemStack <= StackLocation && (ItemStack + Item.Size) > StackLocation) {
// This is our stack item, skip it
@@ -20,8 +20,10 @@ namespace FEX::LinuxEmulation::Threads {
* Will not free the memory immediately, instead saving for reuse temporarily to solve race conditions on stack usage while stack tears down.
*
* @param Ptr The stack base from `AllocateStackObject`
* @param Status The status to pass to the exit syscall.
*/
void DeallocateStackObject(void *Ptr);
[[noreturn]]
void DeallocateStackObjectAndExit(void *Ptr, int Status);
/**
* @brief Registers thread creation handlers with FEXCore.
@@ -253,7 +253,7 @@ namespace FEX::HLE::x32 {
};
void RegisterFD(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X32_PASS(poll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, int timeout) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(poll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, int timeout) -> uint64_t {
uint64_t Result = ::poll(fds, nfds, timeout);
SYSCALL_ERRNO();
});
@@ -292,17 +292,6 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(ppoll_time64, ppoll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, struct timespec *timeout_ts, const uint64_t *sigmask, size_t sigsetsize) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(ppoll),
fds,
nfds,
timeout_ts,
sigmask,
sigsetsize);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(_llseek, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t fd, uint32_t offset_high, uint32_t offset_low, loff_t *result, uint32_t whence) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
@@ -326,17 +315,12 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(chown32, chown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(chown32, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::chown(pathname, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(fchown32, fchown, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::fchown(fd, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(lchown32, lchown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(lchown32, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::lchown(pathname, owner, group);
SYSCALL_ERRNO();
});
@@ -740,16 +724,6 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(timerfd_settime64, timerfd_settime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) -> uint64_t {
uint64_t Result = ::timerfd_settime(fd, flags, new_value, old_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(timerfd_gettime64, timerfd_gettime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, struct itimerspec *curr_value) -> uint64_t {
uint64_t Result = ::timerfd_gettime(fd, curr_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(timerfd_settime, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
int flags,
@@ -896,14 +870,6 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(sendfile64, sendfile, [](FEXCore::Core::CpuStateFrame *Frame, int out_fd, int in_fd, off_t *offset, compat_size_t count) -> uint64_t {
// Linux definition for this is a bit confusing
// Defines offset as compat_loff_t* but loads loff_t worth of data
// count is defined as compat_size_t still
uint64_t Result = ::sendfile(out_fd, in_fd, offset, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pread_64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, uint32_t count, uint32_t offset_low, uint32_t offset_high) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
@@ -40,17 +40,5 @@ namespace FEX::HLE::x32 {
uint64_t Result = ::syscall(SYSCALL_DEF(io_pgetevents), ctx_id, min_nr, nr, events, timeout_ptr, usig);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS(io_pgetevents_time64,
[](FEXCore::Core::CpuStateFrame *Frame,
aio_context_t ctx_id,
long min_nr,
long nr,
struct io_event *events,
struct timespec *timeout,
const struct io_sigset *usig) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_pgetevents), ctx_id, min_nr, nr, events, timeout, usig);
SYSCALL_ERRNO();
});
}
}
@@ -45,16 +45,6 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(mq_timedsend_time64, mq_timedsend, [](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_timedsend), mqdes, msg_ptr, msg_len, msg_prio, abs_timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(mq_timedreceive_time64, mq_timedreceive, [](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio, const struct timespec *abs_timeout) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_timedreceive), mqdes, msg_ptr, msg_len, msg_prio, abs_timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(mq_open, [](FEXCore::Core::CpuStateFrame *Frame, const char *name, int oflag, mode_t mode, compat_ptr<FEX::HLE::x32::mq_attr32> attr) -> uint64_t {
mq_attr HostAttr{};
mq_attr *HostAttr_p{};
@@ -29,10 +29,5 @@ namespace FEX::HLE::x32 {
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS(sched_rr_get_interval_time64, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, struct timespec *tp) -> uint64_t {
uint64_t Result = ::sched_rr_get_interval(pid, tp);
SYSCALL_ERRNO();
});
}
}
@@ -385,11 +385,6 @@ namespace FEX::HLE::x32 {
void RegisterSemaphore(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X32(ipc, _ipc);
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(semtimedop_time64, semtimedop, [](FEXCore::Core::CpuStateFrame *Frame, int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(semtimedop), semid, sops, nsops, timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(semctl, [](FEXCore::Core::CpuStateFrame *Frame, int semid, int semnum, int cmd, semun_32 *semun) -> uint64_t {
uint64_t Result{};
bool IPC64 = cmd & 0x100;
@@ -213,7 +213,7 @@ namespace FEX::HLE::x32 {
REGISTER_SYSCALL_IMPL_X32(pidfd_send_signal, UnimplementedSyscallSafe);
}
REGISTER_SYSCALL_IMPL_X32_PASS(rt_sigqueueinfo, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int sig, compat_ptr<FEXCore::x86::siginfo_t> info) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(rt_sigqueueinfo, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int sig, compat_ptr<FEXCore::x86::siginfo_t> info) -> uint64_t {
siginfo_t info64{};
siginfo_t *info64_p{};
@@ -225,7 +225,7 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS(rt_tgsigqueueinfo, [](FEXCore::Core::CpuStateFrame *Frame, pid_t tgid, pid_t tid, int sig, compat_ptr<FEXCore::x86::siginfo_t> info) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(rt_tgsigqueueinfo, [](FEXCore::Core::CpuStateFrame *Frame, pid_t tgid, pid_t tid, int sig, compat_ptr<FEXCore::x86::siginfo_t> info) -> uint64_t {
siginfo_t info64{};
siginfo_t *info64_p{};
@@ -40,6 +40,7 @@ namespace FEX::HLE::x32 {
void RegisterThread(FEX::HLE::SyscallHandler *Handler);
void RegisterTime(FEX::HLE::SyscallHandler *Handler);
void RegisterTimer(FEX::HLE::SyscallHandler *Handler);
void RegisterPassthrough(FEX::HLE::SyscallHandler *Handler);
x32SyscallHandler::x32SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation, fextl::unique_ptr<MemAllocator> Allocator)
: SyscallHandler{ctx, _SignalDelegation}, AllocHandler{std::move(Allocator)} {
@@ -67,18 +68,9 @@ namespace FEX::HLE::x32 {
FEX::HLE::RegisterFS(this);
FEX::HLE::RegisterInfo(this);
FEX::HLE::RegisterIO(this);
FEX::HLE::RegisterIOUring(this);
FEX::HLE::RegisterKey(this);
FEX::HLE::RegisterMemory(this);
FEX::HLE::RegisterMsg(this);
FEX::HLE::RegisterNamespace(this);
FEX::HLE::RegisterSched(this);
FEX::HLE::RegisterSemaphore(this);
FEX::HLE::RegisterSHM(this);
FEX::HLE::RegisterSignals(this);
FEX::HLE::RegisterSocket(this);
FEX::HLE::RegisterThread(this);
FEX::HLE::RegisterTime(this);
FEX::HLE::RegisterTimer(this);
FEX::HLE::RegisterNotImplemented(this);
FEX::HLE::RegisterStubs(this);
@@ -100,6 +92,7 @@ namespace FEX::HLE::x32 {
FEX::HLE::x32::RegisterThread(this);
FEX::HLE::x32::RegisterTime(this);
FEX::HLE::x32::RegisterTimer(this);
FEX::HLE::x32::RegisterPassthrough(this);
FEX::HLE::x32::InitializeStaticIoctlHandlers();
@@ -214,86 +214,6 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(getgroups32, getgroups, [](FEXCore::Core::CpuStateFrame *Frame, int size, gid_t list[]) -> uint64_t {
uint64_t Result = ::getgroups(size, list);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setgroups32, setgroups, [](FEXCore::Core::CpuStateFrame *Frame, size_t size, const gid_t *list) -> uint64_t {
uint64_t Result = ::setgroups(size, list);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(getuid32, getuid, [](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getuid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(getgid32, getgid, [](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getgid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setuid32, setuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t uid) -> uint64_t {
uint64_t Result = ::setuid(uid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setgid32, setgid, [](FEXCore::Core::CpuStateFrame *Frame, gid_t gid) -> uint64_t {
uint64_t Result = ::setgid(gid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(geteuid32, geteuid, [](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::geteuid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(getegid32, getegid, [](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::getegid();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setfsuid32, setfsuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsuid) -> uint64_t {
uint64_t Result = ::setfsuid(fsuid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setfsgid32, setfsgid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsgid) -> uint64_t {
uint64_t Result = ::setfsgid(fsgid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setreuid32, setreuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t ruid, uid_t euid) -> uint64_t {
uint64_t Result = ::setreuid(ruid, euid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setresuid32, setresuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t ruid, uid_t euid, uid_t suid) -> uint64_t {
uint64_t Result = ::setresuid(ruid, euid, suid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(getresuid32, getresuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t *ruid, uid_t *euid, uid_t *suid) -> uint64_t {
uint64_t Result = ::getresuid(ruid, euid, suid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setresgid32, setresgid, [](FEXCore::Core::CpuStateFrame *Frame, gid_t rgid, gid_t egid, gid_t sgid) -> uint64_t {
uint64_t Result = ::setresgid(rgid, egid, sgid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(getresgid32, getresgid, [](FEXCore::Core::CpuStateFrame *Frame, gid_t *rgid, gid_t *egid, gid_t *sgid) -> uint64_t {
uint64_t Result = ::getresgid(rgid, egid, sgid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(setregid32, setregid, [](FEXCore::Core::CpuStateFrame *Frame, gid_t rgid, gid_t egid) -> uint64_t {
uint64_t Result = ::setregid(rgid, egid);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(sigaltstack, [](FEXCore::Core::CpuStateFrame *Frame, const compat_ptr<stack_t32> ss, compat_ptr<stack_t32> old_ss) -> uint64_t {
stack_t ss64{};
stack_t old64{};
@@ -421,16 +341,5 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(futex_time64, futex, [](FEXCore::Core::CpuStateFrame *Frame, int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, uint32_t val3) -> uint64_t {
uint64_t Result = syscall(SYSCALL_DEF(futex),
uaddr,
futex_op,
val,
timeout,
uaddr2,
val3);
SYSCALL_ERRNO();
});
}
}
@@ -197,36 +197,6 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(clock_gettime64, clock_gettime, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clk_id, timespec *tp) -> uint64_t {
uint64_t Result = ::clock_gettime(clk_id, tp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(clock_adjtime64, clock_adjtime, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clk_id, struct timex *buf) -> uint64_t {
uint64_t Result = ::clock_adjtime(clk_id, buf);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(clock_settime64, clock_settime, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, const struct timespec *tp) -> uint64_t {
uint64_t Result = ::clock_settime(clockid, tp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(clock_getres_time64, clock_getres, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clk_id, timespec *tp) -> uint64_t {
uint64_t Result = ::clock_getres(clk_id, tp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(clock_nanosleep_time64, clock_nanosleep, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, int flags, const struct timespec *request, struct timespec *remain) -> uint64_t {
uint64_t Result = ::clock_nanosleep(clockid, flags, request, remain);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(utimensat_time64, utimensat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, const struct timespec times[2], int flags) -> uint64_t {
uint64_t Result = ::utimensat(dirfd, pathname, times, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(utimes, [](FEXCore::Core::CpuStateFrame *Frame, const char *filename, const timeval32 times[2]) -> uint64_t {
uint64_t Result = 0;
if (times) {
@@ -54,16 +54,6 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(timer_settime64, timer_settime, [](FEXCore::Core::CpuStateFrame *Frame, kernel_timer_t timerid, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(timer_settime), timerid, flags, new_value, old_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(timer_gettime64, timer_gettime, [](FEXCore::Core::CpuStateFrame *Frame, kernel_timer_t timerid, struct itimerspec *curr_value) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(timer_gettime), timerid, curr_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(getitimer, [](FEXCore::Core::CpuStateFrame *Frame, int which, FEX::HLE::x32::itimerval32 *curr_value) -> uint64_t {
itimerval val{};
itimerval *val_p{};
@@ -29,12 +29,12 @@ $end_info$
namespace FEX::HLE::x64 {
void RegisterFD(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(poll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, int timeout) -> uint64_t {
REGISTER_SYSCALL_IMPL_X64(poll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, int timeout) -> uint64_t {
uint64_t Result = ::poll(fds, nfds, timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(select, [](FEXCore::Core::CpuStateFrame *Frame, int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) -> uint64_t {
REGISTER_SYSCALL_IMPL_X64(select, [](FEXCore::Core::CpuStateFrame *Frame, int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) -> uint64_t {
uint64_t Result = ::select(nfds, readfds, writefds, exceptfds, timeout);
SYSCALL_ERRNO();
});
@@ -58,21 +58,11 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(futimesat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, const struct timeval times[2]) -> uint64_t {
REGISTER_SYSCALL_IMPL_X64(futimesat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, const struct timeval times[2]) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(futimesat), dirfd, pathname, times);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(utimensat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, const struct timespec times[2], int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(utimensat), dirfd, pathname, times, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(pselect6, [](FEXCore::Core::CpuStateFrame *Frame, int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const void *sigmaskpack) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(pselect6), nfds, readfds, writefds, exceptfds, timeout, sigmaskpack);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64(stat, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, FEX::HLE::x64::guest_stat *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Stat(pathname, &host_stat);
@@ -100,21 +90,6 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(readv, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec *iov, int iovcnt) -> uint64_t {
uint64_t Result = ::readv(fd, iov, iovcnt);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(writev, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec *iov, int iovcnt) -> uint64_t {
uint64_t Result = ::writev(fd, iov, iovcnt);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(readahead, [](FEXCore::Core::CpuStateFrame *Frame, int fd, off64_t offset, size_t count) -> uint64_t {
uint64_t Result = ::readahead(fd, offset, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64(newfstatat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, FEX::HLE::x64::guest_stat *buf, int flag) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.NewFSStatAt(dirfd, pathname, &host_stat, flag);
@@ -124,102 +99,11 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(vmsplice, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec *iov, unsigned long nr_segs, unsigned int flags) -> uint64_t {
uint64_t Result = ::vmsplice(fd, iov, nr_segs, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(preadv, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec *iov,
uint64_t vlen,
uint64_t pos_l,
uint64_t pos_h) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(preadv), fd, iov, vlen, pos_l, pos_h);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(pwritev, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec *iov,
uint64_t vlen,
uint64_t pos_l,
uint64_t pos_h) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(pwritev), fd, iov, vlen, pos_l, pos_h);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(preadv2, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec *iov,
uint64_t vlen,
uint64_t pos_l,
uint64_t pos_h,
int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(preadv2), fd, iov, vlen, pos_l, pos_h, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(pwritev2, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec *iov,
uint64_t vlen,
uint64_t pos_l,
uint64_t pos_h,
int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(pwritev2), fd, iov, vlen, pos_l, pos_h, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(pread_64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, size_t count, off_t offset) -> uint64_t {
uint64_t Result = ::pread64(fd, buf, count, offset);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(pwrite_64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, size_t count, off_t offset) -> uint64_t {
uint64_t Result = ::pwrite64(fd, buf, count, offset);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(process_vm_readv, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
uint64_t Result = ::process_vm_readv(pid, local_iov, liovcnt, remote_iov, riovcnt, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(process_vm_writev, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
uint64_t Result = ::process_vm_writev(pid, local_iov, liovcnt, remote_iov, riovcnt, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(ppoll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, struct timespec *timeout_ts, const uint64_t *sigmask, size_t sigsetsize) -> uint64_t {
// glibc wrapper doesn't allow timeout_ts to be modified like the kernel does
int Result = ::syscall(SYSCALL_DEF(ppoll), fds, nfds, timeout_ts, sigmask, sigsetsize);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
return GetDentsEmulation<false>(fd, reinterpret_cast<FEX::HLE::x64::linux_dirent*>(dirp), count);
});
REGISTER_SYSCALL_IMPL_X64_PASS(getdents64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
uint64_t Result = syscall(SYSCALL_DEF(getdents64),
static_cast<uint64_t>(fd),
reinterpret_cast<uint64_t>(dirp),
static_cast<uint64_t>(count));
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(sendfile, [](FEXCore::Core::CpuStateFrame *Frame, int out_fd, int in_fd, off_t *offset, size_t count) -> uint64_t {
uint64_t Result = ::sendfile(out_fd, in_fd, offset, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(dup, [](FEXCore::Core::CpuStateFrame *Frame, int oldfd) -> uint64_t {
uint64_t Result = ::dup(oldfd);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(dup2, [](FEXCore::Core::CpuStateFrame *Frame, int oldfd, int newfd) -> uint64_t {
REGISTER_SYSCALL_IMPL_X64(dup2, [](FEXCore::Core::CpuStateFrame *Frame, int oldfd, int newfd) -> uint64_t {
uint64_t Result = ::dup2(oldfd, newfd);
SYSCALL_ERRNO();
});
@@ -228,32 +112,5 @@ namespace FEX::HLE::x64 {
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statfs(path, buf);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(fstatfs, [](FEXCore::Core::CpuStateFrame *Frame, int fd, struct statfs *buf) -> uint64_t {
uint64_t Result = ::fstatfs(fd, buf);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(sync_file_range, [](FEXCore::Core::CpuStateFrame *Frame, int fd, off64_t offset, off64_t nbytes, unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::syscall(SYSCALL_DEF(sync_file_range), fd, offset, nbytes, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(fallocate, [](FEXCore::Core::CpuStateFrame *Frame, int fd, int mode, off_t offset, off_t len) -> uint64_t {
uint64_t Result = ::fallocate(fd, mode, offset, len);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(timerfd_settime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) -> uint64_t {
// Flags don't need remapped
uint64_t Result = ::timerfd_settime(fd, flags, new_value, old_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(timerfd_gettime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, struct itimerspec *curr_value) -> uint64_t {
uint64_t Result = ::timerfd_gettime(fd, curr_value);
SYSCALL_ERRNO();
});
}
}
@@ -1,32 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-x86-64
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include <linux/aio_abi.h>
#include <stdint.h>
#include <syscall.h>
#include <unistd.h>
namespace FEXCore::Core {
struct CpuStateFrame;
}
namespace FEX::HLE::x64 {
void RegisterIO(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(io_getevents, [](FEXCore::Core::CpuStateFrame *Frame, aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_getevents), ctx_id, min_nr, nr, events, timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(io_pgetevents, [](FEXCore::Core::CpuStateFrame *Frame, aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout, const struct io_sigset *usig) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(io_pgetevents), ctx_id, min_nr, nr, events, timeout, usig);
SYSCALL_ERRNO();
});
}
}
@@ -18,28 +18,8 @@ namespace FEX::HLE::x64 {
void RegisterInfo(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_X64_PASS(sysinfo, [](FEXCore::Core::CpuStateFrame *Frame, struct sysinfo *info) -> uint64_t {
uint64_t Result = ::sysinfo(info);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(getrusage, [](FEXCore::Core::CpuStateFrame *Frame, int who, struct rusage *usage) -> uint64_t {
uint64_t Result = ::getrusage(who, usage);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(getrlimit, [](FEXCore::Core::CpuStateFrame *Frame, int resource, struct rlimit *rlim) -> uint64_t {
uint64_t Result = ::getrlimit(resource, rlim);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(setrlimit, [](FEXCore::Core::CpuStateFrame *Frame, int resource, const struct rlimit *rlim) -> uint64_t {
uint64_t Result = ::setrlimit(resource, rlim);
SYSCALL_ERRNO();
});
if (Handler->IsHostKernelVersionAtLeast(6, 6, 0)) {
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(map_shadow_stack, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_X64_FLAGS(map_shadow_stack, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, uint64_t addr, uint64_t size, uint32_t flags) -> uint64_t {
// Claim that shadow stack isn't supported.
return -EOPNOTSUPP;
@@ -1,25 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-x86-64
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include <stdint.h>
#include <sys/ioctl.h>
namespace FEXCore::Core {
struct CpuStateFrame;
}
namespace FEX::HLE::x64 {
void RegisterIoctl(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(ioctl, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uint64_t request, void *args) -> uint64_t {
uint64_t Result = ::ioctl(fd, request, args);
SYSCALL_ERRNO();
});
}
}
@@ -103,18 +103,6 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mlockall, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
uint64_t Result = ::mlockall(flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(munlockall, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame) -> uint64_t {
uint64_t Result = ::munlockall();
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_FLAGS(_shmat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
uint64_t Result = reinterpret_cast<uint64_t>(shmat(shmid, shmaddr, shmflg));
@@ -134,16 +122,5 @@ namespace FEX::HLE::x64 {
}
SYSCALL_ERRNO();
});
if (Handler->IsHostKernelVersionAtLeast(5, 10, 0)) {
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(process_madvise, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pidfd, const struct iovec *iovec, size_t vlen, int advice, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(process_madvise), pidfd, iovec, vlen, advice, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL_X64(process_madvise, UnimplementedSyscallSafe);
}
}
}
@@ -1,48 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-x86-64
$end_info$
*/
#include "LinuxSyscalls/Types.h"
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include <stddef.h>
#include <stdint.h>
#include <syscall.h>
#include <unistd.h>
namespace FEXCore::Core {
struct CpuStateFrame;
}
namespace FEX::HLE::x64 {
void RegisterMsg(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(mq_timedsend, [](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_timedsend), mqdes, msg_ptr, msg_len, msg_prio, abs_timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(mq_timedreceive, [](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio, const struct timespec *abs_timeout) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_timedreceive), mqdes, msg_ptr, msg_len, msg_prio, abs_timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(mq_open, [](FEXCore::Core::CpuStateFrame *Frame, const char *name, int oflag, mode_t mode, struct mq_attr *attr) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_open), name, oflag, mode, attr);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(mq_notify, [](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::mqd_t mqdes, const struct sigevent *sevp) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_notify), mqdes, sevp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(mq_getsetattr, [](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(mq_getsetattr), mqdes, newattr, oldattr);
SYSCALL_ERRNO();
});
}
}
@@ -1,26 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-x86-64
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include <stdint.h>
#include <sched.h>
#include <unistd.h>
namespace FEXCore::Core {
struct CpuStateFrame;
}
namespace FEX::HLE::x64 {
void RegisterSched(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(sched_rr_get_interval, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, struct timespec *tp) -> uint64_t {
uint64_t Result = ::sched_rr_get_interval(pid, tp);
SYSCALL_ERRNO();
});
}
}
@@ -23,16 +23,6 @@ ARG_TO_STR(FEX::HLE::x64::semun, "%lx")
namespace FEX::HLE::x64 {
void RegisterSemaphore(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(semop, [](FEXCore::Core::CpuStateFrame *Frame, int semid, struct sembuf *sops, size_t nsops) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(semop), semid, sops, nsops);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(semtimedop, [](FEXCore::Core::CpuStateFrame *Frame, int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(semtimedop), semid, sops, nsops, timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64(semctl, [](FEXCore::Core::CpuStateFrame *Frame, int semid, int semnum, int cmd, FEX::HLE::x64::semun semun) -> uint64_t {
uint64_t Result{};
switch (cmd) {
@@ -31,26 +31,6 @@ namespace FEX::HLE::x64 {
REGISTER_SYSCALL_IMPL_X64(rt_sigtimedwait, [](FEXCore::Core::CpuStateFrame *Frame, uint64_t *set, siginfo_t *info, const struct timespec* timeout, size_t sigsetsize) -> uint64_t {
return FEX::HLE::_SyscallHandler->GetSignalDelegator()->GuestSigTimedWait(set, info, timeout, sigsetsize);
});
if (Handler->IsHostKernelVersionAtLeast(5, 1, 0)) {
REGISTER_SYSCALL_IMPL_X64_PASS(pidfd_send_signal, [](FEXCore::Core::CpuStateFrame *Frame, int pidfd, int sig, siginfo_t *info, unsigned int flags) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(pidfd_send_signal), pidfd, sig, info, flags);
SYSCALL_ERRNO();
});
}
else {
REGISTER_SYSCALL_IMPL_X64(pidfd_send_signal, UnimplementedSyscallSafe);
}
REGISTER_SYSCALL_IMPL_X64_PASS(rt_sigqueueinfo, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int sig, siginfo_t *info) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(rt_sigqueueinfo), pid, sig, info);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(rt_tgsigqueueinfo, [](FEXCore::Core::CpuStateFrame *Frame, pid_t tgid, pid_t tid, int sig, siginfo_t *info) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(rt_tgsigqueueinfo), tgid, tid, sig, info);
SYSCALL_ERRNO();
});
}
}
@@ -1,55 +0,0 @@
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-x86-64
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include <stdint.h>
#include <sys/socket.h>
namespace FEXCore::Core {
struct CpuStateFrame;
}
namespace FEX::HLE::x64 {
void RegisterSocket(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(accept, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct sockaddr *addr, socklen_t *addrlen) -> uint64_t {
uint64_t Result = ::accept(sockfd, addr, addrlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(recvmmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) -> uint64_t {
uint64_t Result = ::recvmmsg(sockfd, msgvec, vlen, flags, timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(sendmmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct mmsghdr *msgvec, uint32_t vlen, int flags) -> uint64_t {
uint64_t Result = ::sendmmsg(sockfd, msgvec, vlen, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(sendmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, const struct msghdr *msg, int flags) -> uint64_t {
uint64_t Result = ::sendmsg(sockfd, msg, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(recvmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct msghdr *msg, int flags) -> uint64_t {
uint64_t Result = ::recvmsg(sockfd, msg, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(setsockopt, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, int level, int optname, const void *optval, socklen_t optlen) -> uint64_t {
uint64_t Result = ::setsockopt(sockfd, level, optname, optval, optlen);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(getsockopt, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, int level, int optname, void *optval, socklen_t *optlen) -> uint64_t {
uint64_t Result = ::getsockopt(sockfd, level, optname, optval, optlen);
SYSCALL_ERRNO();
});
}
}
@@ -15,17 +15,13 @@ namespace FEX::HLE::x64 {
void RegisterEpoll(FEX::HLE::SyscallHandler *Handler);
void RegisterFD(FEX::HLE::SyscallHandler *Handler);
void RegisterInfo(FEX::HLE::SyscallHandler *Handler);
void RegisterIO(FEX::HLE::SyscallHandler *Handler);
void RegisterIoctl(FEX::HLE::SyscallHandler *Handler);
void RegisterMemory(FEX::HLE::SyscallHandler *Handler);
void RegisterMsg(FEX::HLE::SyscallHandler *Handler);
void RegisterSched(FEX::HLE::SyscallHandler *Handler);
void RegisterSocket(FEX::HLE::SyscallHandler *Handler);
void RegisterSemaphore(FEX::HLE::SyscallHandler *Handler);
void RegisterSignals(FEX::HLE::SyscallHandler *Handler);
void RegisterThread(FEX::HLE::SyscallHandler *Handler);
void RegisterTime(FEX::HLE::SyscallHandler *Handler);
void RegisterNotImplemented(FEX::HLE::SyscallHandler *Handler);
void RegisterPassthrough(FEX::HLE::SyscallHandler *Handler);
x64SyscallHandler::x64SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation)
: SyscallHandler {ctx, _SignalDelegation} {
@@ -54,18 +50,9 @@ namespace FEX::HLE::x64 {
FEX::HLE::RegisterFS(this);
FEX::HLE::RegisterInfo(this);
FEX::HLE::RegisterIO(this);
FEX::HLE::RegisterIOUring(this);
FEX::HLE::RegisterKey(this);
FEX::HLE::RegisterMemory(this);
FEX::HLE::RegisterMsg(this);
FEX::HLE::RegisterNamespace(this);
FEX::HLE::RegisterSched(this);
FEX::HLE::RegisterSemaphore(this);
FEX::HLE::RegisterSHM(this);
FEX::HLE::RegisterSignals(this);
FEX::HLE::RegisterSocket(this);
FEX::HLE::RegisterThread(this);
FEX::HLE::RegisterTime(this);
FEX::HLE::RegisterTimer(this);
FEX::HLE::RegisterNotImplemented(this);
FEX::HLE::RegisterStubs(this);
@@ -74,17 +61,13 @@ namespace FEX::HLE::x64 {
FEX::HLE::x64::RegisterEpoll(this);
FEX::HLE::x64::RegisterFD(this);
FEX::HLE::x64::RegisterInfo(this);
FEX::HLE::x64::RegisterIO(this);
FEX::HLE::x64::RegisterIoctl(this);
FEX::HLE::x64::RegisterMemory(this);
FEX::HLE::x64::RegisterMsg(this);
FEX::HLE::x64::RegisterSched(this);
FEX::HLE::x64::RegisterSocket(this);
FEX::HLE::x64::RegisterSemaphore(this);
FEX::HLE::x64::RegisterSignals(this);
FEX::HLE::x64::RegisterThread(this);
FEX::HLE::x64::RegisterTime(this);
FEX::HLE::x64::RegisterNotImplemented(this);
FEX::HLE::x64::RegisterPassthrough(this);
// x86-64 has a gap of syscalls in the range of [335, 424) where there aren't any
// These are defined that these must return -ENOSYS
@@ -30,8 +30,6 @@ struct InternalThreadState;
}
namespace FEX::HLE::x64 {
#include "SyscallsEnum.h"
class x64SyscallHandler final : public FEX::HLE::SyscallHandler {
public:
x64SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation);
@@ -6,6 +6,7 @@ $end_info$
*/
#pragma once
namespace FEX::HLE::x64 {
///< Enum containing all x86-64 linux syscalls for the guest kernel version
enum Syscalls_x64 {
SYSCALL_x64_read = 0,
@@ -479,4 +480,4 @@ enum Syscalls_x64 {
SYSCALL_x64_futex_time64 = ~0,
SYSCALL_x64_sched_rr_get_interval_time64 = ~0,
};
}
@@ -57,40 +57,6 @@ namespace FEX::HLE::x64 {
return CloneHandler(Frame, &args);
}));
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(futex, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, uint32_t val3) -> uint64_t {
uint64_t Result = syscall(SYSCALL_DEF(futex),
uaddr,
futex_op,
val,
timeout,
uaddr2,
val3);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_FLAGS(set_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, struct robust_list_head *head, size_t len) -> uint64_t {
auto Thread = Frame->Thread;
Thread->ThreadManager.robust_list_head = reinterpret_cast<uint64_t>(head);
#ifdef TERMUX_BUILD
// Termux/Android doesn't support `set_robust_list` syscall.
// The seccomp filter that the OS installs explicitly blocks this syscall from working
// glibc uses this syscall for tls and thread data so almost every application uses it
// Return success since we have stored the pointer ourselves.
return 0;
#else
uint64_t Result = ::syscall(SYSCALL_DEF(set_robust_list), head, len);
SYSCALL_ERRNO();
#endif
});
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(get_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int pid, struct robust_list_head **head, size_t *len_ptr) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(get_robust_list), pid, head, len_ptr);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64(sigaltstack, [](FEXCore::Core::CpuStateFrame *Frame, const stack_t *ss, stack_t *old_ss) -> uint64_t {
return FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterGuestSigAltStack(ss, old_ss);
});
@@ -156,17 +122,5 @@ namespace FEX::HLE::x64 {
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
}));
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(wait4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int *wstatus, int options, struct rusage *rusage) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(wait4), pid, wstatus, options, rusage);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(waitid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int which, pid_t upid, siginfo_t *infop, int options, struct rusage *rusage) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(waitid), which, upid, infop, options, rusage);
SYSCALL_ERRNO();
});
}
}
@@ -21,95 +21,21 @@ $end_info$
namespace FEX::HLE::x64 {
void RegisterTime(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X64_PASS(time, [](FEXCore::Core::CpuStateFrame *Frame, time_t *tloc) -> uint64_t {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_X64_FLAGS(time, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, [](FEXCore::Core::CpuStateFrame *Frame, time_t *tloc) -> uint64_t {
uint64_t Result = ::time(tloc);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(times, [](FEXCore::Core::CpuStateFrame *Frame, struct tms *buf) -> uint64_t {
uint64_t Result = ::times(buf);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(utime, [](FEXCore::Core::CpuStateFrame *Frame, char* filename, const struct utimbuf* times) -> uint64_t {
REGISTER_SYSCALL_IMPL_X64(utime, [](FEXCore::Core::CpuStateFrame *Frame, char* filename, const struct utimbuf* times) -> uint64_t {
uint64_t Result = ::utime(filename, times);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(gettimeofday, [](FEXCore::Core::CpuStateFrame *Frame, struct timeval *tv, struct timezone *tz) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(gettimeofday), tv, tz);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(nanosleep, [](FEXCore::Core::CpuStateFrame *Frame, const struct timespec *req, struct timespec *rem) -> uint64_t {
uint64_t Result = ::nanosleep(req, rem);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(clock_gettime, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clk_id, struct timespec *tp) -> uint64_t {
uint64_t Result = ::clock_gettime(clk_id, tp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(clock_getres, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clk_id, struct timespec *tp) -> uint64_t {
uint64_t Result = ::clock_getres(clk_id, tp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(clock_nanosleep, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, int flags, const struct timespec *request, struct timespec *remain) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(clock_nanosleep), clockid, flags, request, remain);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(clock_settime, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, const struct timespec *tp) -> uint64_t {
uint64_t Result = ::clock_settime(clockid, tp);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(settimeofday, [](FEXCore::Core::CpuStateFrame *Frame, const struct timeval *tv, const struct timezone *tz) -> uint64_t {
uint64_t Result = ::settimeofday(tv, tz);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(utimes, [](FEXCore::Core::CpuStateFrame *Frame, const char *filename, const struct timeval times[2]) -> uint64_t {
REGISTER_SYSCALL_IMPL_X64(utimes, [](FEXCore::Core::CpuStateFrame *Frame, const char *filename, const struct timeval times[2]) -> uint64_t {
uint64_t Result = ::utimes(filename, times);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(getitimer, [](FEXCore::Core::CpuStateFrame *Frame, int which, struct itimerval *curr_value) -> uint64_t {
uint64_t Result = ::getitimer(which, curr_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(setitimer, [](FEXCore::Core::CpuStateFrame *Frame, int which, const struct itimerval *new_value, struct itimerval *old_value) -> uint64_t {
uint64_t Result = ::setitimer(which, new_value, old_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(timer_settime, [](FEXCore::Core::CpuStateFrame *Frame, kernel_timer_t timerid, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(timer_settime), timerid, flags, new_value, old_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(timer_gettime, [](FEXCore::Core::CpuStateFrame *Frame, kernel_timer_t timerid, struct itimerspec *curr_value) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(timer_gettime), timerid, curr_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(adjtimex, [](FEXCore::Core::CpuStateFrame *Frame, struct timex *buf) -> uint64_t {
uint64_t Result = ::adjtimex(buf);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(clock_adjtime, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clk_id, struct timex *buf) -> uint64_t {
uint64_t Result = ::clock_adjtime(clk_id, buf);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_PASS(timer_create, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, struct sigevent *sevp, kernel_timer_t *timerid) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(timer_create), clockid, sevp, timerid);
SYSCALL_ERRNO();
});
}
}
+32 -32
View File
@@ -36,7 +36,7 @@ namespace FEX::VDSO {
using HandlerPtr = void(*)(void*);
namespace x64 {
static uint64_t SyscallRet(int Result) {
static uint64_t SyscallRet(uint64_t Result) {
if (Result == -1) {
return -errno;
}
@@ -45,20 +45,20 @@ namespace FEX::VDSO {
// glibc handlers
namespace glibc {
static void time(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
time_t *a_0;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
int Result = ::time(args->a_0);
uint64_t Result = ::time(args->a_0);
args->rv = SyscallRet(Result);
}
static void gettimeofday(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
struct timeval *tv;
struct timezone *tz;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
int Result = ::gettimeofday(args->tv, args->tz);
@@ -66,10 +66,10 @@ namespace FEX::VDSO {
}
static void clock_gettime(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
int Result = ::clock_gettime(args->clk_id, args->tp);
@@ -77,10 +77,10 @@ namespace FEX::VDSO {
}
static void clock_getres(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
int Result = ::clock_getres(args->clk_id, args->tp);
@@ -88,10 +88,10 @@ namespace FEX::VDSO {
}
static void getcpu(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
uint32_t *cpu;
uint32_t *node;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
int Result = FHU::Syscalls::getcpu(args->cpu, args->node);
@@ -102,7 +102,7 @@ namespace FEX::VDSO {
namespace VDSO {
// VDSO handlers
static void time(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
time_t *a_0;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
@@ -111,40 +111,40 @@ namespace FEX::VDSO {
}
static void gettimeofday(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
struct timeval *tv;
struct timezone *tz;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = VDSOHandlers::GetTimeOfDayPtr(args->tv, args->tz);
}
static void clock_gettime(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = VDSOHandlers::ClockGetTimePtr(args->clk_id, args->tp);
}
static void clock_getres(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = VDSOHandlers::ClockGetResPtr(args->clk_id, args->tp);
}
static void getcpu(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
uint32_t *cpu;
uint32_t *node;
uint64_t rv;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = VDSOHandlers::GetCPUPtr(args->cpu, args->node);
@@ -168,7 +168,7 @@ namespace FEX::VDSO {
// glibc handlers
static void time(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
HLE::x32::compat_ptr<FEX::HLE::x32::old_time32_t> a_0;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
@@ -182,7 +182,7 @@ namespace FEX::VDSO {
}
static void gettimeofday(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
HLE::x32::compat_ptr<FEX::HLE::x32::timeval32> tv;
HLE::x32::compat_ptr<struct timezone> tz;
int rv;
@@ -203,7 +203,7 @@ namespace FEX::VDSO {
}
static void clock_gettime(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
int rv;
@@ -219,7 +219,7 @@ namespace FEX::VDSO {
}
static void clock_gettime64(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<struct timespec> tp;
int rv;
@@ -230,7 +230,7 @@ namespace FEX::VDSO {
}
static void clock_getres(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
int rv;
@@ -247,7 +247,7 @@ namespace FEX::VDSO {
}
static void getcpu(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
HLE::x32::compat_ptr<uint32_t> cpu;
HLE::x32::compat_ptr<uint32_t> node;
int rv;
@@ -265,7 +265,7 @@ namespace FEX::VDSO {
// VDSO handlers
static void time(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
HLE::x32::compat_ptr<FEX::HLE::x32::old_time32_t> a_0;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
@@ -279,7 +279,7 @@ namespace FEX::VDSO {
}
static void gettimeofday(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
HLE::x32::compat_ptr<FEX::HLE::x32::timeval32> tv;
HLE::x32::compat_ptr<struct timezone> tz;
int rv;
@@ -300,7 +300,7 @@ namespace FEX::VDSO {
}
static void clock_gettime(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
int rv;
@@ -316,7 +316,7 @@ namespace FEX::VDSO {
}
static void clock_gettime64(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<struct timespec> tp;
int rv;
@@ -326,7 +326,7 @@ namespace FEX::VDSO {
}
static void clock_getres(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
int rv;
@@ -343,7 +343,7 @@ namespace FEX::VDSO {
}
static void getcpu(void* ArgsRV) {
struct ArgsRV_t {
struct __attribute__((packed)) ArgsRV_t {
HLE::x32::compat_ptr<uint32_t> cpu;
HLE::x32::compat_ptr<uint32_t> node;
int rv;
@@ -309,6 +309,7 @@ int main(int argc, char **argv, char **const envp) {
return 1;
}
auto ParentThread = CTX->CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer());
SignalDelegation->RegisterTLSState(ParentThread);
if (!ParentThread) {
return 1;
@@ -324,6 +325,7 @@ int main(int argc, char **argv, char **const envp) {
SyscallHandler.reset();
SignalDelegation->UninstallTLSState(ParentThread);
CTX->DestroyThread(ParentThread, true);
}
#ifndef _WIN32
+7
View File
@@ -366,6 +366,13 @@ void AnalysisAction::ParseInterface(clang::ASTContext& context) {
continue;
}
// Skip pointers-to-structs passed through to the host in guest_layout.
// This avoids pulling in member types that can't be processed.
if (data.param_annotations[param_idx].is_passthrough &&
param_type->isPointerType() && param_type->getPointeeType()->isStructureType()) {
continue;
}
auto check_struct_type = [&](const clang::Type* type) {
if (type->isIncompleteType()) {
throw report_error(type->getAsTagDecl()->getBeginLoc(), "Unannotated pointer with incomplete struct type; consider using an opaque_type annotation")
+11 -10
View File
@@ -164,7 +164,7 @@ void GenerateThunkLibsAction::EmitLayoutWrappers(
}
if (type->isEnumeralType()) {
fmt::print(file, "template<>\nstruct guest_layout<{}> {{\n", struct_name);
fmt::print(file, "template<>\nstruct __attribute__((packed)) guest_layout<{}> {{\n", struct_name);
fmt::print(file, " using type = {}int{}_t;\n",
type->isUnsignedIntegerOrEnumerationType() ? "u" : "",
guest_abi.at(struct_name).get_if_simple_or_struct()->size_bits);
@@ -182,12 +182,13 @@ void GenerateThunkLibsAction::EmitLayoutWrappers(
}
// Guest layout definition
fmt::print(file, "template<>\nstruct guest_layout<{}> {{\n", struct_name);
// NOTE: uint64_t has lower alignment requirements on 32-bit than on 64-bit, so we require tightly packed structs
// TODO: Now we must emit padding bytes explicitly, though!
fmt::print(file, "template<>\nstruct __attribute__((packed)) guest_layout<{}> {{\n", struct_name);
if (type_compat.at(type) == TypeCompatibility::Full) {
fmt::print(file, " using type = {};\n", struct_name);
} else {
fmt::print(file, " struct type {{\n");
// TODO: Insert any required padding bytes
for (auto& member : guest_abi.at(struct_name).get_if_struct()->members) {
fmt::print( file, " guest_layout<{}{}> {};\n",
member.type_name,
@@ -454,7 +455,7 @@ void GenerateThunkLibsAction::OnAnalysisComplete(clang::ASTContext& context) {
}
// Using trailing return type as it makes handling function pointer returns much easier
file << ") -> " << data.return_type.getAsString() << " {\n";
file << " struct {\n";
file << " struct __attribute__((packed)) {\n";
for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) {
auto& type = data.param_types[idx];
file << " " << format_decl(type.getUnqualifiedType(), fmt::format("a_{}", idx)) << ";\n";
@@ -587,11 +588,11 @@ void GenerateThunkLibsAction::OnAnalysisComplete(clang::ASTContext& context) {
if (!param_type->isPointerType() || !param_type->getPointeeType()->isStructureType()) {
continue;
}
auto type = param_type->getPointeeType();
if (!types.at(context.getCanonicalType(type.getTypePtr())).assumed_compatible && type_compat.at(context.getCanonicalType(type.getTypePtr())) == TypeCompatibility::None) {
// TODO: Factor in "assume_compatible_layout" annotations here
// That annotation should cause the type to be treated as TypeCompatibility::Full
if (!thunk.param_annotations[param_idx].is_passthrough) {
if (!thunk.param_annotations[param_idx].is_passthrough) {
auto type = param_type->getPointeeType();
if (!types.at(context.getCanonicalType(type.getTypePtr())).assumed_compatible && type_compat.at(context.getCanonicalType(type.getTypePtr())) == TypeCompatibility::None) {
// TODO: Factor in "assume_compatible_layout" annotations here
// That annotation should cause the type to be treated as TypeCompatibility::Full
throw report_error(thunk.decl->getLocation(), "Unsupported parameter type %0").AddTaggedVal(param_type);
}
}
@@ -600,7 +601,7 @@ void GenerateThunkLibsAction::OnAnalysisComplete(clang::ASTContext& context) {
// Packed argument structs used in fexfn_unpack_*
auto GeneratePackedArgs = [&](const auto &function_name, const ThunkedFunction &thunk) -> std::string {
std::string struct_name = "fexfn_packed_args_" + libname + "_" + function_name;
file << "struct " << struct_name << " {\n";
file << "struct __attribute__((packed)) " << struct_name << " {\n";
for (std::size_t idx = 0; idx < thunk.param_types.size(); ++idx) {
fmt::print(file, " guest_layout<{}> a_{};\n", get_guest_type_name(thunk.param_types[idx]), idx);
+3 -3
View File
@@ -232,9 +232,6 @@ if (BITNESS EQUAL 64)
target_compile_definitions(libxcb-guest-deps INTERFACE -DXCB_VERSION_MINOR=${XCB_VERSION_MINOR})
target_compile_definitions(libxcb-guest-deps INTERFACE -DXCB_VERSION_PATCH=${XCB_VERSION_PATCH})
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp)
add_guest_lib(wayland-client "libwayland-client.so.0.20.0")
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
add_guest_lib(xcb-dri2 "libxcb-dri2.so.0")
@@ -268,6 +265,9 @@ if (BITNESS EQUAL 64)
add_guest_lib(drm "libdrm.so.2")
endif()
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp)
add_guest_lib(wayland-client "libwayland-client.so.0.20.0")
generate(libVDSO ${CMAKE_CURRENT_SOURCE_DIR}/../libVDSO/libVDSO_interface.cpp)
add_guest_lib(VDSO "linux-vdso.so.1")
# Can't use a stack protector because otherwise cross-compiling fails
+3 -3
View File
@@ -163,9 +163,6 @@ foreach(GUEST_BITNESS IN LISTS BITNESS_LIST)
target_compile_definitions(libxcb-${GUEST_BITNESS}-deps INTERFACE -DXCB_VERSION_MINOR=${XCB_VERSION_MINOR})
target_compile_definitions(libxcb-${GUEST_BITNESS}-deps INTERFACE -DXCB_VERSION_PATCH=${XCB_VERSION_PATCH})
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp ${GUEST_BITNESS})
add_host_lib(wayland-client ${GUEST_BITNESS})
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp ${GUEST_BITNESS})
add_host_lib(xcb-dri2 ${GUEST_BITNESS})
@@ -203,6 +200,9 @@ set (BITNESS_LIST "32;64")
foreach(GUEST_BITNESS IN LISTS BITNESS_LIST)
generate(libfex_thunk_test ${CMAKE_CURRENT_SOURCE_DIR}/../libfex_thunk_test/libfex_thunk_test_interface.cpp ${GUEST_BITNESS})
add_host_lib(fex_thunk_test ${GUEST_BITNESS})
generate(libwayland-client ${CMAKE_CURRENT_SOURCE_DIR}/../libwayland-client/libwayland-client_interface.cpp ${GUEST_BITNESS})
add_host_lib(wayland-client ${GUEST_BITNESS})
endforeach()
add_library(fex_thunk_test SHARED ../libfex_thunk_test/lib.cpp)
+52 -1
View File
@@ -30,6 +30,12 @@ namespace FEXCore {
__attribute__((weak))
HostToGuestTrampolinePtr*
FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr*, void* HostPacker);
__attribute__((weak))
void* GetGuestStack();
__attribute__((weak))
void MoveGuestStack(uintptr_t NewAddress);
}
template<typename Fn>
@@ -121,7 +127,7 @@ template<> inline constexpr bool has_compatible_data_layout<const void**> = true
// Placeholder type to indicate the given data is in guest-layout
template<typename T>
struct guest_layout {
struct __attribute__((packed)) guest_layout {
static_assert(!std::is_class_v<T>, "No guest layout defined for this non-opaque struct type. This may be a bug in the thunk generator.");
static_assert(!std::is_union_v<T>, "No guest layout defined for this non-opaque union type. This may be a bug in the thunk generator.");
static_assert(!std::is_enum_v<T>, "No guest layout defined for this enum type. This is a bug in the thunk generator.");
@@ -161,6 +167,14 @@ struct guest_layout<T*> {
const guest_layout<T>* get_pointer() const {
return reinterpret_cast<const guest_layout<T>*>(uintptr_t { data });
}
T* force_get_host_pointer() {
return reinterpret_cast<T*>(uintptr_t { data });
}
const T* force_get_host_pointer() const {
return reinterpret_cast<const T*>(uintptr_t { data });
}
};
template<typename T>
@@ -447,16 +461,53 @@ auto Projection(guest_layout<T>& data) {
}
}
#ifdef IS_32BIT_THUNK
/**
* Helper class to manage guest stack memory from a host function.
*
* The current guest stack position is saved upon construction and bumped
* for each object construction. Upon destruction, the old guest stack is
* restored.
*/
class GuestStackBumpAllocator final {
uintptr_t Top = reinterpret_cast<uintptr_t>(FEXCore::GetGuestStack());
uintptr_t Next = Top;
public:
~GuestStackBumpAllocator() {
FEXCore::MoveGuestStack(Top);
}
template<typename T, typename... Args>
T* New(Args&&... args) {
Next -= sizeof(T);
Next &= ~uintptr_t { alignof(T) - 1 };
FEXCore::MoveGuestStack(Next);
return new (reinterpret_cast<void*>(Next)) T {
std::forward<Args>(args)...
};
}
};
#endif
template<typename Result, typename... Args>
struct CallbackUnpack<Result(Args...)> {
static Result CallGuestPtr(Args... args) {
GuestcallInfo *guestcall;
LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(guestcall);
#ifndef IS_32BIT_THUNK
PackedArguments<Result, guest_layout<Args>...> packed_args = {
to_guest(to_host_layout(args))...
};
#else
GuestStackBumpAllocator GuestStack;
auto& packed_args = *GuestStack.New<PackedArguments<Result, guest_layout<Args>...>>(
to_guest(to_host_layout(args))...
);
#endif
guestcall->CallCallback(guestcall->GuestUnpacker, guestcall->GuestTarget, &packed_args);
if constexpr (!std::is_void_v<Result>) {
return packed_args.rv;
}
+39 -39
View File
@@ -5,45 +5,45 @@
#include <utility>
template<typename Result, typename... Args>
struct PackedArguments;
struct __attribute__((packed)) PackedArguments;
template<typename R>
struct PackedArguments<R> { R rv; };
struct __attribute__((packed)) PackedArguments<R> { R rv; };
template<typename R, typename A0>
struct PackedArguments<R, A0> { A0 a0; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0> { A0 a0; R rv; };
template<typename R, typename A0, typename A1>
struct PackedArguments<R, A0, A1> { A0 a0; A1 a1; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1> { A0 a0; A1 a1; R rv; };
template<typename R, typename A0, typename A1, typename A2>
struct PackedArguments<R, A0, A1, A2> { A0 a0; A1 a1; A2 a2; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2> { A0 a0; A1 a1; A2 a2; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3>
struct PackedArguments<R, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4>
struct PackedArguments<R, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
typename A20, typename A21, typename A22>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
A20, A21, A22> {
A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9;
@@ -54,7 +54,7 @@ struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
typename A20, typename A21, typename A22, typename A23>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
A20, A21, A22, A23> {
A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9;
@@ -63,45 +63,45 @@ struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
};
template<>
struct PackedArguments<void> { };
struct __attribute__((packed)) PackedArguments<void> { };
template<typename A0>
struct PackedArguments<void, A0> { A0 a0; };
struct __attribute__((packed)) PackedArguments<void, A0> { A0 a0; };
template<typename A0, typename A1>
struct PackedArguments<void, A0, A1> { A0 a0; A1 a1; };
struct __attribute__((packed)) PackedArguments<void, A0, A1> { A0 a0; A1 a1; };
template<typename A0, typename A1, typename A2>
struct PackedArguments<void, A0, A1, A2> { A0 a0; A1 a1; A2 a2; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2> { A0 a0; A1 a1; A2 a2; };
template<typename A0, typename A1, typename A2, typename A3>
struct PackedArguments<void, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; };
template<typename A0, typename A1, typename A2, typename A3, typename A4>
struct PackedArguments<void, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; A18 a18; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; A18 a18; };
// Helper struct that allows assigning the result of a function to a variable, even if that result is a void type.
//
+26 -1
View File
@@ -400,6 +400,8 @@ template<> struct fex_gen_config<vkWaitSemaphoresKHR> {};
template<> struct fex_gen_config<vkSignalSemaphoreKHR> {};
template<> struct fex_gen_config<vkGetPhysicalDeviceFragmentShadingRatesKHR> {};
template<> struct fex_gen_config<vkCmdSetFragmentShadingRateKHR> {};
template<> struct fex_gen_config<vkCmdSetRenderingAttachmentLocationsKHR> {};
template<> struct fex_gen_config<vkCmdSetRenderingInputAttachmentIndicesKHR> {};
template<> struct fex_gen_config<vkWaitForPresentKHR> {};
template<> struct fex_gen_config<vkGetBufferDeviceAddressKHR> {};
template<> struct fex_gen_config<vkGetBufferOpaqueCaptureAddressKHR> {};
@@ -414,6 +416,9 @@ template<> struct fex_gen_config<vkGetPipelineExecutableStatisticsKHR> {};
template<> struct fex_gen_config<vkGetPipelineExecutableInternalRepresentationsKHR> {};
template<> struct fex_gen_config<vkMapMemory2KHR> {};
template<> struct fex_gen_config<vkUnmapMemory2KHR> {};
template<> struct fex_gen_config<vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR> {};
template<> struct fex_gen_config<vkGetEncodedVideoSessionParametersKHR> {};
template<> struct fex_gen_config<vkCmdEncodeVideoKHR> {};
template<> struct fex_gen_config<vkCmdSetEvent2KHR> {};
template<> struct fex_gen_config<vkCmdResetEvent2KHR> {};
template<> struct fex_gen_config<vkCmdWaitEvents2KHR> {};
@@ -437,6 +442,15 @@ template<> struct fex_gen_config<vkGetRenderingAreaGranularityKHR> {};
template<> struct fex_gen_config<vkGetDeviceImageSubresourceLayoutKHR> {};
template<> struct fex_gen_config<vkGetImageSubresourceLayout2KHR> {};
template<> struct fex_gen_config<vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR> {};
template<> struct fex_gen_config<vkCmdSetLineStippleKHR> {};
template<> struct fex_gen_config<vkGetPhysicalDeviceCalibrateableTimeDomainsKHR> {};
template<> struct fex_gen_config<vkGetCalibratedTimestampsKHR> {};
template<> struct fex_gen_config<vkCmdBindDescriptorSets2KHR> {};
template<> struct fex_gen_config<vkCmdPushConstants2KHR> {};
template<> struct fex_gen_config<vkCmdPushDescriptorSet2KHR> {};
template<> struct fex_gen_config<vkCmdPushDescriptorSetWithTemplate2KHR> {};
template<> struct fex_gen_config<vkCmdSetDescriptorBufferOffsets2EXT> {};
template<> struct fex_gen_config<vkCmdBindDescriptorBufferEmbeddedSamplers2EXT> {};
template<> struct fex_gen_config<vkCreateDebugReportCallbackEXT> : fexgen::custom_host_impl {};
template<> struct fex_gen_param<vkCreateDebugReportCallbackEXT, 1, const VkDebugReportCallbackCreateInfoEXT*> : fexgen::ptr_passthrough {};
template<> struct fex_gen_config<vkDestroyDebugReportCallbackEXT> : fexgen::custom_host_impl {};
@@ -579,6 +593,12 @@ template<> struct fex_gen_config<vkCreatePrivateDataSlotEXT> {};
template<> struct fex_gen_config<vkDestroyPrivateDataSlotEXT> {};
template<> struct fex_gen_config<vkSetPrivateDataEXT> {};
template<> struct fex_gen_config<vkGetPrivateDataEXT> {};
template<> struct fex_gen_config<vkCreateCudaModuleNV> {};
template<> struct fex_gen_config<vkGetCudaModuleCacheNV> {};
template<> struct fex_gen_config<vkCreateCudaFunctionNV> {};
template<> struct fex_gen_config<vkDestroyCudaModuleNV> {};
template<> struct fex_gen_config<vkDestroyCudaFunctionNV> {};
template<> struct fex_gen_config<vkCmdCudaLaunchKernelNV> {};
template<> struct fex_gen_config<vkGetDescriptorSetLayoutSizeEXT> {};
template<> struct fex_gen_config<vkGetDescriptorSetLayoutBindingOffsetEXT> {};
template<> struct fex_gen_config<vkGetDescriptorEXT> {};
@@ -633,7 +653,6 @@ template<> struct fex_gen_config<vkCmdDecompressMemoryIndirectCountNV> {};
template<> struct fex_gen_config<vkGetPipelineIndirectMemoryRequirementsNV> {};
template<> struct fex_gen_config<vkCmdUpdatePipelineIndirectBufferNV> {};
template<> struct fex_gen_config<vkGetPipelineIndirectDeviceAddressNV> {};
template<> struct fex_gen_config<vkCmdSetTessellationDomainOriginEXT> {};
template<> struct fex_gen_config<vkCmdSetDepthClampEnableEXT> {};
template<> struct fex_gen_config<vkCmdSetPolygonModeEXT> {};
template<> struct fex_gen_config<vkCmdSetRasterizationSamplesEXT> {};
@@ -644,6 +663,7 @@ template<> struct fex_gen_config<vkCmdSetLogicOpEnableEXT> {};
template<> struct fex_gen_config<vkCmdSetColorBlendEnableEXT> {};
template<> struct fex_gen_config<vkCmdSetColorBlendEquationEXT> {};
template<> struct fex_gen_config<vkCmdSetColorWriteMaskEXT> {};
template<> struct fex_gen_config<vkCmdSetTessellationDomainOriginEXT> {};
template<> struct fex_gen_config<vkCmdSetRasterizationStreamEXT> {};
template<> struct fex_gen_config<vkCmdSetConservativeRasterizationModeEXT> {};
template<> struct fex_gen_config<vkCmdSetExtraPrimitiveOverestimationSizeEXT> {};
@@ -677,6 +697,11 @@ template<> struct fex_gen_config<vkGetShaderBinaryDataEXT> {};
template<> struct fex_gen_config<vkCmdBindShadersEXT> {};
template<> struct fex_gen_config<vkGetFramebufferTilePropertiesQCOM> {};
template<> struct fex_gen_config<vkGetDynamicRenderingTilePropertiesQCOM> {};
template<> struct fex_gen_config<vkSetLatencySleepModeNV> {};
template<> struct fex_gen_config<vkLatencySleepNV> {};
template<> struct fex_gen_config<vkSetLatencyMarkerNV> {};
template<> struct fex_gen_config<vkGetLatencyTimingsNV> {};
template<> struct fex_gen_config<vkQueueNotifyOutOfBandNV> {};
template<> struct fex_gen_config<vkCmdSetAttachmentFeedbackLoopEnableEXT> {};
template<> struct fex_gen_config<vkCreateAccelerationStructureKHR> {};
template<> struct fex_gen_config<vkDestroyAccelerationStructureKHR> {};
+78 -56
View File
@@ -33,17 +33,11 @@ extern "C" const wl_interface wl_callback_interface {};
#include <cstdarg>
#include <cstring>
#include <string>
#include <unordered_map>
#include "common/Guest.h"
#include "thunkgen_guest_libwayland-client.inl"
struct wl_proxy_private {
wl_interface* interface;
// Other data members omitted
};
// See wayland-util.h for documentation on protocol message signatures
template<char> struct ArgType;
template<> struct ArgType<'s'> { using type = const char*; };
@@ -56,37 +50,31 @@ template<> struct ArgType<'f'> { using type = wl_fixed_t; };
template<> struct ArgType<'h'> { using type = int32_t; }; // fd?
template<char... Signature>
static void* WaylandAllocateHostTrampolineForGuestListener(void (*callback)()) {
static uint64_t WaylandAllocateHostTrampolineForGuestListener(void (*callback)()) {
using cb = void(void*, wl_proxy*, typename ArgType<Signature>::type...);
return (void*)AllocateHostTrampolineForGuestFunction((cb*)callback);
return (uint64_t)(uintptr_t)(void*)AllocateHostTrampolineForGuestFunction((cb*)callback);
}
#define WL_CLOSURE_MAX_ARGS 20
// Per-proxy list of callbacks set up via wl_proxy_add_listener.
// These tables store the host-callable trampolines to the actual listener
// callbacks provided by the guest application.
// NOTE: There can only be one listener per proxy. Wayland will return an error
// if wl_proxy_add_listener is called twice.
// NOTE: Entries should be removed in wl_destroy_proxy. Since proxy wrappers do
// not use their own listeners, wl_proxy_wrapper_destroy does not need to
// be customized.
static std::unordered_map<wl_proxy*, std::array<void*, WL_CLOSURE_MAX_ARGS>> proxy_listeners;
extern "C" int wl_proxy_add_listener(wl_proxy *proxy,
void (**callback)(void), void *data) {
auto interface = ((wl_proxy_private*)proxy)->interface;
// Replace guest-provided callback table with host-callable function pointers
// NOTE: A reference to this table is stored in the wl_proxy, so the data
// must remain valid until the proxy is destroyed (or another listener
// is added)
delete[] (uint64_t*)wl_proxy_get_listener(proxy); // Delete previous substitute, if any
auto host_callbacks = new uint64_t[WL_CLOSURE_MAX_ARGS];
// NOTE: This table must remain valid past the return of this function.
auto& host_callbacks = proxy_listeners[proxy];
for (int i = 0; i < ((wl_proxy_private*)proxy)->interface->event_count; ++i) {
auto signature_view = std::string_view { interface->events[i].signature };
for (int i = 0; i < fex_wl_get_interface_event_count(proxy); ++i) {
char event_signature[16];
fex_wl_get_interface_event_signature(proxy, i, event_signature);
auto signature2 = std::string_view { event_signature };
// A leading number indicates the minimum protocol version
uint32_t since_version = 0;
auto [ptr, res] = std::from_chars(signature_view.begin(), signature_view.end(), since_version, 10);
std::string signature { ptr, &*signature_view.end() };
auto [ptr, res] = std::from_chars(signature2.begin(), signature2.end(), since_version, 10);
auto signature = std::string { signature2.substr(ptr - signature2.begin()) };
// ? just indicates that the argument may be null, so it doesn't change the signature
signature.erase(std::remove(signature.begin(), signature.end(), '?'), signature.end());
@@ -185,17 +173,18 @@ extern "C" int wl_proxy_add_listener(wl_proxy *proxy,
// E.g. zwp_text_input_v3::preedit_string
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s', 'i', 'i'>(callback[i]);
} else {
fprintf(stderr, "Unknown wayland signature descriptor \"%s\" for event \"%s\" in interface \"%s\"\n", signature.data(), interface->events[i].name, interface->name);
fprintf(stderr, "TODO: Unknown wayland event signature descriptor %s\n", signature.data());
std::abort();
}
}
return fexfn_pack_wl_proxy_add_listener(proxy, (void(**)())host_callbacks.data(), data);
return fexfn_pack_wl_proxy_add_listener(proxy, (void(**)())host_callbacks, data);
}
extern "C" void wl_proxy_destroy(struct wl_proxy *proxy) {
proxy_listeners.erase(proxy);
return fexfn_pack_wl_proxy_destroy(proxy);
extern "C" void wl_proxy_destroy(wl_proxy *proxy) {
// Delete substitute callback table (if any), then the proxy itself
delete[] (uint64_t*)wl_proxy_get_listener(proxy);
fexfn_pack_wl_proxy_destroy(proxy);
}
// Adapted from the Wayland sources
@@ -266,18 +255,48 @@ extern "C" void wl_proxy_marshal(wl_proxy *proxy, uint32_t opcode, ...) {
va_list ap;
va_start(ap, opcode);
#ifdef IS_32BIT_THUNK
// Must extract signature from host due to different data layout on 32-bit
#error Not implemented
#else
wl_argument_from_va_list(((wl_proxy_private*)proxy)->interface->methods[opcode].signature,
args, WL_CLOSURE_MAX_ARGS, ap);
#endif
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
wl_proxy_marshal_array(proxy, opcode, args);
}
extern "C" wl_proxy *wl_proxy_marshal_constructor(wl_proxy *proxy, uint32_t opcode,
const wl_interface *interface, ...) {
wl_argument args[WL_CLOSURE_MAX_ARGS];
va_list ap;
va_start(ap, interface);
// This is equivalent to reading ((wl_proxy_private*)proxy)->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
return wl_proxy_marshal_array_constructor(proxy, opcode, args, interface);
}
extern "C" wl_proxy *wl_proxy_marshal_constructor_versioned(wl_proxy *proxy, uint32_t opcode,
const wl_interface *interface, uint32_t version, ...) {
wl_argument args[WL_CLOSURE_MAX_ARGS];
va_list ap;
va_start(ap, version);
// This is equivalent to reading ((wl_proxy_private*)proxy)->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
return wl_proxy_marshal_array_constructor_versioned(proxy, opcode, args, interface, version);
}
extern "C" wl_proxy *wl_proxy_marshal_flags(wl_proxy *proxy, uint32_t opcode,
const wl_interface *interface,
uint32_t version,
@@ -286,13 +305,11 @@ extern "C" wl_proxy *wl_proxy_marshal_flags(wl_proxy *proxy, uint32_t opcode,
va_list ap;
va_start(ap, flags);
#ifdef IS_32BIT_THUNK
// Must extract signature from host due to different data layout on 32-bit
#error Not implemented
#else
wl_argument_from_va_list(((wl_proxy_private*)proxy)->interface->methods[opcode].signature,
args, WL_CLOSURE_MAX_ARGS, ap);
#endif
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
char signature[64];
fex_wl_get_method_signature(proxy, opcode, signature);
wl_argument_from_va_list(signature, args, WL_CLOSURE_MAX_ARGS, ap);
va_end(ap);
// wl_proxy_marshal_array_flags is only available starting from Wayland 1.19.91
@@ -304,18 +321,23 @@ extern "C" wl_proxy *wl_proxy_marshal_flags(wl_proxy *proxy, uint32_t opcode,
#endif
}
extern "C" void wl_log_set_handler_client(wl_log_func_t handler) {
// Ignore
}
void OnInit() {
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_output_interface), "wl_output");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_pool_interface), "wl_shm_pool");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_pointer_interface), "wl_pointer");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_compositor_interface), "wl_compositor");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_interface), "wl_shm");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_registry_interface), "wl_registry");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_buffer_interface), "wl_buffer");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_seat_interface), "wl_seat");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_surface_interface), "wl_surface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_keyboard_interface), "wl_keyboard");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_callback_interface), "wl_callback");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_output_interface), "wl_output_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_pool_interface), "wl_shm_pool_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_pointer_interface), "wl_pointer_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_compositor_interface), "wl_compositor_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shm_interface), "wl_shm_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_registry_interface), "wl_registry_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_buffer_interface), "wl_buffer_interface");
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_seat_interface), "wl_seat_interface");
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");
}
LOAD_LIB_INIT(libwayland-client, OnInit)
+321 -25
View File
@@ -5,6 +5,7 @@ $end_info$
*/
#include <string_view>
#include <unordered_map>
#include <wayland-client.h>
#include <stdio.h>
@@ -19,14 +20,272 @@ $end_info$
#include <charconv>
#include <cstring>
#include <map>
#include <span>
#include <string>
#include <ranges>
template<>
struct guest_layout<wl_argument> {
#ifdef IS_32BIT_THUNK
using type = uint32_t;
#else
using type = wl_argument;
#endif
type data;
guest_layout& operator=(const wl_argument from) {
#ifdef IS_32BIT_THUNK
data = from.u;
#else
data = from;
#endif
return *this;
}
};
#include "thunkgen_host_libwayland-client.inl"
struct wl_proxy_private {
wl_interface* interface;
// Other data members omitted
};
// Maps guest interface to host_interfaces
static std::unordered_map<guest_layout<const wl_interface>*, wl_interface*> guest_to_host_interface;
static wl_interface* get_proxy_interface(wl_proxy* proxy) {
// wl_proxy is a private struct, but its first member is the wl_interface pointer
return *reinterpret_cast<wl_interface**>(proxy);
}
static void assert_is_valid_host_interface(const wl_interface* interface) {
// The 32-bit data layout of wl_interface differs from the 64-bit one due to
// its pointer members. Our repacking code takes care of these differences.
//
// To ensure this indeed functions properly, a simple consistency check is
// applied here: If any of the message counts are absurdly high, it means
// data from pointer members leaked into other members.
if ((uint32_t)interface->method_count >= 0x1000 || (uint32_t)interface->event_count >= 0x1000) {
fprintf(stderr, "ERROR: Expected %p to be a host wl_interface, but it's not\n", interface);
std::abort();
}
}
#ifdef IS_32BIT_THUNK
static void assert_is_valid_guest_interface(guest_layout<const wl_interface*> guest_interface) {
// Consistency check for expected data layout.
// See assert_is_valid_host_interface for details
const wl_interface* as_host_interface = (const wl_interface*)guest_interface.force_get_host_pointer();
if ((uint32_t)as_host_interface->method_count < 0x1000 && (uint32_t)as_host_interface->event_count < 0x1000) {
fprintf(stderr, "ERROR: Expected %p to be a guest wl_interface, but it's not\n", guest_interface.force_get_host_pointer());
std::abort();
}
}
static void repack_guest_wl_interface_to_host(guest_layout<const wl_interface*> guest_interface_ptr, wl_interface* host_interface) {
auto& guest_interface = *guest_interface_ptr.get_pointer();
static_assert(sizeof(guest_interface) == 24);
*host_interface = host_layout<wl_interface> { guest_interface }.data;
fex_apply_custom_repacking_entry(reinterpret_cast<host_layout<wl_interface>&>(*host_interface), guest_interface);
}
// Maps guest interface pointers to host pointers
static const wl_interface* lookup_wl_interface(guest_layout<const wl_interface*> interface) {
// Used e.g. for wl_shm_pool_destroy
if (interface.force_get_host_pointer() == nullptr) {
return nullptr;
}
auto [host_interface_it, inserted] = guest_to_host_interface.emplace(interface.get_pointer(), nullptr);
if (!inserted) {
assert_is_valid_host_interface(host_interface_it->second);
return host_interface_it->second;
}
assert_is_valid_guest_interface(interface);
fprintf(stderr, "Unknown wayland interface %p, adding to registry\n", interface.get_pointer());
host_interface_it->second = new wl_interface;
wl_interface* host_interface = host_interface_it->second;
repack_guest_wl_interface_to_host(interface, host_interface);
return host_interface_it->second;
}
void fex_custom_repack_entry(host_layout<wl_interface>& into, guest_layout<wl_interface> const& from) {
// NOTE: These arrays are complements to global symbols in the guest, so we
// never explicitly free this memory
auto& host_interface = into.data;
into.data.methods = new wl_message[into.data.method_count];
into.data.events = new wl_message[into.data.event_count];
memset((void*)host_interface.methods, 0, sizeof(wl_message) * host_interface.method_count);
for (int i = 0; i < host_interface.method_count; ++i) {
const auto& guest_method { from.data.methods.get_pointer()[i] };
host_layout<wl_message> host_method { guest_method };
fex_apply_custom_repacking_entry(host_method, guest_method);
memcpy((void*)&host_interface.methods[i], &host_method, sizeof(host_method));
}
memset((void*)host_interface.events, 0, sizeof(wl_message) * host_interface.event_count);
for (int i = 0; i < host_interface.event_count; ++i) {
const auto& guest_event { from.data.events.get_pointer()[i] };
host_layout<wl_message> host_event { guest_event };
fex_apply_custom_repacking_entry(host_event, guest_event);
memcpy((void*)&host_interface.events[i], &host_event, sizeof(host_event));
}
}
bool fex_custom_repack_exit(guest_layout<wl_interface>&, host_layout<wl_interface> const&) {
fprintf(stderr, "Should not be called: %s\n", __PRETTY_FUNCTION__);
std::abort();
}
void fex_custom_repack_entry(host_layout<wl_message>& into, guest_layout<wl_message> const& from) {
auto& host_method = into.data;
auto num_types = std::ranges::count_if(std::string_view { host_method.signature }, isalpha);
if (num_types) {
host_method.types = new const wl_interface*[num_types];
for (int type = 0; type < num_types; ++type) {
auto guest_interface_addr = from.data.types.get_pointer()[type];
host_method.types[type] = guest_interface_addr.force_get_host_pointer() ? lookup_wl_interface(guest_interface_addr) : nullptr;
}
}
}
bool fex_custom_repack_exit(guest_layout<wl_message>&, host_layout<wl_message> const&) {
fprintf(stderr, "Should not be called: %s\n", __PRETTY_FUNCTION__);
std::abort();
}
#else
const wl_interface* lookup_wl_interface(guest_layout<const wl_interface*> interface) {
return interface.force_get_host_pointer();
}
#endif
static wl_proxy* fexfn_impl_libwayland_client_wl_proxy_create(wl_proxy* proxy, guest_layout<const wl_interface*> guest_interface_raw) {
auto host_interface = lookup_wl_interface(guest_interface_raw);
return fexldr_ptr_libwayland_client_wl_proxy_create(proxy, host_interface);
}
#define WL_CLOSURE_MAX_ARGS 20
static auto
fex_wl_remap_argument_list(guest_layout<wl_argument*> args, const wl_message& message) {
#ifndef IS_32BIT_THUNK
// Cast to host layout and return as std::span
wl_argument* host_args = host_layout<wl_argument*> { args }.data;
return std::span<wl_argument, WL_CLOSURE_MAX_ARGS> { host_args, WL_CLOSURE_MAX_ARGS };
#else
// Return a new array of elements zero-extended to 64-bit
std::array<wl_argument, WL_CLOSURE_MAX_ARGS> host_args;
int arg_count = std::ranges::count_if(std::string_view { message.signature }, isalpha);
for (int i = 0; i < arg_count; ++i) {
// NOTE: wl_argument can store a pointer argument, so for 32-bit guests
// we need to make sure the upper 32-bits are explicitly zeroed
std::memset(&host_args[i], 0, sizeof(host_args[i]));
std::memcpy(&host_args[i], &args.get_pointer()[i], sizeof(args.get_pointer()[i]));
}
return host_args;
#endif
}
extern "C" void
fexfn_impl_libwayland_client_wl_proxy_marshal_array(
wl_proxy *proxy, uint32_t opcode,
guest_layout<wl_argument*> args) {
auto host_args = fex_wl_remap_argument_list(args, get_proxy_interface(proxy)->methods[opcode]);
fexldr_ptr_libwayland_client_wl_proxy_marshal_array(proxy, opcode, host_args.data());
}
static wl_proxy*
fex_wl_proxy_marshal_array(
wl_proxy *proxy, uint32_t opcode,
guest_layout<wl_argument*> args,
guest_layout<const wl_interface*> guest_interface,
bool constructor, // Call the _constructor variant of the native wayland function
std::optional<uint32_t> version,
std::optional<uint32_t> flags) {
auto interface = lookup_wl_interface(guest_interface);
assert_is_valid_host_interface(get_proxy_interface(proxy));
auto host_args = fex_wl_remap_argument_list(args, get_proxy_interface(proxy)->methods[opcode]);
if (false) {
} else if (!constructor && !version && !flags) {
return nullptr;
} else if (!constructor && version && flags) {
// wl_proxy_marshal_array_flags is only available starting from Wayland 1.19.91
#if WAYLAND_VERSION_MAJOR * 10000 + WAYLAND_VERSION_MINOR * 100 + WAYLAND_VERSION_MICRO >= 11991
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_flags(proxy, opcode, interface, version.value(), flags.value(), host_args.data());
#else
fprintf(stderr, "Host Wayland version is too old to support FEX thunking\n");
__builtin_trap();
#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) {
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_constructor(proxy, opcode, host_args.data(), interface);
} else {
fprintf(stderr, "Invalid configuration\n");
__builtin_trap();
}
}
extern "C" wl_proxy*
fexfn_impl_libwayland_client_wl_proxy_marshal_array_constructor_versioned(
wl_proxy *proxy, uint32_t opcode,
guest_layout<wl_argument*> args,
guest_layout<const wl_interface*> interface,
uint32_t version) {
return fex_wl_proxy_marshal_array(proxy, opcode, args, interface, true, version, std::nullopt);
}
extern "C" wl_proxy*
fexfn_impl_libwayland_client_wl_proxy_marshal_array_constructor(
wl_proxy *proxy, uint32_t opcode,
guest_layout<wl_argument*> args,
guest_layout<const wl_interface*> interface) {
return fex_wl_proxy_marshal_array(proxy, opcode, args, interface, true, std::nullopt, std::nullopt);
}
extern "C" wl_proxy*
fexfn_impl_libwayland_client_wl_proxy_marshal_array_flags(
wl_proxy *proxy, uint32_t opcode,
guest_layout<const wl_interface*> interface,
uint32_t version, uint32_t flags,
guest_layout<wl_argument*> args) {
return fex_wl_proxy_marshal_array(proxy, opcode, args, interface, false, version, flags);
}
// Variant of CallbackUnpack::CallGuestPtr that relocates a wl_array parameter
// for 32-bit guests. Relocating this parameter is required since it may
// reference inaccessible memory regions (presumably due to pointing to data
// on the host stack).
#ifndef IS_32BIT_THUNK
template<typename Result, typename... Args>
const auto CallGuestPtrWithWaylandArray = CallbackUnpack<Result(Args..., wl_array*)>::CallGuestPtr;
#else
template<typename Result, typename... Args>
static auto CallGuestPtrWithWaylandArray(Args... args, wl_array *array) -> Result {
GuestcallInfo *guestcall;
LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(guestcall);
using PackedArgumentsType = PackedArguments<Result, guest_layout<Args>..., guest_layout<wl_array*>>;
GuestStackBumpAllocator GuestStack;
auto* guest_array = GuestStack.New<guest_layout<wl_array>>(to_guest(to_host_layout(*array)));
guest_layout<wl_array*> guest_array_ptr = { .data = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(guest_array)) };
auto& packed_args = *GuestStack.New<PackedArgumentsType>(
to_guest(to_host_layout(args))..., guest_array_ptr
);
guestcall->CallCallback(guestcall->GuestUnpacker, guestcall->GuestTarget, &packed_args);
if constexpr (!std::is_void_v<Result>) {
return packed_args.rv;
}
}
#endif
// See wayland-util.h for documentation on protocol message signatures
template<char> struct ArgType;
@@ -46,28 +305,34 @@ static void WaylandFinalizeHostTrampolineForGuestListener(void (*callback)()) {
}
extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy *proxy,
guest_layout<void (**)(void)> callback_raw, void* data) {
auto guest_interface = ((wl_proxy_private*)proxy)->interface;
guest_layout<void (**)(void)> callback_table_raw, void* data) {
auto interface = get_proxy_interface(proxy);
for (int i = 0; i < guest_interface->event_count; ++i) {
auto signature_view = std::string_view { guest_interface->events[i].signature };
assert_is_valid_host_interface(interface);
auto callback_table = callback_table_raw.force_get_host_pointer();
for (int i = 0; i < interface->event_count; ++i) {
auto signature_view = std::string_view { interface->events[i].signature };
// A leading number indicates the minimum protocol version
uint32_t since_version = 0;
auto [ptr, res] = std::from_chars(signature_view.begin(), signature_view.end(), since_version, 10);
std::string signature { ptr, &*signature_view.end() };
auto signature = std::string { signature_view.substr(ptr - signature_view.begin()) };
// ? just indicates that the argument may be null, so it doesn't change the signature
signature.erase(std::remove(signature.begin(), signature.end(), '?'), signature.end());
auto callback = reinterpret_cast<void(*)()>(uintptr_t { callback_raw.get_pointer()[i].data });
auto callback = callback_table[i];
if (signature == "") {
// E.g. xdg_toplevel::close
WaylandFinalizeHostTrampolineForGuestListener<>(callback);
} else if (signature == "a") {
// E.g. xdg_toplevel::wm_capabilities
WaylandFinalizeHostTrampolineForGuestListener<'a'>(callback);
FEXCore::FinalizeHostTrampolineForGuestFunction(
(FEXCore::HostToGuestTrampolinePtr*)callback,
(void*)CallGuestPtrWithWaylandArray<void, void*, wl_proxy*>);
} else if (signature == "hu") {
// E.g. zwp_linux_dmabuf_feedback_v1::format_table
WaylandFinalizeHostTrampolineForGuestListener<'h', 'u'>(callback);
@@ -85,7 +350,9 @@ extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_prox
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i'>(callback);
} else if (signature == "iia") {
// E.g. xdg_toplevel::configure
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i', 'a'>(callback);
FEXCore::FinalizeHostTrampolineForGuestFunction(
(FEXCore::HostToGuestTrampolinePtr*)callback,
(void*)CallGuestPtrWithWaylandArray<void, void*, wl_proxy*, int32_t, int32_t>);
} else if (signature == "iiiiissi") {
// E.g. wl_output_listener::geometry
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i', 'i', 'i', 'i', 's', 's', 'i'>(callback);
@@ -118,7 +385,9 @@ extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_prox
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o'>(callback);
} else if (signature == "uoa") {
// E.g. wl_keyboard_listener::enter
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o', 'a'>(callback);
FEXCore::FinalizeHostTrampolineForGuestFunction(
(FEXCore::HostToGuestTrampolinePtr*)callback,
(void*)CallGuestPtrWithWaylandArray<void, void*, wl_proxy*, uint32_t, wl_surface*>);
} else if (signature == "uoff") {
// E.g. wl_pointer_listener::enter
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o', 'f', 'f'>(callback);
@@ -162,15 +431,15 @@ extern "C" int fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_prox
}
// Pass the original function pointer table to the host wayland library. This ensures the table is valid until the listener is unregistered.
return fexldr_ptr_libwayland_client_wl_proxy_add_listener(proxy,
reinterpret_cast<void(**)(void)>(callback_raw.get_pointer()),
data);
return fexldr_ptr_libwayland_client_wl_proxy_add_listener(proxy, callback_table, data);
}
wl_interface* fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(wl_interface* guest_interface, char const* name) {
auto host_interface = reinterpret_cast<wl_interface*>(dlsym(fexldr_ptr_libwayland_client_so, (std::string { name } + "_interface").c_str()));
void fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(guest_layout<wl_interface*> guest_interface_raw, const char* name) {
auto& guest_interface = *guest_interface_raw.get_pointer();
auto& host_interface = guest_to_host_interface[reinterpret_cast<guest_layout<const wl_interface>*>(&guest_interface)];
host_interface = reinterpret_cast<wl_interface*>(dlsym(fexldr_ptr_libwayland_client_so, name));
if (!host_interface) {
fprintf(stderr, "Could not find host interface corresponding to %p (%s)\n", guest_interface, name);
fprintf(stderr, "Could not find host interface corresponding to %p (%s)\n", &guest_interface, name);
std::abort();
}
@@ -178,23 +447,50 @@ wl_interface* fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(wl_
// 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) & ~uintptr_t { 0xfff };
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)) {
fprintf(stderr, "ERROR: %s\n", strerror(errno));
std::abort();
}
#ifdef IS_32BIT_THUNK
// Requires struct repacking for wl_interface
#error Not implemented
#ifndef IS_32BIT_THUNK
memcpy(&guest_interface, host_interface, sizeof(wl_interface));
#else
memcpy(guest_interface, host_interface, sizeof(wl_interface));
guest_interface = to_guest(to_host_layout(*host_interface));
// NOTE: These arrays are complements to global symbols in the guest, so we
// never explicitly free this memory
guest_interface.data.methods.data = (uintptr_t)new guest_layout<wl_message>[host_interface->method_count];
for (int i = 0; i < host_interface->method_count; ++i) {
guest_interface.data.methods.get_pointer()[i] = to_guest(to_host_layout(host_interface->methods[i]));
guest_interface.data.methods.get_pointer()[i].data.types = to_guest(to_host_layout(host_interface->methods[i].types));
}
guest_interface.data.events.data = (uintptr_t)new guest_layout<wl_message>[host_interface->event_count];
for (int i = 0; i < host_interface->event_count; ++i) {
guest_interface.data.events.get_pointer()[i] = to_guest(to_host_layout(host_interface->events[i]));
guest_interface.data.events.get_pointer()[i].data.types = to_guest(to_host_layout(host_interface->events[i].types));
}
#endif
// TODO: Disabled until we ensure the interface data is indeed stored in rodata
// mprotect((void*)page_begin, 0x1000, PROT_READ);
}
return host_interface;
void fexfn_impl_libwayland_client_fex_wl_get_method_signature(wl_proxy* proxy, uint32_t opcode, char* out) {
strcpy(out, get_proxy_interface(proxy)->methods[opcode].signature);
}
int fexfn_impl_libwayland_client_fex_wl_get_interface_event_count(wl_proxy* proxy) {
return get_proxy_interface(proxy)->event_count;
}
void fexfn_impl_libwayland_client_fex_wl_get_interface_event_name(wl_proxy* proxy, int i, char* out) {
strcpy(out, get_proxy_interface(proxy)->events[i].name);
}
void fexfn_impl_libwayland_client_fex_wl_get_interface_event_signature(wl_proxy* proxy, int i, char* out) {
strcpy(out, get_proxy_interface(proxy)->events[i].signature);
}
EXPORTS(libwayland_client)
@@ -16,30 +16,41 @@ struct fex_gen_param {};
template<> struct fex_gen_type<wl_display> : fexgen::opaque_type {};
template<> struct fex_gen_type<wl_proxy> : fexgen::opaque_type {};
template<> struct fex_gen_type<wl_interface> : fexgen::opaque_type {};
template<> struct fex_gen_type<wl_event_queue> : fexgen::opaque_type {};
// Passed over Wayland's wire protocol for some functions
template<> struct fex_gen_type<wl_array> {};
template<> struct fex_gen_type<wl_array> : fexgen::emit_layout_wrappers {};
#ifdef IS_32BIT_THUNK
// wl_interface and wl_message reference each other through pointers
template<> struct fex_gen_type<wl_interface> : fexgen::emit_layout_wrappers {};
template<> struct fex_gen_config<&wl_interface::methods> : fexgen::custom_repack {};
template<> struct fex_gen_config<&wl_interface::events> : fexgen::custom_repack {};
template<> struct fex_gen_type<wl_message> : fexgen::emit_layout_wrappers {};
template<> struct fex_gen_config<&wl_message::types> : fexgen::custom_repack {};
#else
template<> struct fex_gen_type<wl_interface> : fexgen::assume_compatible_data_layout {};
#endif
template<> struct fex_gen_config<wl_proxy_destroy> : fexgen::custom_guest_entrypoint {};
template<> struct fex_gen_config<wl_display_cancel_read> {};
template<> struct fex_gen_config<wl_display_connect> {};
template<> struct fex_gen_config<wl_display_flush> {};
template<> struct fex_gen_config<wl_display_cancel_read> {};
template<> struct fex_gen_config<wl_display_create_queue> {};
template<> struct fex_gen_config<wl_display_disconnect> {};
template<> struct fex_gen_config<wl_display_dispatch> {};
template<> struct fex_gen_config<wl_display_dispatch_pending> {};
template<> struct fex_gen_config<wl_display_dispatch_queue> {};
template<> struct fex_gen_config<wl_display_dispatch_queue_pending> {};
template<> struct fex_gen_config<wl_display_flush> {};
template<> struct fex_gen_config<wl_display_get_error> {};
template<> struct fex_gen_config<wl_display_prepare_read> {};
template<> struct fex_gen_config<wl_display_prepare_read_queue> {};
template<> struct fex_gen_config<wl_display_read_events> {};
template<> struct fex_gen_config<wl_display_roundtrip> {};
template<> struct fex_gen_config<wl_display_roundtrip_queue> {};
template<> struct fex_gen_config<wl_display_connect_to_fd> {};
template<> struct fex_gen_config<wl_display_get_fd> {};
template<> struct fex_gen_config<wl_event_queue_destroy> {};
@@ -49,8 +60,12 @@ template<> struct fex_gen_config<wl_proxy_add_listener> : fexgen::custom_host_im
template<> struct fex_gen_param<wl_proxy_add_listener, 1, void(**)()> : fexgen::ptr_passthrough {};
// User-provided data pointer (not used in caller-provided callback)
template<> struct fex_gen_param<wl_proxy_add_listener, 2, void*> : fexgen::assume_compatible_data_layout {};
template<> struct fex_gen_config<wl_proxy_create> {};
template<> struct fex_gen_config<wl_proxy_create> : fexgen::custom_host_impl {};
template<> struct fex_gen_param<wl_proxy_create, 1, const wl_interface*> : fexgen::ptr_passthrough {};
template<> struct fex_gen_config<wl_proxy_create_wrapper> {};
template<> struct fex_gen_config<wl_proxy_get_class> {};
template<> struct fex_gen_config<wl_proxy_get_id> {};
template<> struct fex_gen_config<wl_proxy_get_listener> {};
template<> struct fex_gen_config<wl_proxy_get_tag> {};
template<> struct fex_gen_config<wl_proxy_get_user_data> {};
template<> struct fex_gen_config<wl_proxy_get_version> {};
@@ -60,12 +75,33 @@ template<> struct fex_gen_config<wl_proxy_set_tag> {};
template<> struct fex_gen_config<wl_proxy_set_user_data> {};
template<> struct fex_gen_config<wl_proxy_wrapper_destroy> {};
template<> struct fex_gen_config<wl_proxy_marshal_array> {};
template<> struct fex_gen_config<wl_proxy_marshal_array> : fexgen::custom_host_impl {};
template<> struct fex_gen_param<wl_proxy_marshal_array, 2, wl_argument*> : fexgen::ptr_passthrough {};
template<> struct fex_gen_config<wl_proxy_marshal_array_constructor> : fexgen::custom_host_impl {};
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor, 2, wl_argument*> : fexgen::ptr_passthrough {};
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor, 3, const wl_interface*> : fexgen::ptr_passthrough {};
template<> struct fex_gen_config<wl_proxy_marshal_array_constructor_versioned> : fexgen::custom_host_impl {};
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor_versioned, 2, wl_argument*> : fexgen::ptr_passthrough {};
template<> struct fex_gen_param<wl_proxy_marshal_array_constructor_versioned, 3, const wl_interface*> : fexgen::ptr_passthrough {};
// wl_proxy_marshal_array_flags is only available starting from Wayland 1.19.91
#if WAYLAND_VERSION_MAJOR * 10000 + WAYLAND_VERSION_MINOR * 100 + WAYLAND_VERSION_MICRO >= 11991
template<> struct fex_gen_config<wl_proxy_marshal_array_flags> {};
template<> struct fex_gen_config<wl_proxy_marshal_array_flags> : fexgen::custom_host_impl {};
template<> struct fex_gen_param<wl_proxy_marshal_array_flags, 2, const wl_interface*> : fexgen::ptr_passthrough {};
template<> struct fex_gen_param<wl_proxy_marshal_array_flags, 5, wl_argument*> : fexgen::ptr_passthrough {};
#endif
// Guest notifies host about its interface. Host returns its corresponding interface pointer
wl_interface* fex_wl_exchange_interface_pointer(wl_interface*, const char* name);
void fex_wl_exchange_interface_pointer(wl_interface*, const char* name);
template<> struct fex_gen_config<fex_wl_exchange_interface_pointer> : fexgen::custom_host_impl {};
template<> struct fex_gen_param<fex_wl_exchange_interface_pointer, 0, wl_interface*> : fexgen::ptr_passthrough {};
// This is equivalent to reading proxy->interface->methods[opcode].signature on 64-bit.
// On 32-bit, the data layout differs between host and guest however, so we let the host extract the data.
void fex_wl_get_method_signature(wl_proxy*, uint32_t opcode, char*);
template<> struct fex_gen_config<fex_wl_get_method_signature> : fexgen::custom_host_impl {};
int fex_wl_get_interface_event_count(wl_proxy*);
template<> struct fex_gen_config<fex_wl_get_interface_event_count> : fexgen::custom_host_impl {};
void fex_wl_get_interface_event_name(wl_proxy*, int, char*);
template<> struct fex_gen_config<fex_wl_get_interface_event_name> : fexgen::custom_host_impl {};
void fex_wl_get_interface_event_signature(wl_proxy*, int, char*);
template<> struct fex_gen_config<fex_wl_get_interface_event_signature> : fexgen::custom_host_impl {};
+1 -1
View File
@@ -1,4 +1,4 @@
# FEX-2402
# FEX-2403
## FEXCore
See [FEXCore/Readme.md](../FEXCore/Readme.md) for more details
+2
View File
@@ -117,4 +117,6 @@ add_custom_target(
32bit_asm_tests
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
USES_TERMINAL
DEPENDS 32bit_asm_files
DEPENDS "${CMAKE_BINARY_DIR}/Bin/TestHarnessRunner"
COMMAND "ctest" "--output-on-failure" "--timeout" "302" "-j${CORES}" "-R" "\.*32Bit\.*.asm$$")
@@ -0,0 +1,43 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0x41424344",
"RBX": "0x41424344",
"RCX": "0x51525354"
},
"MemoryRegions": {
"0x00fd0000": "4096",
"0xf0000000": "4096"
},
"MemoryData": {
"0xf0000000": "0x41424344",
"0x00fd0000": "0x51525354"
},
"Mode": "32BIT"
}
%endif
; Ensures that zero extension of addresses are adhered to.
lea eax, [0xf000_0000]
mov eax, [ds:eax]
; Ensures that zext occurs correctly with two registers that have the sign bit set.
mov ebx, 0xffff_ffff
mov ecx, 0xf000_0001
; Break the block so it can't optimize through.
jmp .test
.test:
mov ebx, [ebx+ecx]
; Ensures that zext occurs correctly with SIB indexing with second argument not having sign bit set but "index" having sign bit.
; Originally saw in Metal Gear Rising Revengeance with a `jmp dword [ecx*4+0xfdbf10]` instruction.
; With ecx = 0xfffffff4 = -12. This is them loading a switch table's branches just before the switch base.
mov ecx, -12
; Break the block so it can't optimize through.
jmp .test2
.test2:
mov ecx, [ecx*4+0x00fd_0030]
hlt
+12 -1
View File
@@ -83,7 +83,7 @@ foreach(ASM_SRC ${ASM_SOURCES})
list(GET TEST_ARGS ${TEST_NAME_INDEX} TEST_DESC)
list(GET TEST_ARGS ${TEST_TYPE_INDEX} TEST_TYPE)
set(TEST_NAME "${TEST_DESC}/Test_${REL_TEST_ASM}")
set(TEST_NAME "${TEST_DESC}/Test_64Bit_${REL_TEST_ASM}")
string(REPLACE " " ";" ARGS_LIST ${ARGS})
if (TEST_NAME MATCHES "SelfModifyingCode")
@@ -120,8 +120,19 @@ add_custom_target(asm_files ALL
execute_process(COMMAND "nproc" OUTPUT_VARIABLE CORES)
string(STRIP ${CORES} CORES)
add_custom_target(
64bit_asm_tests
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
USES_TERMINAL
DEPENDS asm_files
DEPENDS "${CMAKE_BINARY_DIR}/Bin/TestHarnessRunner"
COMMAND "ctest" "--output-on-failure" "--timeout" "302" "-j${CORES}" "-R" "\.*64Bit\.*.asm$$")
add_custom_target(
asm_tests
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
USES_TERMINAL
DEPENDS asm_files
DEPENDS 32bit_asm_files
DEPENDS "${CMAKE_BINARY_DIR}/Bin/TestHarnessRunner"
COMMAND "ctest" "--output-on-failure" "--timeout" "302" "-j${CORES}" "-R" "\.*.asm$$")
+32
View File
@@ -0,0 +1,32 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0"
}
}
%endif
; 32-bit:
; 265 = clock_gettime
; 64-bit
; 265 = linkat
; rax = syscall on both 32-bit and 64-bit
mov rax, 265
; rdi/rbx = first argument on 64-bit and 32-bit respectively
mov rdi, 0
mov rbx, 0
; rsi/rcx = second argument on 64-bit and 32-bit respectively
lea rsi, [rel .data]
lea rcx, [rel .data]
; Do a 32-bit syscall
; On a real linux kernel this will execute clock_gettime
; Under FEX without 32-bit syscall support this might try to execute linkat and return -ENOENT.
int 0x80
hlt
.data:
dq 0, 0, 0, 0
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more