Compare commits

...
297 Commits
Author SHA1 Message Date
Ryan Houdek 85c2889652 Docs: Update for release FEX-2210 2022-10-13 00:46:59 -07:00
Ryan Houdek 23dd056b60 Merge pull request #2067 from lioncash/vmul
IR: Handle 256-bit VSMul/VUMul
2022-10-12 15:59:03 -07:00
lioncash 71043e372a IR: Handle 256-bit VSMul/VUMul
Extends VSMul and VUMul to handle 256-bit vectors.
2022-10-12 00:00:15 +00:00
Ryan Houdek c412d073b9 Merge pull request #2066 from lioncash/vrev64
IR: Handle 256-bit VRev64
2022-10-11 14:21:45 -07:00
lioncash 8fb03ff1b9 IR: Handle 256-bit VRev64
Extends VRev64 to handle 256-bit vectors.
2022-10-11 20:17:04 +00:00
Ryan Houdek c2b6aef6f4 Merge pull request #2065 from lioncash/shift-imm
IR: Handle 256-bit VShlI/VUShlI/VUShrI
2022-10-11 12:16:44 -07:00
lioncash 24547318c6 IR: Handle 256-bit VShlI
Extends VShlI to handle 256-bit vectors.
2022-10-11 18:19:23 +00:00
Ryan Houdek b693112c80 Merge pull request #2058 from Sonicadvance1/add_opencl_thunk_db
Add opencl thunk db
2022-10-11 11:06:20 -07:00
lioncash 48d1184066 IR: Handle 256-bit VSShrI
Extends VSShrI to handle 256-bit vectors.
2022-10-11 18:04:13 +00:00
Ryan Houdek 8da9ebc2e0 Merge pull request #2062 from wannacu/main
SMC: Fix possible deadlock
2022-10-11 10:13:02 -07:00
lioncash 5ba510474b IR: Handle 256-bit VUShrI
Extends VUShrI to handle 256-bit vectors.
2022-10-11 17:08:35 +00:00
Ryan Houdek 51214d1be1 Merge pull request #2064 from lioncash/vushls
IR: Handle 256-bit VSShrS/VUShlS/VUShrS
2022-10-11 09:59:03 -07:00
Ryan Houdek 4d6e15d7af Merge pull request #2063 from lioncash/interp-shift
Interpreter: Handle 256-bit VSShr/VUShl/VUShr
2022-10-11 09:22:11 -07:00
lioncash 4721894427 IR: Handle 256-bit VSShrS
Extends VSShrS to handle 256-bit vectors.
2022-10-11 16:21:50 +00:00
lioncash d429865b6e IR: Handle 256-bit VUShrS
Extends VUShrS to handle 256-bit vectors.
2022-10-11 16:08:09 +00:00
lioncash ca5881a72c IR: Handle 256-bit VUShlS
Extends VUShlS to handle 256-bit vectors.
2022-10-11 15:45:39 +00:00
lioncash 7151b9daff Interpreter/VectorOps: Remove lingering magic 32 constants
Makes these functions consistent with the rest that explicitly test for
256 bit width.
2022-10-11 15:02:41 +00:00
lioncash d9b5e28b22 Interpreter: Handle 256-bit VSShr
This is only implemented in the interpreter, so this is trivial.
2022-10-11 14:58:40 +00:00
lioncash aa7954a7d6 Interpreter: Handle 256-bit VUShr
This is only implemented in the interpreter, so this is trivial.
2022-10-11 14:56:19 +00:00
lioncash 802c70d1ab Interpreter: Handle 256-bit VUShl
This is only implemented in the interpreter, so this is trivial.
2022-10-11 14:55:17 +00:00
wannacu 8f905988e9 Use compatible syscall helpers 2022-10-11 16:35:17 +08:00
wannacu 478c5595ad SMC: Fix possible deadlock 2022-10-11 15:31:27 +08:00
Ryan Houdek 3977e1f29e Merge pull request #2055 from Sonicadvance1/update_description_ripping_script
Scripts: Updates DefinitionExtract
2022-10-10 10:11:16 -07:00
Ryan Houdek 2b1ef97354 Merge pull request #2060 from Sonicadvance1/clang_thunks
Thunks: Add support for building with clang
2022-10-10 09:44:58 -07:00
Mai eaddf7f1a5 Merge pull request #2061 from Sonicadvance1/fix_linker_script_depends
Thunks: Adds dependency on linker script
2022-10-10 12:17:20 -04:00
Ryan Houdek 3237de3085 Merge pull request #2056 from Sonicadvance1/guest_function_bool
Thunks/Host: Adds bool operator to fex_guest_function_ptr
2022-10-10 09:10:34 -07:00
Ryan Houdek df3d398d31 Thunks/Host: Adds bool operator to fex_guest_function_ptr
Lets us check if nullptr was passed in
2022-10-10 08:52:50 -07:00
Ryan Houdek b44b3401b7 Merge pull request #2015 from Sonicadvance1/map_regular_offset
ELFCodeLoader: Map primary ELF more like the kernel
2022-10-10 08:49:49 -07:00
Ryan Houdek c28ca0fac9 ELFCodeLoader: Map primary ELF more like the kernel
The kernel maps the primary ELF with a hint to some place *near* the
middle of the virtual address space. While the interpreter stays at the
top of the address space.

This also adds ASLR to the ELF loading, with a define for debugging and
testing purposes.

Requires both #2013 and #2014 merged first.
2022-10-10 08:38:15 -07:00
Ryan Houdek 235e2b6c2c FEXCore/Allocator: Store what the host VA is
Calling this function multiple times without this will change the
result.

Necessary so we can determine what the host VA is  from multiple
locations.
2022-10-10 08:35:40 -07:00
Mai d68b84bc27 Merge pull request #2013 from Sonicadvance1/fix_mapper
ELFCodeloader: Map once and then use MAP_FIXED to overwrite
2022-10-10 10:05:53 -04:00
Mai edca528608 Merge pull request #2039 from Sonicadvance1/fix_dynamic_non_interpreter_elfs
ELFCodeLoader: Fixes dynamic non-interpreter ELFs
2022-10-10 10:04:19 -04:00
Ryan Houdek b75e8f2abf Thunks: Add support for building with clang
Fairly straightforward, just requires enabling lld in this case since
cross-compiling doesn't work well with gnu linker.

Also lld doesn't understand the linker script program header symbolic
names for read/write/execute. So we need to use the raw number there.

Works around an issue where GCC 11 generates broken `init_array` section
and also plt sections that glibc doesn't understand.
2022-10-09 23:07:30 -07:00
Ryan Houdek ec3158e4cd Thunks: Adds dependency on linker script
Ensures the thunk is rebuilt if the linker scripts have changed.

Fixes #2054
2022-10-09 22:57:52 -07:00
Ryan Houdek 9c8c8041e0 ThunksDB: Adds OpenCL to the json
This will be used soon
2022-10-09 19:52:36 -07:00
Ryan Houdek e3adaacb51 Thunks: Adds another packed arguments template
This will be used soon
2022-10-09 19:47:37 -07:00
Ryan Houdek c49e11484f Scripts: Updates DefinitionExtract
Unused warning attribute wasn't getting ignored.
Also need to update output text to match new format
2022-10-09 19:44:49 -07:00
Ryan Houdek 7b4b9a80fa Merge pull request #2053 from lioncash/vfcmpord
IR: Handle 256-bit VFCMPORD/VFCMPUNO
2022-10-09 19:43:36 -07:00
lioncash c7ad066987 IR: Handle 256-bit VFCMPUNO
Extends VFCMPUNO to handle 256-bit vectors.
2022-10-06 15:43:01 +00:00
lioncash f4d229f1ba IR: Handle 256-bit VFCMPORD
Extends VFCMPORD to handle 256-bit vectors.
2022-10-06 15:33:30 +00:00
Ryan Houdek 25a8a00771 Merge pull request #2050 from lioncash/vfcmplt
IR: Handle 256-bit VFCMPLT/VFCMPGT/VFCMPLE
2022-10-04 14:44:54 -07:00
Ryan Houdek a67f7422b2 Merge pull request #2049 from lioncash/fcmeq
IR: Handle 256-bit VFCMPEQ/VFCMPNEQ
2022-10-04 14:43:52 -07:00
Ryan Houdek ed8150cfb6 Merge pull request #2048 from lioncash/vcmpgt
IR: Handle 256-bit VCMPGT/VCMPGTZ/VCMPLTZ
2022-10-04 14:42:53 -07:00
Ryan Houdek 462a163ba7 Merge pull request #2047 from lioncash/vcmpeq
IR: Handle 256-bit VCMPEQ/VCMPEQZ
2022-10-04 14:40:48 -07:00
Ryan Houdek 6374175a64 Merge pull request #2046 from lioncash/vbsl
IR: Handle 256-bit VBSL
2022-10-04 14:37:25 -07:00
lioncash 280b15ba2a IR: Handle 256-bit VFCMPLE
Extends VFCMPLE to handle 256-bit vectors.
2022-10-04 19:56:59 +00:00
lioncash 2a0b488e99 IR: Handle 256-bit VFCMPGT
Extends VFCMPGT to handle 256-bit vectors.
2022-10-04 19:43:32 +00:00
lioncash 3ef7c4ab51 IR: Handle 256-bit VFCMPLT
Extends VFCMPLT to handle 256-bit vectors.
2022-10-04 19:29:36 +00:00
lioncash e72d746036 IR: Handle 256-bit VFCMPNEQ
Extends VFCMPNEQ to handle 256-bit vectors.
2022-10-04 19:02:20 +00:00
lioncash 0bb4091e34 IR: Handle 256-bit VFCMPEQ
Extends VFCMPEQ to handle 256-bit vectors.
2022-10-04 18:44:12 +00:00
lioncash 6822fc595c IR: Handle 256-bit VCMPLTZ
Extends VCMPLTZ to handle 256-bit vectors.
2022-10-04 18:09:39 +00:00
lioncash a506a589dd IR: Handle 256-bit VCMPGTZ
Extends VCMPGTZ to handle 256-bit vectors.
2022-10-04 17:55:03 +00:00
lioncash 684a5977dd IR: Handle 256-bit VCMPGT
Extends VCMPGT to handle 256-bit registers.
2022-10-04 17:38:20 +00:00
lioncash ac3682e058 IR: Handle 256-bit VCMPEQZ
Extends VCMPEQZ to handle 256-bit vectors.
2022-10-04 16:57:19 +00:00
lioncash d5faf01f5a IR: Handle 256-bit VCMPEQ
Extends VCMPEQ to handle 256-bit vectors.
2022-10-04 16:33:25 +00:00
lioncash ecba1b6838 IR: Handle 256-bit VBSL
Extends VBSL to handle 256-bit vectors.
2022-10-03 18:06:27 +00:00
Ryan Houdek 8d8b029285 Merge pull request #2044 from lioncash/vumax
IR: Handle 256-bit VSMax/VUMax
2022-09-29 13:52:40 -07:00
lioncash bf6f855868 IR: Handle 256-bit VSMax
Extends VSMax to handle 256-bit vectors.
2022-09-29 20:31:19 +00:00
Ryan Houdek aa6a499329 Merge pull request #2043 from lioncash/vumin
IR: Handle 256-bit VSMin/VUMin
2022-09-29 13:19:07 -07:00
lioncash 0971650ef9 IR: Handle 256-bit VUMax
Extends VUMax to handle 256-bit vectors.
2022-09-29 20:18:42 +00:00
Ryan Houdek 64c4fdccf7 Merge pull request #2042 from lioncash/vnot
IR: Handle 256-bit VNot
2022-09-29 13:13:23 -07:00
Ryan Houdek d715ffbc8e Merge pull request #2041 from lioncash/vfneg
IR: Handle 256-bit VFNeg
2022-09-29 13:12:21 -07:00
lioncash aef801b5b5 IR: Handle 256-bit VSMin
Extends VSMin to handle 256-bit vectors.
2022-09-29 20:01:04 +00:00
lioncash 6c9e29796b IR: Handle 256-bit VUMin
Extends VUMin to handle 256-bit vectors.
2022-09-29 19:47:47 +00:00
lioncash 364bb3ac1e IR: Handle 256-bit VNot
Extends VNot to handle 256-bit vectors.
2022-09-29 14:40:46 +00:00
lioncash 428ea68507 IR: Handle 256-bit VFNeg
Extends VFNeg to handle 256-bit vectors.
2022-09-29 14:00:52 +00:00
Ryan Houdek 5cf59408a7 Merge pull request #2040 from Sonicadvance1/fix_vsyscall
VDSO: Fix vsyscall
2022-09-29 01:47:53 -07:00
Ryan Houdek 1596843015 VDSO: Fix vsyscall
The `mov ebp, ecx` was breaking vsyscall and was expected to be used
with the `syscall` instruction rather than `int 0x80`.
Remove that to fix it.

Also remove the pushes and pops around the syscall instruction, these
are unnecessary in an emulated environment, we won't clobber the
registers.

Fixes Steam execution with VDSO.
2022-09-28 17:34:24 -07:00
Ryan Houdek af6582ff5b ELFCodeLoader: Fixes dynamic non-interpreter ELFs
Specifically fixes /sbin/ldconfig.
Fixes 8df7c2d84f
Fixes Steam launching

I failed to test ELF files that are dynamic with no interpreter here, so
EntryPoint ended up being set to zero, which results in an instant
crash.

Ensure these values are set correctly after the primary ELF and
interpreter are loaded so starting RIP is correct.
2022-09-28 16:00:02 -07:00
Ryan Houdek 1799d4c675 Merge pull request #2038 from lioncash/vneg
IR: Handle 256-bit VNeg
2022-09-28 13:16:55 -07:00
Ryan Houdek 808e1c0330 Merge pull request #2029 from lioncash/interp
Interpreter: Use constant for AVX register size where applicable
2022-09-28 13:16:14 -07:00
Ryan Houdek dacd96cab5 Merge pull request #2037 from lioncash/vfrsqrt
IR: Handle 256-bit VFRSqrt
2022-09-28 13:15:50 -07:00
Ryan Houdek ca4d3bf64d Merge pull request #2036 from lioncash/vfsqrt
IR: Handle 256-bit VFSqrt
2022-09-28 13:14:25 -07:00
Ryan Houdek ea38b043c1 Merge pull request #2035 from lioncash/vfrecp
IR: Handle 256-bit VFRecp
2022-09-28 13:12:57 -07:00
Ryan Houdek a39746df2e Merge pull request #2034 from lioncash/vfmax
IR: Handle 256-bit VFMax
2022-09-28 13:11:04 -07:00
Ryan Houdek 2367a8e50b Merge pull request #2033 from lioncash/vfmin
IR: Handle 256-bit VFMin
2022-09-28 13:10:17 -07:00
Ryan Houdek cb121d7f17 Merge pull request #2032 from lioncash/vaddp
IR: Handle 256-bit VAddP
2022-09-28 13:08:40 -07:00
Ryan Houdek 412793c21d Merge pull request #2030 from lioncash/interp-mov
Interpreter: Handle 256-bit VMov
2022-09-28 13:05:16 -07:00
lioncash ce2286c48b IR: Handle 256-bit VNeg
Extends VNeg to handle 256-bit vectors.
2022-09-28 19:25:33 +00:00
lioncash d5694d6de0 IR: Handle 256-bit VFRSqrt
Extends VFRSqrt to handle 256-bit vectors.
2022-09-28 19:11:01 +00:00
lioncash 121218aa8a IR: Handle 256-bit VFSqrt
Extends VFSqrt to handle 256-bit vectors.
2022-09-28 18:46:43 +00:00
lioncash 6bb53fa758 IR: Handle 256-bit VFRecp
Extends VFRecp to handle 256-bit vectors.
2022-09-28 18:27:34 +00:00
lioncash 89aa0c5471 IR: Handle 256-bit VFMax
Extends VFMax to handle 256-bit vectors.
2022-09-28 17:40:16 +00:00
lioncash 53fcbf6afa IR: Handle 256-bit VFMin
Extends VFMin to handle 256-bit vectors.
2022-09-28 16:56:22 +00:00
lioncash 2f5643ae6b Arm64/VectorOps: Amend half-precision case in VFAddP
Noticed that I forgot to change the Zn register over to VTMP1.
This would have been caught by a vixl internal assert anyway.

Also make the behavior equal with VAddP, where we clear and only copy
over the exact amount of bytes instead of the whole register.
2022-09-28 15:10:49 +00:00
lioncash d162ac8b3d IR: Handle 256-bit VAddP
Extends VAddP to handle 256-bit vectors.
2022-09-28 15:01:31 +00:00
lioncash c9a704fbde Interpreter: Handle 256-bit VMov
Now we'll properly handle the move.

Also put an assert in place to catch any over-sized values.
2022-09-28 13:47:23 +00:00
lioncash 9d5a822a3a Interpreter: Use constant for AVX register size where applicable
Makes the previously implemented ops a little more self-documenting,
and, if we ever actually need to change this, there's a nice constant
that can be looked up instead of magic 32 values.
2022-09-28 13:38:03 +00:00
Ryan Houdek 50eba4066a Merge pull request #2028 from lioncash/vfdiv
IR: Handle 256-bit VFDiv
2022-09-27 13:39:02 -07:00
lioncash 6116ae5330 IR: Handle 256-bit VFDiv
Extends VFDiv to handle 256-bit vectors.
2022-09-27 20:20:31 +00:00
Ryan Houdek 447226576f Merge pull request #2027 from lioncash/vfmul
IR: Handle 256-bit VFMul
2022-09-27 13:18:38 -07:00
Ryan Houdek 3f8b872f17 Merge pull request #2026 from lioncash/vfsub
IR: Handle 256-bit VFSub
2022-09-27 13:17:00 -07:00
Ryan Houdek e573ddc2db Merge pull request #2025 from lioncash/vfaddp
IR: Handle 256-bit VFAddP
2022-09-27 12:56:58 -07:00
lioncash fe9aa681f0 IR: Handle 256-bit VFMul
Extends VFMul to handle 256-bit vectors.
2022-09-27 19:54:57 +00:00
lioncash 1b2f2c1559 IR: Handle 256-bit VFSub
Extends VFSub to be able to handle 256-bit vectors.
2022-09-27 19:33:46 +00:00
lioncash 84c75a86c3 IR: Handle 256-bit VFAddP
Extends VFAddP to be able to handle 256-bit vectors.
2022-09-27 19:03:44 +00:00
Ryan Houdek eedbde6f15 Merge pull request #2024 from lioncash/vfadd
IR: Handle 256-bit VFAdd
2022-09-27 09:53:25 -07:00
Ryan Houdek 4e441e5a08 Merge pull request #2023 from lioncash/vpopcnt
IR: Handle 256-bit VPopcount
2022-09-27 09:51:27 -07:00
Ryan Houdek 3e287a36c2 Merge pull request #2022 from lioncash/vabs
IR: Handle 256-bit VAbs
2022-09-27 09:48:26 -07:00
lioncash eadc477695 IR: Handle 256-bit VFAdd 2022-09-27 16:07:42 +00:00
lioncash 59aa324678 IR: Handle 256-bit VPopcount
Extends VPopcount to be able to handle 256-bit width vectors
2022-09-27 15:19:24 +00:00
lioncash 219bce1467 IR: Handle 256-bit VAbs
Extends VAbs to be able to handle 256-bit width vectors.
2022-09-27 13:56:38 +00:00
Ryan Houdek 46bde401bd Merge pull request #2019 from Sonicadvance1/remove_mov
IR: Removes Mov IR op
2022-09-26 19:49:52 -07:00
Ryan Houdek 01beac4956 Merge pull request #2018 from Sonicadvance1/remove_vextractelement
IR: Removes VExtractElement
2022-09-26 19:49:46 -07:00
Ryan Houdek fcd981e6b7 Merge pull request #2017 from Sonicadvance1/remove_vbitcast
IR: Removes unnecessary VBitcast IR op
2022-09-26 19:44:12 -07:00
Ryan Houdek 825833cfcc IR: Removes Mov IR op
This is unused and shouldn't ever be used.
2022-09-26 16:04:27 -07:00
Ryan Houdek 4a4c49bf68 IR: Removes VExtractElement
This is a duplicate of VDupElement since AArch64 doesn't support an
element extract plus zero of the rest of the register.

Removes and replaces its uses with VDupElement.
2022-09-26 15:55:41 -07:00
Ryan Houdek 763cea423a IR: Removes unnecessary VBitcast IR op
This instruction was purely a move that did format reinterpretation.
This was necessary with LLVM when we had implicit IR op sizes.

Now that all vector ops are explicitly sized, this is not only
redundant, but also completely unnecessary sicne we don't support LLVM
anymore.

Remove the op, which technically is a very minor optimization for the
two instructions that still used it.
2022-09-26 15:42:24 -07:00
Ryan Houdek 0fee355ff5 Merge pull request #2016 from lioncash/pred
Arm64/VectorOps: Make use of static predicate registers
2022-09-26 14:48:26 -07:00
Ryan Houdek 6f6f3c9dc5 Merge pull request #2012 from Sonicadvance1/32bit_vdso
32-bit VDSO support
2022-09-26 14:46:27 -07:00
Ryan Houdek 25e5d88ab2 VDSO Emulation: Wires up support for 32-bit VDSO 2022-09-26 14:35:38 -07:00
Ryan Houdek 87013340bb Thunks: Adds support for building 32-bit. Only VDSO for now. 2022-09-26 14:35:38 -07:00
Ryan Houdek 47c075ccc9 Thunks/VDSO: Add 32-bit linker script 2022-09-26 14:35:38 -07:00
Ryan Houdek b1a32d4ccf Thunks: Ensure fexthunks functions are hidden visible by default 2022-09-26 14:35:38 -07:00
Ryan Houdek 7af6a8dbdf Thunks/VDSO: Extend to support clock_gettime64 2022-09-26 14:35:38 -07:00
Ryan Houdek 383e99e4ef unittests: Extend VDSO test for gettime64 2022-09-26 14:35:38 -07:00
Ryan Houdek 8c7cfc4d11 Config: Adds support for unique 32-bit GuestThunk path 2022-09-26 14:01:48 -07:00
lioncash 496ee730c8 Arm64/VectorOps: Make use of static predicate registers
Since PR #2003, we now set up some predicate registers within
FillStaticRegs. We can now make use of those instead of manually setting
up predicate registers inside the IR opcodes.
2022-09-26 13:20:01 +00:00
Ryan Houdek 71f7ff5101 Merge pull request #2014 from Sonicadvance1/map_interp_first
ELFCodeLoader: Map interpreter first
2022-09-26 02:42:39 -07:00
Ryan Houdek 1ea00f68a2 Merge pull request #2010 from Sonicadvance1/add_support_for_32_bit_thunk_op
Thunks: Implement the Thunk IR op for 32-bit mode
2022-09-26 02:33:47 -07:00
Ryan Houdek 8df7c2d84f ELFCodeLoader: Map interpreter first
This more closely matches behaviour of the kernel.
Provides an example in source to ensure we don't break it in the future.
2022-09-25 18:07:31 -07:00
Ryan Houdek 46557a7a1f ELFCodeloader: Map once and then use MAP_FIXED to overwrite
Instead of mapping to find a range, unmapping, and then submapping
inside of it.

Map once, then mmap with MAP_FIXED to overwrite the mapping.
This fixes an issue where if you enabled ASAN then it would stick
additional mappings inbetween where we want to map. Thus breaking asan.
2022-09-25 17:50:16 -07:00
Ryan Houdek d8c2a8271f Merge pull request #2009 from Sonicadvance1/libvulkan_fix_print
Thunks/libvulkan: Fixes print for 32-bit
2022-09-25 13:33:00 -07:00
Ryan Houdek cc4c705fc0 Merge pull request #2008 from Sonicadvance1/disable_32_bit_x11
ThunkLibs: X11/Xext: Removes two functions that don't exist on 32-bit
2022-09-25 13:32:54 -07:00
Ryan Houdek 22f249fcf6 Thunks: Implement the Thunk IR op for 32-bit mode
Use the fastcall ABI for 32-bit x86 to make our lives easier.
Fastcall ABI puts the first two 32-bit arguments in ECX and EDX
respectively.

Compilers are nice today and allow us to do cross-abi function calls
like this.
2022-09-25 13:16:30 -07:00
Mai c262362a03 Merge pull request #2011 from Sonicadvance1/add_missing_flake
FEXLinuxTests: Adds missing pthread_cancel flake status
2022-09-24 20:53:22 -04:00
Ryan Houdek 212df9aa7b FEXLinuxTests: Adds missing pthread_cancel flake status
Missed the 32-bit version of this test
2022-09-24 10:05:36 -07:00
Ryan Houdek 691e39ec76 Thunks/libvulkan: Fixes print for 32-bit
Value passed in to this print will be 32-bit or 64-bit depending on
arch.

Noticed this while tinkering around and is easy enough to solve today.
2022-09-24 09:56:50 -07:00
Ryan Houdek 107cae2975 ThunkLibs: X11/Xext: Removes two functions that don't exist on 32-bit
_XData32 and _XRead32 don't exist as real functions in 32-bit versions
of these libraries, these end up just being defines that redirect to the
non-suffixed versions of the functions.

Noticed this while tinkering around and is easy enough to solve today.
2022-09-24 09:51:40 -07:00
Ryan Houdek 6742e0c376 Merge pull request #2003 from lioncash/svespill
JITs: Handle spilling/filling 256-bit vectors
2022-09-23 17:27:53 -07:00
Ryan Houdek 8f70137b1a Merge pull request #1981 from neobrain/feature_flt_catch2
FEXLinuxTests: Migrate to Catch2
2022-09-23 17:27:45 -07:00
lioncash 707db51b1b Arm64Emitter: Amend comment for GPR temporaries
Only x3 can be used across spill boundaries.
2022-09-24 00:12:01 +00:00
lioncash 5b5fa1aa29 x86_64/JIT: Handle pushing and popping 256-bit values 2022-09-24 00:11:56 +00:00
Ryan Houdek 2b9cc9666a Merge pull request #2006 from Sonicadvance1/remove_splat
IR: Removes SplatVector{2,4}
2022-09-23 14:42:44 -07:00
Ryan Houdek 82eba22292 Merge pull request #2007 from Sonicadvance1/remove_insscalar
IR: Removes VInsScalarElement
2022-09-23 14:42:36 -07:00
Ryan Houdek 5c84e8f23c IR: Removes VInsScalarElement
This IR op duplicates what VInsElement does.
2022-09-22 17:57:20 -07:00
Ryan Houdek 081b61677a IR: Removes SplatVector{2,4}
These IR ops are redundant and mostly unused.
VDupElement does exactly what these operations were already doing and
more closely matches what the hardware wants.
2022-09-22 17:49:28 -07:00
lioncash 9c54814b98 Arm64Emitter: Handle spilling 256-bit dynamic regs 2022-09-22 12:40:54 +00:00
lioncash 35d7b855ed Arm64Dispatcher: Increment code buffer size
vixl hits an assertion in CodeBuffer's Emit() function since there's no
space for any more instructions with the changes made to handle SVE.
2022-09-22 12:40:54 +00:00
lioncash 0b8799274c Arm64Emitter: Handle filling/spilling 256-bit static FPRs
Drops in handling for spilling/filling FPRs using SVE for supporting
AVX.

Also alters the dispatcher and JIT a little to avoid accidentally clobbering
TMP4 (x3 as of this commit)
2022-09-22 12:40:54 +00:00
lioncash ace2b737d8 Arm64Emitter: Initialize fixed predicate register values in FillStaticRegs
Allows us to have values set up in a way that we don't need to
constantly set up predicates in IR ops.
2022-09-22 12:40:54 +00:00
Mai ad85268524 Merge pull request #2005 from Sonicadvance1/fix_sve_vectorimm
Arm64: Fixes SVE VectorImm
2022-09-22 08:38:10 -04:00
Mai 832a320e22 Merge pull request #2004 from Sonicadvance1/update_vixl3
Update vixl external
2022-09-22 08:33:39 -04:00
Ryan Houdek 83763df6fd Arm64: Fixes SVE VectorImm
SVE DUP instruction does sign extension on the incoming immediate, while
ASIMD MOVI does zero extension.

If the immediate doesn't fit then move in to a GPR first and then DUP
from GPR.
2022-09-22 01:16:58 -07:00
Ryan Houdek bee868e9ba Update vixl external 2022-09-22 01:10:25 -07:00
Tony Wasserka c41de81694 FEXLinuxTests: Drop support for now unused "args:" annotations 2022-09-22 10:03:38 +02:00
Tony Wasserka 41aaeb1ff0 FEXLinuxTests: Migrate signal tests to Catch2 2022-09-22 10:03:37 +02:00
Tony Wasserka 16be2792ab FEXLinuxTests: Migrate FD test to Catch2 2022-09-22 10:03:36 +02:00
Tony Wasserka 6610bb355c FEXLinuxTests: Migrate VDSO test to Catch2 2022-09-22 10:03:35 +02:00
Tony Wasserka 4312fd7291 FEXLinuxTests: Migrate SMC tests to Catch2 2022-09-22 10:03:33 +02:00
Tony Wasserka 89a225a96d FEXLinuxTests: Enable use of Catch2 in tests 2022-09-22 10:03:31 +02:00
Ryan Houdek a590977639 Merge pull request #1984 from Sonicadvance1/functional_thunk_ci
Thunks: Adds functional thunk testing to CI
2022-09-20 11:14:03 -07:00
Ryan Houdek 0d0d116bde Merge pull request #2002 from lioncash/slots
JITs: Expand max spill slot size to 32 bytes
2022-09-19 15:22:25 -07:00
lioncash 341bdb5a54 JITs: Handle 32 byte spills and fills
Puts in the plumbing necessary to handle spilling and filling 256-bit
data.
2022-09-19 22:00:20 +00:00
lioncash 2f3dbfb289 JITs: Expand max spill slot size to 32 bytes
This will be necessary to handle spilling 256-bit vectors.
2022-09-19 19:51:32 +00:00
Ryan Houdek 169cfbbeed Merge pull request #2001 from lioncash/defmove
Arm64: Centralize location for register defines
2022-09-19 11:45:24 -07:00
Ryan Houdek f97a4afd8f Merge pull request #2000 from Sonicadvance1/fix_struct_verifier_ubuntu_20_04
CI: Fixes struct verifier on Ubuntu 20.04
2022-09-19 11:44:38 -07:00
lioncash 868e4a6d81 Arm64: Centralize location for register defines
Gets rid of a few repeated definitions and allows the emitter itself to
make use of these defines without causing a circular dependency on the
JIT.
2022-09-19 17:44:21 +00:00
Ryan Houdek 6f48f7d3ac CI: Fixes struct verifier on Ubuntu 20.04
Older clang fails to pull in these include paths when cross compiling.
Add them manually.
2022-09-19 01:27:54 -07:00
Tony Wasserka 1ed3ecb409 Merge pull request #1999 from neobrain/feature_toolchain_32bit
CMake: Add toolchain file for 32-bit cross-compiler
2022-09-19 09:30:57 +02:00
Tony Wasserka 2cb455b9d4 CMake: Add toolchain file for 32-bit cross-compiler 2022-09-19 09:19:22 +02:00
Ryan Houdek d4b5bf0f78 Merge pull request #1998 from Sonicadvance1/fix_struct_verifier
StructVerifier: Fixes CI failure
2022-09-18 19:26:21 -07:00
Ryan Houdek 69013772c1 StructVerifier: Fixes CI failure
The x86 runner had unattended-upgrades accidentally still enabled. It
upgraded a bunch of development packages which broke CI.

Fix the struct verifier so it works with the new packages.
Sadly python3-clang doesn't support all the new CursorKind types so we
need to self-define some of them for now.

Once this tool gets converted over to C++ it will be a non-issue.
2022-09-16 19:45:13 -07:00
Ryan Houdek 3448c83431 Merge pull request #1997 from neobrain/refactor_flt_unified_cmake
FEXLinuxTests: Build 32-bit and 64-bit test variants separately
2022-09-16 17:14:24 -07:00
Tony Wasserka e4c84542ea FEXLinuxTests: Build 32-bit and 64-bit test variants separately
This allows to use different toolchain files for each and it reduces
build system repetition in test target setup.

The "tests-32" directories has been integrated into the "tests" one. Tests
that should only run on 32-bit are detected by their filename ending with
".32.cpp" now.
2022-09-16 11:24:18 +02:00
Ryan Houdek acddc0323b Merge pull request #1995 from Sonicadvance1/disable_bad_test
unittests: Disable gvisor pselect test
2022-09-15 22:06:37 -07:00
Ryan Houdek c4285f0d30 unittests: Disable gvisor pselect test
This has a badly coded test that can hang forever. Our timeout kills it
at 5 minutes, which causes it to not even fall down the flake path.

Just disable it outright because of the bad test.
2022-09-15 16:29:02 -07:00
Ryan Houdek 977d6dd247 Merge pull request #1993 from lioncash/vuravg
VectorOps: Handle 256-bit VURAvg
2022-09-15 15:09:46 -07:00
lioncash 2bb27fffb7 VectorOps: Handle 256-bit VURAvg 2022-09-15 19:38:31 +00:00
Ryan Houdek 0261ed353d Merge pull request #1992 from lioncash/uminv
VectorOps: Handle 256-bit VUMinV
2022-09-15 12:05:27 -07:00
Ryan Houdek 96fecfd7c5 Merge pull request #1989 from Sonicadvance1/ci_flakes
CI: Adds support for flakes
2022-09-15 12:05:08 -07:00
Ryan Houdek 9fac1b8105 CI: Adds support for flakes
If a test is marked as a flake then it will be tried five times before
giving up.

Works around the problem of needing to babysit CI once a PR is pushed.
As long as we have all the flake tests marked.
2022-09-15 11:44:56 -07:00
lioncash 95fbcd7b9a VectorOps: Handle 256-bit VUMinV 2022-09-15 18:36:46 +00:00
Ryan Houdek 6adf227611 Merge pull request #1990 from Sonicadvance1/uninstall
cmake: Adds uninstall target
2022-09-15 11:33:27 -07:00
Ryan Houdek b5cb429243 Merge pull request #1991 from lioncash/bits
Interpreter: Handle 256-bit VAnd/VBic/VOr/VXor
2022-09-15 11:23:49 -07:00
Ryan Houdek 26ba8079a3 cmake: Adds uninstall target
Following guidance from cmake's FAQ:
https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake

Due to some of the special handling that we do with installs, we need to
do additional uninstall handling that the install manifest doesn't cover.

Specifically we need to add additional uninstall targets for:
- FEXInterpreter
- binfmt_misc
- guest_thunks (Doing its own uninstall target, so passthrough)

While it isn't generally advised to install and uninstall through source
systems, this is something that users want to do all the time.
This has been asked for a couple of times now.

Fixes #1592
2022-09-15 11:22:24 -07:00
lioncash f999d30bc5 Interpreter: Handle 256-bit VAnd 2022-09-15 16:18:14 +00:00
lioncash ecb1cc4ed4 Interpreter: Handle 256-bit VBic 2022-09-15 16:18:14 +00:00
lioncash 5622bcae16 Interpreter: Handle 256-bit VOr 2022-09-15 16:18:14 +00:00
lioncash f4539ee289 Interpreter: Handle 256-bit VXor 2022-09-15 16:18:10 +00:00
Ryan Houdek d2138694b4 Merge pull request #1986 from neobrain/refactor_flt_cmake_cleanup
FEXLinuxTests: Use the build system instead of setting up compile flags via source-code annotations
2022-09-15 01:14:08 -07:00
Tony Wasserka 1767e21273 FEXLinuxTests: Use the build system instead of setting up compile flags via source-code annotations
The intent of these annotations was presumably to make it easier to adjust
build settings on a per-test basis, but doing this in the build system is
actually much cleaner.
2022-09-15 08:55:18 +02:00
Ryan Houdek 8f9d799342 Merge pull request #1988 from Sonicadvance1/fexserver_wait_old_kernel
FEXServer: Fix waiting on kernel version older than 5.3
2022-09-14 17:07:41 -07:00
Ryan Houdek a3b0b246f4 FEXServer: Fix waiting on kernel version older than 5.3
pidfd_open was added in kernel 5.3 so older kernel devices weren't able
to use `FEXServer -w`. If the syscall doesn't give us an FD to the
process, then use a pipe instead.

Since we are only polling for the FD to hangup this works for us.
2022-09-14 16:04:26 -07:00
Ryan Houdek dee85f14fe Merge pull request #1987 from Sonicadvance1/fix_fhu_syscalls
FHU: Convert to a interface target
2022-09-14 15:55:04 -07:00
Ryan Houdek 27309114be FHU: Convert to a interface target
Noticed recently that `FEXServer -w` was broken and couldn't understand
why. Turns out that FHU syscall handling was /always/ falling down the
`#else` path in the handlers since cmake `add_definitions` follows
folder scoping rules.

This means it was always returning -1, which was causing FEXServer's
pidfd_open usage to always receive -1, which meant the sendmsg with FD
was always failing, which meant the `FEXServer -w` would forever wait
for a message that was never sent.

Converting the utility over to a target not only fixes definition
scoping problems, but also makes the other paths actually work.

This found some compiling bugs and instead lets us define SYS_pidfd_open
if it doesn't exist. Letting the kernel return the ENOSYS if it doesn't
exist on that platform.

Main thing, fixes FEXServer -w hanging forever.
2022-09-14 14:58:23 -07:00
Ryan Houdek 704afed97b Merge pull request #1985 from neobrain/refactor_thunkgen_fmt
Thunks/gen: Use fmt for writing formatted output
2022-09-14 13:52:15 -07:00
Ryan Houdek 121f0a2c6c CI: FetchRootFS More robust rootfs fetching
Permissions mean we need to delete the folder before extracting.
On error make sure to delete the image file as well to ensure it reruns
everything.
2022-09-14 13:18:24 -07:00
Ryan Houdek 1c580ec92c Thunks: Adds functional thunk testing to CI
This is the bare minimum, it only tests glxinfo and vulkaninfo with and
without thunks. Nothing more special than that. Already found the .1 bug
with libvulkan host library loading.
2022-09-14 12:48:13 -07:00
Tony Wasserka ab8fc721a0 Thunks/gen: Use fmt for writing formatted output 2022-09-14 11:56:15 +02:00
Ryan Houdek 790447115c CI: Set CMAKE_INSTALL_PREFIX
This will be used in the next commit
2022-09-13 17:21:03 -07:00
Ryan Houdek 80abeac28a Thunks: Fixes a missing version number on libvulkan
Fixes an issue with loading libvulkan without development packages.
2022-09-13 17:20:06 -07:00
Ryan Houdek 54915f87ce Merge pull request #1978 from neobrain/refactor_astvisitor_to_frontendaction
Move thunk generator logic from ASTVisitor to ASTFrontendAction
2022-09-13 11:26:36 -07:00
Ryan Houdek f34f1309a7 Merge pull request #1983 from lioncash/vsqadd
VectorOps: Extend VSQAdd/VSQSub/VUQAdd/VUQSub
2022-09-13 11:26:11 -07:00
Ryan Houdek 0ad52b7d19 Merge pull request #1982 from lioncash/vadd
VectorOps: Extend VAdd/VSub
2022-09-13 11:24:39 -07:00
lioncash 809f60df06 VectorOps: Handle 256-bit VSQSub 2022-09-13 16:43:42 +00:00
lioncash cbdcd8253c VectorOps: Handle 256-bit VSQAdd 2022-09-13 16:20:37 +00:00
lioncash 7ac2cd7cc8 VectorOps: Handle 256-bit VUQSub 2022-09-13 16:03:30 +00:00
lioncash cf1bb1348c VectorOps: Handle 256-bit VUQAdd 2022-09-13 16:03:27 +00:00
lioncash b60a26ff9e VectorOps: Handle 256-bit VSub 2022-09-13 15:40:45 +00:00
lioncash b805c07342 VectorOps: Handle 256-bit VAdd 2022-09-13 15:40:06 +00:00
Mai 8d69f539ac Merge pull request #1979 from Sonicadvance1/fix_thunkconfig
FEXConfig: Ensure APP_CONFIG_NAME isn't stored in json
2022-09-13 11:38:24 -04:00
Ryan Houdek c8c0054f67 FEXConfig: Ensure APP_CONFIG_NAME isn't stored in json
Also in FEXLoader make sure to use `EraseSet` for these runtime options.

Fixes a bug where the config was being set to nothing, breaking the
ThunksDB configuration option.
2022-09-13 00:08:20 -07:00
Tony Wasserka 1085385bbe Thunks/gen: Move logic from ASTVisitor to ASTFrontendAction
ASTVisitor is great for iterating over AST nodes by type, but most of our
analysis is based on symbol names. For this task, a lookup in DeclContexts
after parsing is complete is better suited.
2022-09-12 18:52:33 +02:00
Tony Wasserka 56460b220c Thunks/gen: Move definition of GenerateThunkLibsAction into gen.cpp 2022-09-12 18:52:33 +02:00
Mai a583ebe590 Merge pull request #1977 from Sonicadvance1/extend_arch_check
CMake: Extend AArch64 check to include arm64
2022-09-09 22:36:30 -04:00
Ryan Houdek ce8175a800 CMake: Extend AArch64 check to include arm64
This has been seen in some build environments, forgot to commit this a
while ago.
2022-09-08 15:34:35 -07:00
Mai c987e1ef44 Merge pull request #1974 from Sonicadvance1/update_release_process
Docs: Update Release docs
2022-09-08 11:48:41 -04:00
Mai b36ec152d2 Merge pull request #1976 from Sonicadvance1/support_simulator
Add support for the vixl simulator
2022-09-08 11:47:46 -04:00
Ryan Houdek 44c62e703e github: Adds vixl simulator CI 2022-09-07 20:08:50 -07:00
Ryan Houdek 0f59c1d5e3 Add support for the vixl simulator
This will allow CI to test ARM features before we have any hardware that
supports it.
2022-09-07 19:54:07 -07:00
Ryan Houdek 5739f0b459 Update vixl 2022-09-07 19:10:13 -07:00
Ryan Houdek 4145fabfb6 Docs: Update Release docs
Reorder PPA building to be after the github tag. PPA takes a while to
run, so good to get it out of the way up front so it can be handled in
the background while doing the rest of the release.

Also update the link which was renamed.
2022-09-05 11:08:44 -07:00
Ryan Houdek ae34b1e521 Docs: Update for release FEX-2209 2022-09-05 10:32:07 -07:00
Ryan Houdek c17da25617 Merge pull request #1973 from neobrain/refactor_single_thunkgen_output
Thunks: Consolidate all generated code to one file per library per platform
2022-09-05 10:20:54 -07:00
Tony Wasserka cc8ef16240 Thunks/gen: Consolidate all generated code to one file per library per platform 2022-09-05 15:03:49 +02:00
Tony Wasserka 30fac81c41 Thunks/gen: Remove unused symtable generation code 2022-09-05 15:03:49 +02:00
Ryan Houdek bbcca80b60 Merge pull request #1971 from Sonicadvance1/termux_namespace_collision
Syscalls: Use underscored shm syscall names
2022-09-05 02:55:29 -07:00
Ryan Houdek 56ae3716bb Syscalls: Use underscored shm syscall names
Removes us needing to carry a patch downstream in the termux package
repo.
2022-09-05 02:39:42 -07:00
Ryan Houdek 2fba3a4a9a LinuxAllocator: Uppercase function names
Not only does this match our internal naming convention. This avoids the
shm symbol namespace collision as well.
2022-09-05 02:39:42 -07:00
Ryan Houdek 8ba0312d40 Syscalls: Prefix _ to shm syscall names to avoid namespace conflict
Termux uses defines for these, so our token pasting fails, but we also
still want to use their define so we can fall down their emulation
library whenever possible.

Prefix an underscore to be able to use both our number definitions and
their defines in the same file.
2022-09-05 02:39:42 -07:00
Ryan Houdek 53623ffa72 Merge pull request #1941 from Sonicadvance1/vdso_implementation
Thunks: Adds VDSO thunk library
2022-09-04 23:02:49 -07:00
Mai d6199687f4 Merge pull request #1970 from Sonicadvance1/fix_fresh_runner
Github: Fix fresh runner rootfs checkout
2022-09-02 21:49:22 -04:00
Ryan Houdek 7cb413dde6 Github: Fix fresh runner rootfs checkout
build folder doesn't exist on a freshly started runner.
Executing the rootfs fetch script doesn't care what the working
directory is. Doesn't need to be in `build/` which doesn't exist on a
fresh runner and will fail chdir.
2022-09-02 15:15:21 -07:00
Ryan Houdek c5a7fc1e2a Resolve most VDSO comments 2022-09-02 15:13:18 -07:00
Ryan Houdek 0869b0aa29 FEXLinuxTests: Adds a VDSO test
Ensures VDSO is working as correctly as it can.
2022-09-02 13:31:36 -07:00
Ryan Houdek 4e81f847f2 ELFCodeLoader: Load VDSO thunk at application startup
This ensures it will always be available to the application as long as
the library is installed.
2022-09-02 13:31:36 -07:00
Ryan Houdek 8fa27d2d9f Thunks: Adds VDSO thunk library
VDSO is heavily abused by Proton games to the point it is showing up as
CPU time.
Implement a guest-facing only thunk library using the hardcoded VDSO
interface in Thunks.

If available this will always be loaded on application load and set the
auxv value to support it.

This requires a bit of special treatment as our first user of linker
scripts since the format of the ELF must be careful crafted to not break
applications trying to parse it.

This library exposes a handful of symbols:
- clock_gettime
- clock_getres
- gettimeofday
- time
- getcpu
- All previous with `__vdso_` prefix
- LINUX_2.6

All of these symbols get routed directly to the host architecture VDSO
interface if they exist.
AArch64 doesn't have getcpu or time VDSO.

In a microbench, VDSO improved bench times substantially
x86-64 host: 3.612s -> 1.369s - 2.63x speed
AArch64 host: 3.821s -> 2.284s - 1.67x speed
  - AArch64 isn't as improved due to missing VDSO symbols

This is also our first /always/ enabled thunk as long as the file exists
2022-09-02 13:31:36 -07:00
Ryan Houdek e5a8a29efe Thunks: Add support for lds linker script on Guest libraries
This is going to be necessary in the next commit
2022-09-02 13:31:36 -07:00
Ryan Houdek f967f53176 Thunks: Adds VDSO specific thunks
x86-64 has five symbols within VDSO that we need to emulate.

Pass this through either glibc or host vdso if the symbol exists.
AArch64 doesn't have the time or getcpu vdso interface, so fall down
glibc instead.
2022-09-02 13:31:36 -07:00
Ryan Houdek 31fefaae0d Merge pull request #1969 from Sonicadvance1/fexrootfsfetcher_fix_crash
FEXRootFSFetcher: Fix crash if curl fails to download rootfs definition file
2022-09-02 11:07:03 -07:00
Ryan Houdek 7f9edbf39e Merge pull request #1968 from Sonicadvance1/new_domain
New domain.
2022-09-02 10:57:35 -07:00
Ryan Houdek 760b9c8e7f FEXRootFSFetcher: Fix crash if curl fails to download rootfs definition file
This can happen when the link goes down or internet blip.
2022-09-02 10:53:51 -07:00
Ryan Houdek cc7fb008fc New domain.
Needed to fix FEXRootFSFetcher from #1967
2022-09-02 10:43:07 -07:00
Ryan Houdek 98dbfbe654 Merge pull request #1949 from lioncash/interp-op
InterpreterOps: Extend SSAData size to accomodate 256-bit operations
2022-09-02 09:54:58 -07:00
lioncash 6444726614 InterpreterOps: Use designated initializer for IR op data
Same behavior, but keeps everything all initialized at the point of
declaration, rather than after the fact.
2022-09-02 12:40:25 -04:00
lioncash 0a562fbb10 InterpreterOps: Extend SSAData to handle 256-bit vectors 2022-09-02 12:40:14 -04:00
Ryan Houdek 7d8950de40 Merge pull request #1966 from lioncash/bool
Arm64/JIT: Rename CanUseSVE to HostSupportsSVE
2022-09-01 13:13:00 -07:00
lioncash 3fdde0c90b Arm64/JIT: Rename CanUseSVE to HostSupportsSVE
This is a much more descriptive name.

Spawned off of discussion in #1944
2022-09-01 13:22:41 -04:00
Ryan Houdek e776f4cd4e Merge pull request #1948 from lioncash/svebit
VectorOps: Extend VAnd/VBic/VOr/VXor
2022-08-30 17:57:51 -07:00
Ryan Houdek e7d7dd13d7 Merge pull request #1945 from lioncash/vectormov
VectorOps: Extend VMov
2022-08-30 17:57:00 -07:00
Ryan Houdek d5c83a2e45 Merge pull request #1950 from lioncash/x86run
HostRunner: Handle upper YMM lanes in sigsegv handler
2022-08-30 17:55:38 -07:00
Ryan Houdek 37ccb13917 Merge pull request #1946 from lioncash/x86dep
x86_64/JIT: Resolve lingering fmt deprecation warning
2022-08-30 17:54:57 -07:00
Ryan Houdek 8439cf410f Merge pull request #1944 from lioncash/sveimm
VectorOps: Extend VectorImm
2022-08-30 17:54:32 -07:00
lioncash 614d9448f7 HostRunner: Handle upper YMM lanes in sigsegv handler
Now we properly copy out the upper lanes instead of ignoring them.
2022-08-25 14:13:19 -04:00
lioncash 70efdbba5d VectorOps: Handle 256-bit VectorImm
Extends VectorImm to be capable of using SVE to handle 256-bit length
vectors.
2022-08-24 13:28:48 -04:00
Stefanos Kornilios Mitsis Poiitidis 12fee91ebb Merge pull request #1947 from neobrain/fix_tmpnam_warning
unittests/ThunkLibs: Fix warning about "dangerous" use of tmpnam
2022-08-24 20:13:04 +03:00
lioncash bcb7e20619 VectorOps: Handle 256-bit VXor 2022-08-24 12:52:05 -04:00
lioncash afc5e8a140 VectorOps: Handle 256-bit VOr 2022-08-24 12:52:05 -04:00
lioncash 4be6626c89 VectorOps: Handle 256-bit VBic 2022-08-24 12:52:02 -04:00
lioncash 5f2b6d629b VectorOps: Handle 256-bit VAnd 2022-08-24 12:43:51 -04:00
Tony Wasserka 1b8f5f08f0 unittests/ThunkLibs: Fix warning about "dangerous" use of tmpnam
tmpnam is considered insecure since it's vulnerable to TOCTOU issues.
This is not an issue for these tests, but replacing tmpnam is not any
more complicated than silencing the warning.
2022-08-24 18:15:51 +02:00
lioncash 79674c697a x86_64/JIT: Resolve lingering fmt deprecation warning
Just a log that was missed during the previous fmt deprecation cleanup.
2022-08-24 11:49:02 -04:00
lioncash 416d8c1df6 VectorOps: Handle 256-bit VMov
Kind of sucky that SVE doesn't have a convenient way to manipulate
predicate registers with immediates or anything to make this nicer (that
I know of).

Having to use a temp to clear the upper part of the vector reliably is
bleh.
2022-08-24 11:35:34 -04:00
Ryan Houdek d03b6a9382 Merge pull request #1942 from lioncash/zero
VectorOps: Extend VectorZero
2022-08-23 20:00:39 -07:00
lioncash df22e0c796 x86_64/JITClass: Add ToYMM helper
Will be used in subsequent changes to handle 256-bit operations in the
x86-64 backend
2022-08-23 12:58:25 -04:00
lioncash 79a3bd75cc VectorOps: Handle 256-bit VectorZero 2022-08-23 12:57:59 -04:00
Stefanos Kornilios Mitsis Poiitidis e6acdcc583 Merge pull request #1940 from neobrain/refactor_1868_cleanups
Thunks: Minor cleanups for signature-based function pointer thunking
2022-08-23 01:08:51 +03:00
Tony Wasserka cd78984228 Thunks: Define _M_X86_64/_M_ARM_64 when invoking thunkgen
This avoids the need to provide a fallback definition for platform-specific
macros. The definitions are only added host-side, since only Host.h is
included in any interface files.
2022-08-22 18:11:28 +02:00
Tony Wasserka 2f007c5f0a Thunks: Remove unused parameters of exports initializer 2022-08-22 18:11:28 +02:00
Tony Wasserka ae64a1e30c Thunks: Replace compiler-specific attributes with FEX_DEFAULT_VISIBILITY 2022-08-22 18:11:28 +02:00
Stefanos Kornilios Mitsis Poiitidis 097184c3e0 Merge pull request #1926 from Sonicadvance1/no_irloader_on_no_tests
IRLoader/TestHarnessLoader: Don't build if not building tests
2022-08-21 09:50:44 +03:00
Stefanos Kornilios Mitsis Poiitidis 84a95adae1 Merge pull request #1931 from Sonicadvance1/support_thunksdb_in_config
Thunks: Support direct thunk config in configuration files
2022-08-21 09:50:08 +03:00
Stefanos Kornilios Mitsis Poiitidis 123b6728e4 Merge pull request #1932 from Sonicadvance1/fix_allocator_perf_hit
64BitAllocator: Fixes a significant state tracking perf problem
2022-08-21 09:47:54 +03:00
Ryan Houdek 69b4fc98ed 64BitAllocator: Fixes a significant state tracking perf problem
Due to how the 64-bit allocator previously worked, it was never subjected to memory
regions larger than 64GB to be tracked. With the change in
PR #1885, this has changed to have regions that will hit sizes larger
than 170TB on some platforms.

Better yet, even with smaller regions it still had a performance issue,
it just wasn't as visible.

First problem: We used MemSet instead of MemClear for the live page
clearing. This caused pages to be claimed as "always in use".

This would cause us to always scan the entire region on allocation, find
that it didn't work and allocate a fresh region on every slab
allocation.
jemalloc saving us here since it allocates slabs from the OS fairly
aggressively.

Second problem: We used MemSet (now changed to MemClear) to "clear" the
state tracking for pages.

This causes ~600MB of memory to be used purely for state tracking.
This was physically backed since we were writing to every bit of
tracking for handling 256TB of VA.
This had a fault dance with the kernel for every new page being hit
here.
Instead of clearing the the bits with a memset, clear it with madvise so
it doesn't consume physical pages at all.

This means we use significantly less physical memory for 32-bit
applications.

With this change, pressure-vessel startup time goes from 24 seconds down
to 17 seconds. 70% of the original startup time.
But really the main savings here comes from the memory reduction that
PR #1885 ballooned, but has been an unseen problem before.

Before that PR we were burning 2MB of physical memory per region for no
reason.
After that PR we were burning up to 600MB of physical memory per region
for no reason. Changing a bit depending on how large the region ended up
being.

This now ends up being 2 pages starting out and grows as more pages are
are used. A significant improvement.
2022-08-20 15:32:40 -07:00
Ryan Houdek 35cf7703b1 Thunks: Support direct thunk config in configuration files
Previously in order to enable thunks, we needed an independent
description file of which thunks to be enabled. This is nice for quickly
testing out new games by setting `FEX_THUNKCONFIG` environment variable.

For users that just want to enable thunks this is an unwieldy
indirection that doesn't make much sense at a glance.

Previously this meant you needed two files as an example:
```
  ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat thunks.json
  {
    "ThunksDB": {
      "GL": 1,
      "Vulkan": 1
    }
  }
  ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat AppConfig/EnderLiliesSteam-Linux-Shipping.json
  {
    "Config": {
      "ThunkConfig":"~\/.fex-emu\/thunks.json"
    }
  }
```

Instead of this unwieldy redirection just support `ThunksDB` json
directly in the AppConfig.

```
  ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat AppConfig/EnderLiliesSteam-Linux-Shipping.json
  {
    "Config": {
      <...>
    },
    "ThunksDB": {
      "GL": 1,
      "Vulkan": 1
    }
  }
```

As can be seen this makes this significantly easier for new users
getting in to thunks. Depending on which path to enable thunks the user
is more comfortable with, they can still enable them using the
`ThunkConfig` option or embedding directly in the application
configuration.

Additionally this removes the older non-ThunksDB path to loading thunks.
All users of it have moved on to using ThunksDB.
2022-08-20 10:02:13 -07:00
Ryan Houdek 8c1137543b Config: Adds APP_CONFIG_NAME meta config option
We have separate configurations for the Application path versus the
application name we are using as a configuration choice.

Example 1: FEXBash "wine Crysis64.exe"

Previous APP_FILENAME will contain `/usr/bin/wine`, which is still used
elsewhere.

This new APP_CONFIG_NAME will contain `Crysis64.exe`

Example 2: FEXBash glxgears

Previous APP_FILENAME will contain `/usr/bin/glxgears`
APP_CONFIG_NAME will contain `glxgears`

We didn't have this exposed any other way before.
2022-08-20 09:58:23 -07:00
Ryan Houdek b8e66e56a0 Config: Remove log message about Config file existing without Config json object 2022-08-20 09:58:04 -07:00
Ryan Houdek fbb008e510 Merge pull request #1929 from Sonicadvance1/support_variadic_struct_packing
Thunks/X11: Support Variadic stack packing
2022-08-20 06:31:22 -07:00
Ryan Houdek 04678f8404 Merge pull request #1885 from FEX-Emu/skmp/simpler-memory-stealing
Allocator: Simplify StealMemory, make it less chatty with kernel space
2022-08-20 06:20:36 -07:00
Stefanos Kornilios Mitsis Poiitidis 1b5aaf1fb8 Allocator: Simplify StealMemory, make it less chatty with kernel space 2022-08-20 13:10:50 +03:00
Ryan Houdek d8e4873f43 Thunks/X11: Support Variadic stack packing
Found an issue with wine + DXVK + thunks where these were passing in
more than 7 arguments and crashing.

Create some assembly to support any size of variadic stack packing.
Only implemented for AArch64 for now.
2022-08-19 21:52:19 -07:00
Ryan Houdek 998a3d8353 Merge pull request #1928 from Sonicadvance1/more_x11_thunks
Thunks/X11: Adds missing XLibint functions
2022-08-19 17:08:40 -07:00
Ryan Houdek 336dedbed5 Merge pull request #1927 from Sonicadvance1/non_fatal_get_fdpath
FDUtils: Don't make unknown get_fdpath fatal
2022-08-19 17:08:32 -07:00
Ryan Houdek 955595be8a FDUtils: Don't make unknown get_fdpath fatal
Encountered this while running wine things.
Non-fatal so don't explode
2022-08-19 02:28:19 -07:00
Ryan Houdek 576bd4f69a Thunks/X11: Adds missing XLibint functions
Some of these were required to get thunking to work with Proton and
DXVK.
2022-08-19 00:03:54 -07:00
Ryan Houdek cd6915917b IRLoader/TestHarnessLoader: Don't build if not building tests
If we're not building tests then just don't build the IRLoader since it
won't get used.
2022-08-17 17:04:13 -07:00
Ryan Houdek 0adbe31112 Merge pull request #1919 from Sonicadvance1/termux_shm_library
Termux: Add android-shmem library
2022-08-15 11:14:27 -07:00
Ryan Houdek d5138f509c Merge pull request #1917 from Sonicadvance1/fix_compile_without_jemalloc
Thunks: Fix compile without jemalloc
2022-08-15 11:14:17 -07:00
Ryan Houdek 1fe6fc3feb Merge pull request #1920 from Sonicadvance1/tests_from_host_features
unittests: Support skipping unit tests based on host feature support
2022-08-15 11:07:02 -07:00
Ryan Houdek c03a7fd482 Merge pull request #1924 from 1ace/fix-git-abbrev
cmake: fix incorrect assumption about the value of git's core.abbrev
2022-08-15 10:44:59 -07:00
Eric Engestrom 31c47f05f4 cmake: fix incorrect assumption about the value of git's core.abbrev
While the default value of `git config core.abbrev` is `7` (used to
truncate the commit hash in several git commands) and most people don't
configure anything else, I happen to prefer when commits hashes stay
valid for a while, so I changed that value to `20`. Because of this,
FEX fails to build in External/FEXCore/Source/Interface/Core/CPUID.cpp:961

Let's be explicit in the git command about what minimum length of commit
hash we expect.
2022-08-15 16:25:27 +01:00
Ryan Houdek edad24479b unittests: Support skipping unit tests based on host feature support
For these unit tests we no longer need to put them in the disabled tests
file. Instead it will be skipped if the host doesn't support the feature
required.
2022-08-14 20:04:26 -07:00
Ryan Houdek bba58732e0 HostFeatures: Add new supported features flag 2022-08-14 19:56:14 -07:00
Ryan Houdek 8d1cc6cf51 Termux: Add android-shmem library
Otherwise we fail at linking.
2022-08-14 18:23:53 -07:00
Ryan Houdek f03d0be5ef Thunks: Fix compile without jemalloc
This only fixes the compile error. Thunks aren't expected to be used
without jemalloc enabled.
2022-08-13 11:29:11 -07:00
Ryan Houdek a2f4f494a9 Merge pull request #1916 from Sonicadvance1/server_socket_path_override
FEXServer: Support socket path override
2022-08-13 07:00:59 -07:00
Ryan Houdek 9de25c200c Merge pull request #1915 from Sonicadvance1/emulate_64bit_getdents
Linux: Emulate classic getdents syscall for x64 and x32
2022-08-13 07:00:52 -07:00
Ryan Houdek fe1f00aadb FEXServer: Support socket path override
This is necessary for the fexserver to function correctly when chrooting
in to our rootfs and doing things.

Requires independent rootfs script modifications which will come with
the next rootfs update.

Problem comes down to a chroot supporting multiple users, where our
typical use case is only one user. Bind the server file to a single
server for the entire chroot session regardless of users, solving this
problem inside the chroot.

Fixes apt-get inside of chroot, which runs as user _apt.
2022-08-12 22:52:30 -07:00
Ryan Houdek a847fac4ca Linux: Emulate classic getdents syscall for x64 and x32
Arm64 doesn't have the classic getdents syscall, only getdents64.
Old glibc versions (like 2.17) don't support getdents64

This fixes 64-bit ls with a centos 7 rootfs. Likely also fixes some other very
old applications.

Packing differences between 32-bit and 64-bit getdents means we need to
template this between the two types, otherwise it's quite similar.

No known applications rely on the 32-bit getdents but worked with test
applications.
2022-08-12 18:21:49 -07:00
Ryan Houdek 0496506fb7 Linux: Define linux_dirent types for x64
These don't match the compatibility defines and they aren't part of the
public interface.
2022-08-12 16:00:06 -07:00
Ryan Houdek e544591c9c Merge pull request #1901 from Sonicadvance1/build_thunks
CI: Build Thunks
2022-08-12 14:47:54 -07:00
Ryan Houdek e5237d1149 Github: Enable Thunkgen tests 2022-08-12 14:31:18 -07:00
Ryan Houdek b9c848c5e9 Thunks: Xext version check define function prototypes
Nothing major here
2022-08-12 14:31:18 -07:00
Ryan Houdek b64e61a793 Thunks: Check for X11 version
There is no define for this so we must generate our own.
Declare a type ourselves if the library is too old
2022-08-12 14:31:18 -07:00
Ryan Houdek 249e19bf2a Thunks: Asound fix building with alsa 1.2.2 2022-08-09 06:18:43 -07:00
Ryan Houdek baf52ed286 CI: Try building thunks 2022-08-09 05:30:53 -07:00
269 changed files with 9770 additions and 5053 deletions

No files matched your search

+46 -3
View File
@@ -40,7 +40,6 @@ jobs:
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
- name : submodule checkout
@@ -65,7 +64,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
- name: Build
working-directory: ${{runner.workspace}}/build
@@ -178,6 +177,51 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXLinuxTests.log || true
- name: Thunkgen tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target thunkgen_tests
- name: Thunkgen Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkgenTests.log || true
- name: Install
if: matrix.arch[1] == 'x64'
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target install
- name: Test GL No-Thunks
if: matrix.arch[1] == 'x64'
working-directory: ${{runner.workspace}}/build
shell: bash
env:
DISPLAY: ":0"
run: cmake --build . --config $BUILD_TYPE --target thunk_functional_tests_nothunks
- name: No thunks Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_NoThunkResults.log || true
- name: Test GL Thunks
if: matrix.arch[1] == 'x64'
working-directory: ${{runner.workspace}}/build
shell: bash
env:
DISPLAY: ":0"
run: cmake --build . --config $BUILD_TYPE --target thunk_functional_tests_thunks
- name: Thunks Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkResults.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
@@ -197,4 +241,3 @@ jobs:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
retention-days: 3
+118
View File
@@ -0,0 +1,118 @@
name: Vixl Simulator run
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
CC: clang
CXX: clang++
jobs:
build:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
# Only the x86-64 runner is fast enough to run this
arch: [[self-hosted, x64], [self-hosted, ARMv8.4]]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Set runner label
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
- name: Set rootfs paths
run: |
echo "FEX_ROOTFS_MOUNT=/mnt/AutoNFS/rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS_PATH=$HOME/Rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
echo "ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
- name: Update RootFS cache
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
- name : submodule checkout
# Need to update submodules
run: |
git submodule sync --recursive
git submodule update --init --depth 1
- name: Clean Build Environment
run: rm -Rf ${{runner.workspace}}/build
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure CMake
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
- name: Build
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE
- name: ASM Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: IR Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target ir_tests
- name: IR Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_IR.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
# Cap out the log files at 20M in case something crash spins and dumps fault text
# ASM tests get quite close to 10MB
run: truncate --size=<20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
- name: Set runner name
if: ${{ always() }}
run: echo "runner_name=$(hostname)" >> $GITHUB_ENV
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v2'
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
retention-days: 3
+5
View File
@@ -0,0 +1,5 @@
{
"ThunksDB": {
"GL": 1
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"ThunksDB": {
"Vulkan": 1
}
}
+21
View File
@@ -0,0 +1,21 @@
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif()
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif()
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif()
endforeach()
+60 -7
View File
@@ -7,6 +7,7 @@ CHECK_INCLUDE_FILES ("gdb/jit-reader.h" HAVE_GDB_JIT_READER_H)
option(BUILD_TESTS "Build unit tests to ensure sanity" TRUE)
option(BUILD_FEX_LINUX_TESTS "Build FEXLinuxTests, requires x86 compiler" FALSE)
option(BUILD_THUNKS "Build thunks" FALSE)
option(BUILD_CLANG_THUNKS "Build thunks with clang" FALSE)
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
option(ENABLE_IWYU "Enables include what you use program" FALSE)
option(ENABLE_LTO "Enable LTO with compilation" TRUE)
@@ -27,10 +28,23 @@ option(ENABLE_LIBCXX "Enables LLVM libc++" FALSE)
option(ENABLE_INTERPRETER "Enables FEX's Interpreter" FALSE)
option(ENABLE_CCACHE "Enables ccache for compile caching" TRUE)
option(ENABLE_TERMUX_BUILD "Forces building for Termux on a non-Termux build machine" FALSE)
option(ENABLE_VIXL_SIMULATOR "Forces the FEX JIT to use the VIXL simulator" FALSE)
set (X86_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86.cmake" CACHE FILEPATH "Toolchain file for the x86 (cross-)compiler")
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
set (DATA_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/fex-emu" CACHE PATH "global data directory")
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake)
endif()
# These options are meant for package management
set (TUNE_CPU "native" CACHE STRING "Override the CPU the build is tuned for")
set (TUNE_ARCH "generic" CACHE STRING "Override the Arch the build is tuned for")
@@ -84,10 +98,9 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(_M_X86_64 1)
add_definitions(-D_M_X86_64=1)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
set (X86_TOOLCHAIN_FILE "")
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
set(_M_ARM_64 1)
add_definitions(-D_M_ARM_64=1)
endif()
@@ -363,8 +376,6 @@ if (BUILD_TESTS)
endif()
add_subdirectory(FEXHeaderUtils/)
include_directories(FEXHeaderUtils/)
add_subdirectory(External/FEXCore)
# Binfmt_misc files must be installed prior to Source/ installs
@@ -403,8 +414,28 @@ if (BUILD_THUNKS)
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
BINARY_DIR "Guest"
CMAKE_ARGS
"-DBITNESS=64"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_TOOLCHAIN_FILE}"
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_64_TOOLCHAIN_FILE}"
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
"-DGENERATOR_EXE=$<TARGET_FILE:thunkgen>"
INSTALL_COMMAND ""
BUILD_ALWAYS ON
DEPENDS thunkgen
)
ExternalProject_Add(guest-libs-32
PREFIX guest-libs-32
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
BINARY_DIR "Guest_32"
CMAKE_ARGS
"-DBITNESS=32"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_32_TOOLCHAIN_FILE}"
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
@@ -422,6 +453,28 @@ if (BUILD_THUNKS)
)"
DEPENDS guest-libs
)
install(
CODE "MESSAGE(\"-- Installing: guest-libs-32\")"
CODE "
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target install
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest_32
)"
DEPENDS guest-libs-32
)
add_custom_target(uninstall_guest-libs
COMMAND ${CMAKE_COMMAND} "--build" "." "--target" "uninstall"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest
)
add_custom_target(uninstall_guest-libs-32
COMMAND ${CMAKE_COMMAND} "--build" "." "--target" "uninstall"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest_32
)
add_dependencies(uninstall uninstall_guest-libs)
add_dependencies(uninstall uninstall_guest-libs-32)
endif()
set(FEX_VERSION_MAJOR "0")
@@ -476,7 +529,7 @@ endif()
set (CPACK_GENERATOR "DEB")
set (CPACK_PACKAGE_NAME fex-emu)
set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${GIT_DESCRIBE_STRING}_${CMAKE_SYSTEM_PROCESSOR}")
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.org>")
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.com>")
set (CPACK_PACKAGE_VERSION_MAJOR "${FEX_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${FEX_VERSION_MINOR}")
set (CPACK_PACKAGE_VERSION_PATCH "${FEX_VERSION_PATCH}")
+1 -1
View File
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at team@fex-emu.org. All
reported by contacting the project team at team@fex-emu.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
+8
View File
@@ -165,6 +165,14 @@
"@PREFIX_LIB@/x86_64-linux-gnu/libXfixes.so.3.1.0"
]
},
"OpenCL": {
"Library" : "libOpenCL-guest.so",
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so",
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so.1",
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so.1.0.0"
]
},
"":{}
}
}
+12 -6
View File
@@ -9,12 +9,19 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
set(_M_ARM_64 1)
endif()
set(ENABLE_JIT_X86_64 ${_M_X86_64} CACHE BOOL "Enable the x86_64 JIT")
set(ENABLE_JIT_ARM64 ${_M_ARM_64} CACHE BOOL "Enable the ARM64 JIT")
if (ENABLE_VIXL_SIMULATOR)
# If the vixl simulator is enabled then we are using the ARM64 JIT
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" FALSE)
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" TRUE)
else()
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" ${_M_X86_64})
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" ${_M_ARM_64})
endif()
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
@@ -27,7 +34,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(CheckCXXCompilerFlag)
include(CheckIncludeFileCXX)
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
# Useful to have for freestanding libFEXCore
add_subdirectory(External/vixl/)
@@ -46,14 +52,14 @@ if (OVERRIDE_VERSION STREQUAL "detect")
if (GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_SHORT_HASH
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${GIT_EXECUTABLE} describe
COMMAND ${GIT_EXECUTABLE} describe --abbrev=7
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DESCRIBE_STRING
ERROR_QUIET
+9 -4
View File
@@ -177,6 +177,11 @@ if (_M_ARM_64)
list(APPEND DEFINES -D_M_ARM_64=1)
endif()
if (ENABLE_VIXL_SIMULATOR)
# We can run the simulator on both x86-64 or AArch64 hosts
list(APPEND DEFINES -DVIXL_SIMULATOR=1 -DVIXL_INCLUDE_SIMULATOR_AARCH64=1)
endif()
if (ENABLE_JIT_X86_64)
list(APPEND SRCS
Interface/Core/JIT/x86_64/JIT.cpp
@@ -213,7 +218,7 @@ if (ENABLE_JIT_ARM64)
)
endif()
set (LIBS vixl dl xxhash tiny-json)
set (LIBS fmt::fmt vixl dl xxhash tiny-json FEXHeaderUtils)
if (ENABLE_JEMALLOC)
list (APPEND LIBS FEX_jemalloc)
endif()
@@ -359,14 +364,14 @@ endfunction()
# Build FEXCore_Config static library
add_library(FEXCore_Base STATIC ${FEXCORE_BASE_SRCS})
target_link_libraries(FEXCore_Base fmt::fmt tiny-json)
target_link_libraries(FEXCore_Base ${LIBS})
AddDefaultOptionsToTarget(FEXCore_Base)
function(AddObject Name Type)
add_library(${Name} ${Type} ${SRCS})
add_dependencies(${Name} IR_INC)
target_link_libraries(${Name} FEXCore_Base ${LIBS})
target_link_libraries(${Name} FEXCore_Base)
AddDefaultOptionsToTarget(${Name})
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
@@ -374,7 +379,7 @@ endfunction()
function(AddLibrary Name Type)
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
target_link_libraries(${Name} FEXCore_Base ${LIBS})
target_link_libraries(${Name} FEXCore_Base)
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
AddDefaultOptionsToTarget(${Name})
+1 -1
View File
@@ -82,7 +82,7 @@ namespace JSON {
json_t const* ConfigList = json_getProperty(json, "Config");
if (!ConfigList) {
LogMan::Msg::EFmt("Couldn't get config list");
// This is a non-error if the configuration file exists but no Config section
return;
}
+25
View File
@@ -90,6 +90,13 @@
"Folder to find the guest-side thunking libraries."
]
},
"ThunkGuestLibs32": {
"Type": "str",
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks_32/",
"Desc": [
"Folder to find the 32-bit guest-side thunking libraries."
]
},
"ThunkConfig": {
"Type": "str",
"Default": "",
@@ -351,6 +358,13 @@
"Desc": [
"Loads an AOT IR cache for the loaded executable."
]
},
"ServerSocketPath": {
"Type": "str",
"Default": "",
"Desc": [
"Override for a FEXServer socket path. Only useful for chroots."
]
}
}
},
@@ -368,6 +382,17 @@
"Type": "str",
"Default": ""
},
"APP_CONFIG_NAME": {
"Type": "str",
"Default": "",
"Desc": [
"This is the application config name that has been loaded.",
"This differs from APP_FILENAME in two ways",
"Where APP_FILENAME always points to the executable path that FEX-Emu is executing.",
"This matches what is used to load the AppLayer configuration name.",
"When running through a compatibility layer like wine, this will only be the exe name, instead of wine full path."
]
},
"IS64BIT_MODE": {
"Type": "bool",
"Default": "false"
+4
View File
@@ -202,6 +202,10 @@ namespace FEXCore::Context {
return CTX->AddCustomIREntrypoint(Entrypoint, Handler, Creator, Data);
}
void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
CTX->AppendThunkDefinitions(Definitions);
}
namespace Debug {
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
CTX->CompileRIP(CTX->ParentThread, RIP);
+4 -1
View File
@@ -15,6 +15,7 @@
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/Event.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <stdint.h>
@@ -189,7 +190,7 @@ namespace FEXCore::Context {
static void ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
auto Thread = Frame->Thread;
LogMan::Throw::AFmt(Thread->ThreadManager.GetTID() == gettid(), "Must be called from owning thread {}, not {}", Thread->ThreadManager.GetTID(), gettid());
LogMan::Throw::AFmt(Thread->ThreadManager.GetTID() == FHU::Syscalls::gettid(), "Must be called from owning thread {}, not {}", Thread->ThreadManager.GetTID(), FHU::Syscalls::gettid());
FHU::ScopedSignalMaskWithUniqueLock lk(Thread->CTX->CodeInvalidationMutex);
@@ -322,6 +323,8 @@ namespace FEXCore::Context {
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
}
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
FEXCore::Utils::PooledAllocatorMMap OpDispatcherAllocator;
FEXCore::Utils::PooledAllocatorMMap FrontendAllocator;
@@ -17,7 +17,6 @@
#include <utility>
namespace FEXCore::CPU {
#define STATE x28
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
@@ -25,11 +24,15 @@ Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
, EmitterCTX {ctx} {
CPU.SetUp();
#ifdef VIXL_SIMULATOR
auto Features = vixl::CPUFeatures::All();
#else
auto Features = vixl::CPUFeatures::InferFromOS();
if (ctx->HostFeatures.SupportsAtomics) {
// Hypervisor can hide this on the c630?
Features.Combine(vixl::CPUFeatures::Feature::kLORegions);
}
#endif
SetCPUFeatures(Features);
}
@@ -214,7 +217,8 @@ void Arm64Emitter::SpillStaticRegs(bool FPRs, uint32_t GPRSpillMask, uint32_t FP
const auto Reg = SRAFPR[i];
if (((1U << Reg.GetCode()) & FPRSpillMask) != 0) {
str(Reg.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[i][0])));
mov(TMP4, offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
st1b(Reg.Z().VnB(), PRED_TMP_32B, SVEMemOperand(STATE, TMP4));
}
}
} else {
@@ -257,11 +261,19 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
if (FPRs) {
if (EmitterCTX->HostFeatures.SupportsAVX) {
// Set up predicate registers.
// We don't bother spilling these in SpillStaticRegs,
// since all that matters is we restore them on a fill.
// It's not a concern if they get trounced by something else.
ptrue(PRED_TMP_16B.VnB(), SVE_VL16);
ptrue(PRED_TMP_32B.VnB(), SVE_VL32);
for (size_t i = 0; i < SRAFPR.size(); i++) {
const auto Reg = SRAFPR[i];
if (((1U << Reg.GetCode()) & FPRFillMask) != 0) {
ldr(Reg.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[i][0])));
mov(TMP4, offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
ld1b(Reg.Z().VnB(), PRED_TMP_32B.Zeroing(), SVEMemOperand(STATE, TMP4));
}
}
} else {
@@ -286,20 +298,31 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
}
void Arm64Emitter::PushDynamicRegsAndLR() {
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
const auto GPRSize = (RA64.size() + 1) * Core::CPUState::GPR_REG_SIZE;
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE
: Core::CPUState::XMM_SSE_REG_SIZE;
const auto FPRSize = RAFPR.size() * FPRRegSize;
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
sub(sp, sp, SPOffset);
int i = 0;
for (auto RA : RAFPR)
{
str(RA.Q(), MemOperand(sp, i * 8));
i+=2;
if (CanUseSVE) {
for (const auto& RA : RAFPR) {
mov(TMP4, i * 8);
st1b(RA.Z().VnB(), PRED_TMP_32B, SVEMemOperand(sp, TMP4));
i += 4;
}
} else {
for (const auto& RA : RAFPR) {
str(RA.Q(), MemOperand(sp, i * 8));
i += 2;
}
}
#if 0 // All GPRs should be caller saved
for (auto RA : RA64)
{
for (const auto& RA : RA64) {
str(RA, MemOperand(sp, i * 8));
i++;
}
@@ -309,18 +332,29 @@ void Arm64Emitter::PushDynamicRegsAndLR() {
}
void Arm64Emitter::PopDynamicRegsAndLR() {
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
const auto GPRSize = (RA64.size() + 1) * Core::CPUState::GPR_REG_SIZE;
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE
: Core::CPUState::XMM_SSE_REG_SIZE;
const auto FPRSize = RAFPR.size() * FPRRegSize;
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
int i = 0;
for (auto RA : RAFPR)
{
ldr(RA.Q(), MemOperand(sp, i * 8));
i+=2;
if (CanUseSVE) {
for (const auto& RA : RAFPR) {
mov(TMP4, i * 8);
ld1b(RA.Z().VnB(), PRED_TMP_32B.Zeroing(), SVEMemOperand(sp, TMP4));
i += 4;
}
} else {
for (const auto& RA : RAFPR) {
ldr(RA.Q(), MemOperand(sp, i * 8));
i += 2;
}
}
#if 0 // All GPRs should be caller saved
for (auto RA : RA64)
{
for (const auto& RA : RA64) {
ldr(RA, MemOperand(sp, i * 8));
i++;
}
@@ -8,6 +8,10 @@
#include <aarch64/cpu-aarch64.h>
#include <aarch64/operands-aarch64.h>
#include <platform-vixl.h>
#ifdef VIXL_SIMULATOR
#include <aarch64/simulator-aarch64.h>
#include <aarch64/simulator-constants-aarch64.h>
#endif
#include <FEXCore/Config/Config.h>
@@ -58,6 +62,27 @@ const std::array<aarch64::VRegister, 12> RAFPR = {
v8, v9, v10, v11, v12, v13, v14, v15
};
// Contains the address to the currently available CPU state
#define STATE x28
// GPR temporaries. Only x3 can be used across spill boundaries
// so if these ever need to change, be very careful about that.
#define TMP1 x0
#define TMP2 x1
#define TMP3 x2
#define TMP4 x3
// Vector temporaries
#define VTMP1 v1
#define VTMP2 v2
#define VTMP3 v3
// 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.
// PRED_TMP_32B indicates a predicate register that indicates the first 32 bytes set to 1.
#define PRED_TMP_16B p6
#define PRED_TMP_32B p7
// This class contains common emitter utility functions that can
// be used by both Arm64 JIT and ARM64 Dispatcher
class Arm64Emitter : public vixl::aarch64::Assembler {
@@ -67,6 +92,10 @@ protected:
FEXCore::Context::Context *EmitterCTX;
vixl::aarch64::CPU CPU;
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant, bool NOPPad = false);
// NOTE: These functions WILL clobber the register TMP4 if AVX support is enabled
// and FPRs are being spilled or filled. If only GPRs are spilled/filled, then
// TMP4 is left alone.
void SpillStaticRegs(bool FPRs = true, uint32_t GPRSpillMask = ~0U, uint32_t FPRSpillMask = ~0U);
void FillStaticRegs(bool FPRs = true, uint32_t GPRFillMask = ~0U, uint32_t FPRFillMask = ~0U);
@@ -83,6 +112,71 @@ protected:
void PopCalleeSavedRegisters();
void Align16B();
#ifdef VIXL_SIMULATOR
// Generates a vixl simulator runtime call.
//
// This matches behaviour of vixl's macro assembler, but we need to reimplement it since we aren't using the macro assembler.
// This isn't too complex with how vixl emits this.
//
// Emit:
// 1) hlt(kRuntimeCallOpcode)
// 2) Simulator wrapper handler
// 3) Function to call
// 4) Style of the function call (Call versus tail-call)
template<typename R, typename... P>
void GenerateRuntimeCall(R (*Function)(P...)) {
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
&(Simulator::RuntimeCallStructHelper<R, P...>::Wrapper));
uintptr_t FunctionAddress = reinterpret_cast<uintptr_t>(Function);
hlt(kRuntimeCallOpcode);
// Simulator wrapper address pointer.
dc(SimulatorWrapperAddress);
// Runtime function address to call
dc(FunctionAddress);
// Call type
dc32(kCallRuntime);
}
template<typename R, typename... P>
void GenerateIndirectRuntimeCall(vixl::aarch64::Register Reg) {
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
&(Simulator::RuntimeCallStructHelper<R, P...>::Wrapper));
hlt(kIndirectRuntimeCallOpcode);
// Simulator wrapper address pointer.
dc(SimulatorWrapperAddress);
// Register that contains the function to call
dc(Reg.GetCode());
// Call type
dc32(kCallRuntime);
}
template<>
void GenerateIndirectRuntimeCall<float, __uint128_t>(vixl::aarch64::Register Reg) {
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
&(Simulator::RuntimeCallStructHelper<float, __uint128_t>::Wrapper));
hlt(kIndirectRuntimeCallOpcode);
// Simulator wrapper address pointer.
dc(SimulatorWrapperAddress);
// Register that contains the function to call
dc(Reg.GetCode());
// Call type
dc32(kCallRuntime);
}
#endif
FEX_CONFIG_OPT(StaticRegisterAllocation, SRA);
};
+18 -13
View File
@@ -221,7 +221,7 @@ namespace FEXCore::Context {
#if (_M_X86_64 && JIT_X86_64)
FEXCore::CPU::InitializeX86JITSignalHandlers(this);
BackendFeatures = FEXCore::CPU::GetX86JITBackendFeatures();
#elif (_M_ARM_64 && JIT_ARM64)
#elif (_M_ARM_64 && JIT_ARM64) || defined(VIXL_SIMULATOR)
FEXCore::CPU::InitializeArm64JITSignalHandlers(this);
BackendFeatures = FEXCore::CPU::GetArm64JITBackendFeatures();
#else
@@ -238,16 +238,16 @@ namespace FEXCore::Context {
DispatcherConfig.StaticRegisterAllocation = Config.StaticRegisterAllocation && BackendFeatures.SupportsStaticRegisterAllocation;
#if (_M_X86_64)
Dispatcher = FEXCore::CPU::Dispatcher::CreateX86(this, DispatcherConfig);
#elif (_M_ARM_64)
#if JIT_ARM64
Dispatcher = FEXCore::CPU::Dispatcher::CreateArm64(this, DispatcherConfig);
#elif JIT_X86_64
Dispatcher = FEXCore::CPU::Dispatcher::CreateX86(this, DispatcherConfig);
#else
ERROR_AND_DIE_FMT("FEXCore has been compiled with an unknown target");
#endif
// Initialize common signal handlers
auto PauseHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
return Thread->CTX->Dispatcher->HandleSignalPause(Thread, Signal, info, ucontext);
};
@@ -573,7 +573,7 @@ namespace FEXCore::Context {
#if (_M_X86_64 && JIT_X86_64)
Thread->CPUBackend = FEXCore::CPU::CreateX86JITCore(this, Thread);
#elif (_M_ARM_64 && JIT_ARM64)
#elif (_M_ARM_64 && JIT_ARM64) || defined(VIXL_SIMULATOR)
Thread->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, Thread);
#else
ERROR_AND_DIE_FMT("FEXCore has been compiled without a viable JIT core");
@@ -740,7 +740,7 @@ namespace FEXCore::Context {
}
}
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
Thread->OpDispatcher->ReownOrClaimBuffer();
Thread->OpDispatcher->ResetWorkingList();
@@ -749,7 +749,7 @@ namespace FEXCore::Context {
std::shared_lock lk(CustomIRMutex);
auto Handler = CustomIRHandlers.find(GuestRIP);
if (Handler != CustomIRHandlers.end()) {
TotalInstructions = 1;
@@ -872,7 +872,7 @@ namespace FEXCore::Context {
}
}
}
Thread->OpDispatcher->Finalize();
Thread->FrontendDecoder->DelayedDisownBuffer();
@@ -1182,7 +1182,7 @@ namespace FEXCore::Context {
static void InvalidateGuestCodeRangeInternal(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
std::lock_guard lk(CTX->ThreadCreationMutex);
for (auto &Thread : CTX->Threads) {
InvalidateGuestThreadCodeRange(Thread, Start, Length);
}
@@ -1190,7 +1190,7 @@ namespace FEXCore::Context {
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
}
@@ -1233,9 +1233,9 @@ namespace FEXCore::Context {
Thread->LookupCache->AddBlockLink(GuestDestination, HostLink, delinker);
}
void Context::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
void Context::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
LogMan::Throw::AFmt(Thread->CTX->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
std::lock_guard<std::recursive_mutex> lk(Thread->LookupCache->WriteLock);
Thread->DebugStore.erase(GuestRIP);
@@ -1332,6 +1332,11 @@ namespace FEXCore::Context {
}
}
void Context::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
ThunkHandler->AppendThunkDefinitions(Definitions);
}
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
@@ -38,11 +38,20 @@ namespace FEXCore::CPU {
using namespace vixl;
using namespace vixl::aarch64;
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE x28
constexpr size_t MAX_DISPATCHER_CODE_SIZE = 8192;
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
: FEXCore::CPU::Dispatcher(ctx, config), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE) {
: FEXCore::CPU::Dispatcher(ctx, config), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE)
#ifdef VIXL_SIMULATOR
, Simulator {&Decoder}
#endif
{
#ifdef VIXL_SIMULATOR
// Hardcode a 256-bit vector width if we are running in the simulator.
Simulator.SetVectorLengthInBits(256);
#endif
SetAllowAssembler(true);
DispatchPtr = GetCursorAddress<AsmDispatch>();
@@ -178,7 +187,12 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
ret();
}
#ifdef VIXL_SIMULATOR
// VIXL simulator can't run syscalls.
constexpr bool SignalSafeCompile = false;
#else
constexpr bool SignalSafeCompile = true;
#endif
{
ExitFunctionLinkerAddress = GetCursorAddress<uint64_t>();
if (config.StaticRegisterAllocation)
@@ -206,8 +220,12 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
mov(x0, STATE);
mov(x1, lr);
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionLink));
blr(x3);
ldr(x2, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionLink));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uintptr_t, void *, void *>(x2);
#else
blr(x2);
#endif
if (SignalSafeCompile) {
// Now restore the signal mask
@@ -266,8 +284,11 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
ldr(x3, &l_CompileBlock);
// X2 contains our guest RIP
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<void, void *, uint64_t, void *>(x3);
#else
blr(x3); // { CTX, Frame, RIP}
#endif
if (SignalSafeCompile) {
// Now restore the signal mask
@@ -349,7 +370,11 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
ldr(x0, &l_CTX);
mov(x1, STATE);
ldr(x2, &l_Sleep);
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<void, void *, void *>(x2);
#else
blr(x2);
#endif
PauseReturnInstruction = GetCursorAddress<uint64_t>();
// Fault to start running again
@@ -412,11 +437,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
LUDIVHandlerAddress = GetCursorAddress<uint64_t>();
PushDynamicRegsAndLR();
SpillStaticRegs();
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LUDIV));
SpillStaticRegs();
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
#else
blr(x3);
#endif
FillStaticRegs();
// Result is now in x0
@@ -431,11 +459,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
LDIVHandlerAddress = GetCursorAddress<uint64_t>();
PushDynamicRegsAndLR();
SpillStaticRegs();
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LDIV));
SpillStaticRegs();
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
#else
blr(x3);
#endif
FillStaticRegs();
// Result is now in x0
@@ -450,11 +481,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
LUREMHandlerAddress = GetCursorAddress<uint64_t>();
PushDynamicRegsAndLR();
SpillStaticRegs();
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LUREM));
SpillStaticRegs();
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
#else
blr(x3);
#endif
FillStaticRegs();
// Result is now in x0
@@ -469,11 +503,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
LREMHandlerAddress = GetCursorAddress<uint64_t>();
PushDynamicRegsAndLR();
SpillStaticRegs();
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LREM));
SpillStaticRegs();
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
#else
blr(x3);
#endif
FillStaticRegs();
// Result is now in x0
@@ -504,13 +541,27 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
}
}
#ifdef VIXL_SIMULATOR
void Arm64Dispatcher::ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
Simulator.WriteXRegister(0, reinterpret_cast<int64_t>(Frame));
Simulator.RunFrom(reinterpret_cast<Instruction const*>(DispatchPtr));
}
void Arm64Dispatcher::ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
Simulator.WriteXRegister(0, reinterpret_cast<int64_t>(Frame));
Simulator.WriteXRegister(1, RIP);
Simulator.RunFrom(reinterpret_cast<Instruction const*>(CallbackPtr));
}
#endif
// Used by GenerateGDBPauseCheck, GenerateInterpreterTrampoline, destination buffer is set before use
static thread_local vixl::aarch64::Assembler emit((uint8_t*)&emit, 1);
size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) {
*emit.GetBuffer() = vixl::CodeBuffer(CodeBuffer, MaxGDBPauseCheckSize);
vixl::CodeBufferCheckScope scope(&emit, MaxGDBPauseCheckSize, vixl::CodeBufferCheckScope::kDontReserveBufferSpace, vixl::CodeBufferCheckScope::kNoAssert);
aarch64::Label RunBlock;
@@ -546,7 +597,7 @@ size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t Gues
size_t Arm64Dispatcher::GenerateInterpreterTrampoline(uint8_t *CodeBuffer) {
LOGMAN_THROW_AA_FMT(!config.StaticRegisterAllocation, "GenerateInterpreterTrampoline dispatcher does not support SRA");
*emit.GetBuffer() = vixl::CodeBuffer(CodeBuffer, MaxInterpreterTrampolineSize);
vixl::CodeBufferCheckScope scope(&emit, MaxInterpreterTrampolineSize, vixl::CodeBufferCheckScope::kDontReserveBufferSpace, vixl::CodeBufferCheckScope::kNoAssert);
@@ -594,7 +645,7 @@ void Arm64Dispatcher::SpillSRA(FEXCore::Core::InternalThreadState *Thread, void
}
void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) {
// Setup dispatcher specific pointers that need to be accessed from JIT code
// Setup dispatcher specific pointers that need to be accessed from JIT code
{
auto &Common = Thread->CurrentFrame->Pointers.Common;
@@ -3,6 +3,10 @@
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#ifdef VIXL_SIMULATOR
#include <aarch64/simulator-aarch64.h>
#endif
namespace FEXCore::Context {
struct Context;
}
@@ -20,6 +24,11 @@ class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) override;
size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) override;
#ifdef VIXL_SIMULATOR
void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) override;
void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) override;
#endif
protected:
void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) override;
@@ -29,6 +38,11 @@ class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
uint64_t LDIVHandlerAddress{};
uint64_t LUREMHandlerAddress{};
uint64_t LREMHandlerAddress{};
#ifdef VIXL_SIMULATOR
vixl::aarch64::Decoder Decoder;
vixl::aarch64::Simulator Simulator;
#endif
};
}
@@ -32,7 +32,7 @@ struct DispatcherConfig {
class Dispatcher {
public:
virtual ~Dispatcher() = default;
/**
* @name Dispatch Helper functions
* @{ */
@@ -75,12 +75,12 @@ public:
static std::unique_ptr<Dispatcher> CreateX86(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
static std::unique_ptr<Dispatcher> CreateArm64(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
virtual void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
DispatchPtr(Frame);
}
void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
virtual void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
CallbackPtr(Frame, RIP);
}
+55 -28
View File
@@ -1,7 +1,7 @@
#include "Interface/Core/CPUID.h"
#include <FEXCore/Core/HostFeatures.h>
#ifdef _M_ARM_64
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
@@ -50,8 +50,12 @@ static uint32_t GetDCZID() {
HostFeatures::HostFeatures() {
#ifdef _M_ARM_64
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
#ifdef VIXL_SIMULATOR
auto Features = vixl::CPUFeatures::All();
#else
auto Features = vixl::CPUFeatures::InferFromOS();
#endif
SupportsAES = Features.Has(vixl::CPUFeatures::Feature::kAES);
SupportsCRC = Features.Has(vixl::CPUFeatures::Feature::kCRC32);
SupportsAtomics = Features.Has(vixl::CPUFeatures::Feature::kAtomics);
@@ -62,9 +66,24 @@ HostFeatures::HostFeatures() {
SupportsRCPC = Features.Has(vixl::CPUFeatures::Feature::kRCpc);
SupportsTSOImm9 = Features.Has(vixl::CPUFeatures::Feature::kRCpcImm);
Supports3DNow = true;
SupportsSSE4A = true;
#ifdef VIXL_SIMULATOR
// Hardcode enable SVE with 256-bit wide registers.
SupportsAVX = true;
#else
SupportsAVX = Features.Has(vixl::CPUFeatures::Feature::kSVE2) &&
vixl::aarch64::CPU::ReadSVEVectorLengthInBits() >= 256;
#endif
SupportsSHA = true;
SupportsBMI1 = true;
SupportsBMI2 = true;
if (!SupportsAtomics) {
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
}
#ifdef _M_ARM_64
// We need to get the CPU's cache line size
// We expect sane targets that have correct cacheline sizes across clusters
uint64_t CTR;
@@ -74,32 +93,6 @@ HostFeatures::HostFeatures() {
DCacheLineSize = 4 << ((CTR >> 16) & 0xF);
ICacheLineSize = 4 << (CTR & 0xF);
if (!SupportsAtomics) {
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
}
#endif
#ifdef _M_X86_64
Xbyak::util::Cpu Features{};
SupportsAES = Features.has(Xbyak::util::Cpu::tAESNI);
SupportsCRC = Features.has(Xbyak::util::Cpu::tSSE42);
SupportsRAND = Features.has(Xbyak::util::Cpu::tRDRAND) && Features.has(Xbyak::util::Cpu::tRDSEED);
SupportsRCPC = true;
SupportsTSOImm9 = true;
SupportsAVX = true;
// xbyak doesn't know how to check for CLZero
uint32_t eax, ebx, ecx, edx;
// First ensure we support a new enough extended CPUID function range
__cpuid(0x8000'0000, eax, ebx, ecx, edx);
if (eax >= 0x8000'0008U) {
// CLZero defined in 8000_00008_EBX[bit 0]
__cpuid(0x8000'0008, eax, ebx, ecx, edx);
SupportsCLZERO = ebx & 1;
}
SupportsFlushInputsToZero = true;
SupportsFloatExceptions = true;
#else
// Test if this CPU supports float exception trapping by attempting to enable
// On unsupported these bits are architecturally defined as RAZ/WI
constexpr uint32_t ExceptionEnableTraps =
@@ -120,6 +113,39 @@ HostFeatures::HostFeatures() {
SetFPCR(OriginalFPCR);
#endif
#endif
#if defined(_M_X86_64) && !defined(VIXL_SIMULATOR)
Xbyak::util::Cpu Features{};
SupportsAES = Features.has(Xbyak::util::Cpu::tAESNI);
SupportsCRC = Features.has(Xbyak::util::Cpu::tSSE42);
SupportsRAND = Features.has(Xbyak::util::Cpu::tRDRAND) && Features.has(Xbyak::util::Cpu::tRDSEED);
SupportsRCPC = true;
SupportsTSOImm9 = true;
Supports3DNow = Features.has(Xbyak::util::Cpu::t3DN) && Features.has(Xbyak::util::Cpu::tE3DN);
SupportsSSE4A = Features.has(Xbyak::util::Cpu::tSSE4a);
SupportsAVX = true;
SupportsSHA = Features.has(Xbyak::util::Cpu::tSHA);
SupportsBMI1 = Features.has(Xbyak::util::Cpu::tBMI1);
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tBMI2);
// xbyak doesn't know how to check for CLZero
uint32_t eax, ebx, ecx, edx;
// First ensure we support a new enough extended CPUID function range
__cpuid(0x8000'0000, eax, ebx, ecx, edx);
if (eax >= 0x8000'0008U) {
// CLZero defined in 8000_00008_EBX[bit 0]
__cpuid(0x8000'0008, eax, ebx, ecx, edx);
SupportsCLZERO = ebx & 1;
}
SupportsFlushInputsToZero = true;
SupportsFloatExceptions = true;
#endif
#ifdef VIXL_SIMULATOR
// simulator doesn't support dc(ZVA)
SupportsCLZERO = false;
#else
// Check if we can support cacheline clears
uint32_t DCZID = GetDCZID();
if ((DCZID & DCZID_DZP_MASK) == 0) {
@@ -129,5 +155,6 @@ HostFeatures::HostFeatures() {
// This means we can use the instruction
SupportsCLZERO = DCZID_Bytes == CPUIDEmu::CACHELINE_SIZE;
}
#endif
}
}
@@ -159,21 +159,26 @@
break; \
}
struct InterpVector256 {
__uint128_t Lower;
__uint128_t Upper;
};
template<typename Res>
Res GetDest(void* SSAData, FEXCore::IR::OrderedNodeWrapper Op) {
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.ID().Value];
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.ID().Value];
return reinterpret_cast<Res>(DstPtr);
}
template<typename Res>
Res GetDest(void* SSAData, FEXCore::IR::NodeID Op) {
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.Value];
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.Value];
return reinterpret_cast<Res>(DstPtr);
}
template<typename Res>
Res GetSrc(void* SSAData, FEXCore::IR::OrderedNodeWrapper Src) {
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Src.ID().Value];
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Src.ID().Value];
return reinterpret_cast<Res>(DstPtr);
}
@@ -1,5 +1,6 @@
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUID.h"
#include "InterpreterDefines.h"
#include "InterpreterOps.h"
#include "F80Ops.h"
@@ -180,13 +181,10 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
// Move ops
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
REGISTER_OP(MOV, Mov);
// Vector ops
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(SPLATVECTOR2, SplatVector);
REGISTER_OP(SPLATVECTOR4, SplatVector);
REGISTER_OP(VMOV, VMov);
REGISTER_OP(VAND, VAnd);
REGISTER_OP(VBIC, VBic);
@@ -245,8 +243,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
REGISTER_OP(VUSHRS, VUShrS);
REGISTER_OP(VSSHRS, VSShrS);
REGISTER_OP(VINSELEMENT, VInsElement);
REGISTER_OP(VINSSCALARELEMENT, VInsScalarElement);
REGISTER_OP(VEXTRACTELEMENT, VExtractElement);
REGISTER_OP(VDUPELEMENT, VDupElement);
REGISTER_OP(VEXTR, VExtr);
REGISTER_OP(VSLI, VSLI);
@@ -256,7 +252,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
REGISTER_OP(VSHLI, VShlI);
REGISTER_OP(VUSHRNI, VUShrNI);
REGISTER_OP(VUSHRNI2, VUShrNI2);
REGISTER_OP(VBITCAST, VBitcast);
REGISTER_OP(VSXTL, VSXTL);
REGISTER_OP(VSXTL2, VSXTL2);
REGISTER_OP(VUXTL, VUXTL);
@@ -336,23 +331,28 @@ void InterpreterOps::Op_NoOp(FEXCore::IR::IROp_Header *IROp, IROpData *Data, IR:
void InterpreterOps::InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::IR::IRListView const *CurrentIR) {
volatile void *StackEntry = alloca(0);
uintptr_t ListSize = CurrentIR->GetSSACount();
const uintptr_t ListSize = CurrentIR->GetSSACount();
static_assert(sizeof(FEXCore::IR::IROp_Header) == 4);
static_assert(sizeof(FEXCore::IR::OrderedNode) == 16);
auto BlockEnd = CurrentIR->GetBlocks().end();
InterpreterOps::IROpData OpData{};
OpData.State = Frame->Thread;
OpData.SSAData = alloca(ListSize * 16);
OpData.CurrentEntry = Frame->State.rip;
OpData.CurrentIR = CurrentIR;
OpData.StackEntry = StackEntry;
OpData.BlockIterator = CurrentIR->GetBlocks().begin();
constexpr size_t ListEntrySizeInBytes = sizeof(InterpVector256);
const size_t SSADataSize = ListSize * ListEntrySizeInBytes;
// Clear them all to zero. Required for Zero-extend semantics
memset(OpData.SSAData, 0, ListSize * 16);
InterpreterOps::IROpData OpData{
.State = Frame->Thread,
.CurrentEntry = Frame->State.rip,
.CurrentIR = CurrentIR,
.StackEntry = StackEntry,
.SSAData = alloca(SSADataSize),
.BlockResults = {},
.BlockIterator = CurrentIR->GetBlocks().begin(),
};
// Clear all SSAData entries to zero. Required for Zero-extend semantics
memset(OpData.SSAData, 0, SSADataSize);
while (1) {
using namespace FEXCore::IR;
@@ -207,7 +207,6 @@ namespace FEXCore::CPU {
///< Vector ops
DEF_OP(VectorZero);
DEF_OP(VectorImm);
DEF_OP(SplatVector);
DEF_OP(VMov);
DEF_OP(VAnd);
DEF_OP(VBic);
@@ -264,8 +263,6 @@ namespace FEXCore::CPU {
DEF_OP(VUShrS);
DEF_OP(VSShrS);
DEF_OP(VInsElement);
DEF_OP(VInsScalarElement);
DEF_OP(VExtractElement);
DEF_OP(VDupElement);
DEF_OP(VExtr);
DEF_OP(VSLI);
@@ -275,7 +272,6 @@ namespace FEXCore::CPU {
DEF_OP(VShlI);
DEF_OP(VUShrNI);
DEF_OP(VUShrNI2);
DEF_OP(VBitcast);
DEF_OP(VSXTL);
DEF_OP(VSXTL2);
DEF_OP(VUXTL);
@@ -30,13 +30,6 @@ DEF_OP(CreateElementPair) {
memcpy(Dst + IROp->ElementSize, Src_Upper, IROp->ElementSize);
}
DEF_OP(Mov) {
auto Op = IROp->C<IR::IROp_Mov>();
const uint8_t OpSize = IROp->Size;
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Value), OpSize);
}
#undef DEF_OP
} // namespace FEXCore::CPU
File diff suppressed because it is too large. Load diff
@@ -40,7 +40,7 @@ DEF_OP(CallbackReturn) {
ResetStack();
// We can now lower the ref counter again
ldr(w2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SignalHandlerRefCounter)));
sub(w2, w2, 1);
str(w2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SignalHandlerRefCounter)));
@@ -197,7 +197,11 @@ DEF_OP(Syscall) {
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SyscallHandlerFunc)));
mov(x1, STATE);
mov(x2, sp);
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint64_t, void*, void*, void*>(x3);
#else
blr(x3);
#endif
add(sp, sp, SPOffset);
@@ -381,7 +385,11 @@ DEF_OP(Thunk) {
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
LoadConstant(x2, (uintptr_t)thunkFn);
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<void, void*, void*>(x2);
#else
blr(x2);
#endif
PopDynamicRegsAndLR();
@@ -448,7 +456,11 @@ DEF_OP(ThreadRemoveCodeEntry) {
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadRemoveCodeEntryFromJIT)));
SpillStaticRegs();
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<void, void*, void*>(x2);
#else
blr(x2);
#endif
FillStaticRegs();
// Fix the stack and any values that were stepped on
@@ -459,6 +471,7 @@ DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
PushDynamicRegsAndLR();
SpillStaticRegs();
// x0 = CPUID Handler
// x1 = CPUID Function
@@ -467,10 +480,13 @@ DEF_OP(CPUID) {
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.CPUIDFunction)));
mov(x1, GetReg<RA_64>(Op->Function.ID()));
mov(x2, GetReg<RA_64>(Op->Leaf.ID()));
SpillStaticRegs();
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<__uint128_t, void*, uint64_t, uint64_t>(x3);
#else
blr(x3);
FillStaticRegs();
#endif
FillStaticRegs();
PopDynamicRegsAndLR();
// Results are in x0, x1
+75 -11
View File
@@ -94,7 +94,12 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
uxth(w0, GetReg<RA_32>(IROp->Args[0].ID()));
ldr(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<void, uint16_t>(x1);
#else
blr(x1);
#endif
PopDynamicRegsAndLR();
@@ -109,7 +114,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
fmov(v0.S(), GetSrc(IROp->Args[0].ID()).S()) ;
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<__uint128_t, float>(x0);
#else
blr(x0);
#endif
PopDynamicRegsAndLR();
@@ -128,7 +137,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<__uint128_t, double>(x0);
#else
blr(x0);
#endif
PopDynamicRegsAndLR();
@@ -153,7 +166,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
mov(w0, GetReg<RA_32>(IROp->Args[0].ID()));
}
ldr(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<__uint128_t, uint32_t>(x1);
#else
blr(x1);
#endif
PopDynamicRegsAndLR();
@@ -174,7 +191,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<float, uint64_t, uint64_t>(x2);
#else
blr(x2);
#endif
PopDynamicRegsAndLR();
@@ -193,7 +214,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<double, uint64_t, uint64_t>(x2);
#else
blr(x2);
#endif
PopDynamicRegsAndLR();
@@ -210,7 +235,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<double, double>(x0);
#else
blr(x0);
#endif
PopDynamicRegsAndLR();
@@ -229,7 +258,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
mov(v1.D(), GetSrc(IROp->Args[1].ID()).D());
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<double, double, double>(x0);
#else
blr(x0);
#endif
PopDynamicRegsAndLR();
@@ -249,7 +282,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint32_t, uint64_t, uint64_t>(x2);
#else
blr(x2);
#endif
PopDynamicRegsAndLR();
@@ -267,7 +304,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint32_t, uint64_t, uint64_t>(x2);
#else
blr(x2);
#endif
PopDynamicRegsAndLR();
@@ -285,7 +326,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t>(x2);
#else
blr(x2);
#endif
PopDynamicRegsAndLR();
@@ -306,8 +351,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w3, GetSrc(IROp->Args[1].ID()).V8H(), 4);
ldr(x4, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t>(x4);
#else
blr(x4);
#endif
PopDynamicRegsAndLR();
FillStaticRegs();
@@ -324,7 +372,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t>(x2);
#else
blr(x2);
#endif
PopDynamicRegsAndLR();
@@ -347,7 +399,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
umov(w3, GetSrc(IROp->Args[1].ID()).V8H(), 4);
ldr(x4, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t, uint64_t, uint64_t>(x4);
#else
blr(x4);
#endif
PopDynamicRegsAndLR();
@@ -426,6 +482,7 @@ void Arm64JITCore::Op_NoOp(IR::IROp_Header *IROp, IR::NodeID Node) {
Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
, Arm64Emitter(ctx, 0)
, HostSupportsSVE{ctx->HostFeatures.SupportsAVX}
, CTX {ctx} {
RAPass = Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA");
@@ -472,7 +529,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
// Common
auto &Common = ThreadState->CurrentFrame->Pointers.Common;
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
@@ -493,7 +550,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
// Platform Specific
auto &AArch64 = ThreadState->CurrentFrame->Pointers.AArch64;
AArch64.LUDIV = reinterpret_cast<uint64_t>(LUDIV);
AArch64.LDIV = reinterpret_cast<uint64_t>(LDIV);
AArch64.LUREM = reinterpret_cast<uint64_t>(LUREM);
@@ -510,6 +567,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
return Thread->CTX->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
}, true);
#ifdef _M_ARM_64
CTX->SignalDelegation->RegisterHostSignalHandler(SIGBUS, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
if (!Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) {
// Wasn't a sigbus in JIT code
@@ -518,6 +576,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
return FEXCore::ArchHelpers::Arm64::HandleSIGBUS(Thread->CTX->Config.ParanoidTSO(), Signal, info, ucontext);
}, true);
#endif
}
void Arm64JITCore::EmitDetectionString() {
@@ -529,7 +588,7 @@ void Arm64JITCore::EmitDetectionString() {
void Arm64JITCore::ClearCache() {
// Get the backing code buffer
auto CodeBuffer = GetEmptyCodeBuffer();
*GetBuffer() = vixl::CodeBuffer(CodeBuffer->Ptr, CodeBuffer->Size);
EmitDetectionString();
@@ -724,10 +783,12 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
SpillSlots = RAData->SpillSlots();
if (SpillSlots) {
if (IsImmAddSub(SpillSlots * 16)) {
sub(sp, sp, SpillSlots * 16);
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
if (IsImmAddSub(TotalSpillSlotsSize)) {
sub(sp, sp, TotalSpillSlotsSize);
} else {
LoadConstant(x0, SpillSlots * 16);
LoadConstant(x0, TotalSpillSlotsSize);
sub(sp, sp, x0);
}
}
@@ -795,14 +856,17 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
}
void Arm64JITCore::ResetStack() {
if (SpillSlots == 0)
if (SpillSlots == 0) {
return;
}
if (IsImmAddSub(SpillSlots * 16)) {
add(sp, sp, SpillSlots * 16);
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
if (IsImmAddSub(TotalSpillSlotsSize)) {
add(sp, sp, TotalSpillSlotsSize);
} else {
// Too big to fit in a 12bit immediate
LoadConstant(x0, SpillSlots * 16);
LoadConstant(x0, TotalSpillSlotsSize);
add(sp, sp, x0);
}
}
+1 -16
View File
@@ -23,16 +23,6 @@ $end_info$
#include <utility>
#include <vector>
#define STATE x28
#define TMP1 x0
#define TMP2 x1
#define TMP3 x2
#define TMP4 x3
#define VTMP1 v1
#define VTMP2 v2
#define VTMP3 v3
namespace FEXCore::Core {
struct InternalThreadState;
}
@@ -66,6 +56,7 @@ public:
private:
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
const bool HostSupportsSVE{};
Label *PendingTargetLabel;
FEXCore::Context::Context *CTX;
@@ -364,13 +355,10 @@ private:
///< Move ops
DEF_OP(ExtractElementPair);
DEF_OP(CreateElementPair);
DEF_OP(Mov);
///< Vector ops
DEF_OP(VectorZero);
DEF_OP(VectorImm);
DEF_OP(SplatVector2);
DEF_OP(SplatVector4);
DEF_OP(VMov);
DEF_OP(VAnd);
DEF_OP(VBic);
@@ -429,8 +417,6 @@ private:
DEF_OP(VUShrS);
DEF_OP(VSShrS);
DEF_OP(VInsElement);
DEF_OP(VInsScalarElement);
DEF_OP(VExtractElement);
DEF_OP(VDupElement);
DEF_OP(VExtr);
DEF_OP(VSLI);
@@ -440,7 +426,6 @@ private:
DEF_OP(VShlI);
DEF_OP(VUShrNI);
DEF_OP(VUShrNI2);
DEF_OP(VBitcast);
DEF_OP(VSXTL);
DEF_OP(VSXTL2);
DEF_OP(VUXTL);
@@ -446,9 +446,9 @@ DEF_OP(StoreContextIndexed) {
}
DEF_OP(SpillRegister) {
auto Op = IROp->C<IR::IROp_SpillRegister>();
const auto Op = IROp->C<IR::IROp_SpillRegister>();
const uint8_t OpSize = IROp->Size;
const uint32_t SlotOffset = Op->Slot * 16;
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
if (Op->Class == FEXCore::IR::GPRClass) {
switch (OpSize) {
@@ -468,23 +468,36 @@ DEF_OP(SpillRegister) {
str(GetReg<RA_64>(Op->Value.ID()), MemOperand(sp, SlotOffset));
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
default:
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
break;
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
const auto Src = GetSrc(Op->Value.ID());
switch (OpSize) {
case 4: {
str(GetSrc(Op->Value.ID()).S(), MemOperand(sp, SlotOffset));
str(Src.S(), MemOperand(sp, SlotOffset));
break;
}
case 8: {
str(GetSrc(Op->Value.ID()).D(), MemOperand(sp, SlotOffset));
str(Src.D(), MemOperand(sp, SlotOffset));
break;
}
case 16: {
str(GetSrc(Op->Value.ID()), MemOperand(sp, SlotOffset));
str(Src, MemOperand(sp, SlotOffset));
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
case 32: {
// TODO: Eliminate ptrue with statically allocated predicate register.
ptrue(p7.VnB(), SVE_VL32);
mov(TMP3, SlotOffset);
st1b(Src.Z().VnB(), p7, SVEMemOperand(sp, TMP3));
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
@@ -492,9 +505,9 @@ DEF_OP(SpillRegister) {
}
DEF_OP(FillRegister) {
auto Op = IROp->C<IR::IROp_FillRegister>();
uint8_t OpSize = IROp->Size;
uint32_t SlotOffset = Op->Slot * 16;
const auto Op = IROp->C<IR::IROp_FillRegister>();
const uint8_t OpSize = IROp->Size;
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
if (Op->Class == FEXCore::IR::GPRClass) {
switch (OpSize) {
@@ -514,23 +527,36 @@ DEF_OP(FillRegister) {
ldr(GetReg<RA_64>(Node), MemOperand(sp, SlotOffset));
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
default:
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
break;
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
const auto Dst = GetDst(Node);
switch (OpSize) {
case 4: {
ldr(GetDst(Node).S(), MemOperand(sp, SlotOffset));
ldr(Dst.S(), MemOperand(sp, SlotOffset));
break;
}
case 8: {
ldr(GetDst(Node).D(), MemOperand(sp, SlotOffset));
ldr(Dst.D(), MemOperand(sp, SlotOffset));
break;
}
case 16: {
ldr(GetDst(Node), MemOperand(sp, SlotOffset));
ldr(Dst, MemOperand(sp, SlotOffset));
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
case 32: {
// TODO: Eliminate ptrue with statically allocated predicate register.
ptrue(p7.VnB(), SVE_VL32);
mov(TMP3, SlotOffset);
ld1b(Dst.Z().VnB(), p7.Zeroing(), SVEMemOperand(sp, TMP3));
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
@@ -119,8 +119,11 @@ DEF_OP(SetRoundingMode) {
mrs(TMP1, FPCR);
// vixl simulator doesn't support anything beyond ties-to-even rounding
#ifndef VIXL_SIMULATOR
// Insert the rounding flags
bfi(TMP1, TMP2, 22, 2);
#endif
// Insert the FTZ flag
lsr(TMP2, Src, 2);
@@ -134,6 +137,7 @@ DEF_OP(Print) {
auto Op = IROp->C<IR::IROp_Print>();
PushDynamicRegsAndLR();
SpillStaticRegs();
if (IsGPR(Op->Value.ID())) {
mov(x0, GetReg<RA_64>(Op->Value.ID()));
@@ -145,10 +149,10 @@ DEF_OP(Print) {
fmov(x1, GetSrc(Op->Value.ID()).V1D(), 1);
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.PrintVectorValue)));
}
SpillStaticRegs();
blr(x3);
FillStaticRegs();
blr(x3);
FillStaticRegs();
PopDynamicRegsAndLR();
}
@@ -68,17 +68,11 @@ DEF_OP(CreateElementPair) {
}
}
DEF_OP(Mov) {
auto Op = IROp->C<IR::IROp_Mov>();
mov(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Value.ID()));
}
#undef DEF_OP
void Arm64JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
REGISTER_OP(MOV, Mov);
#undef REGISTER_OP
}
}
File diff suppressed because it is too large. Load diff
@@ -33,7 +33,7 @@ namespace FEXCore::CPU {
DEF_OP(SignalReturn) {
// Adjust the stack first for a regular return
if (SpillSlots) {
add(rsp, SpillSlots * 16); // + 8 to consume return address
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
}
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler)]);
@@ -42,7 +42,7 @@ DEF_OP(SignalReturn) {
DEF_OP(CallbackReturn) {
// Adjust the stack first for a regular return
if (SpillSlots) {
add(rsp, SpillSlots * 16); // + 8 to consume return address
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
}
// Make sure to adjust the refcounter so we don't clear the cache now
@@ -71,7 +71,7 @@ DEF_OP(ExitFunction) {
if (SpillSlots) {
add(rsp, SpillSlots * 16);
add(rsp, SpillSlots * MaxSpillSlotSize);
}
uint64_t NewRIP;
+29 -17
View File
@@ -25,6 +25,7 @@ $end_info$
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/IR/RegisterAllocationData.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <algorithm>
@@ -59,32 +60,42 @@ static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) {
namespace FEXCore::CPU {
void X86JITCore::PushRegs() {
sub(rsp, 16 * RAXMM_x.size());
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
sub(rsp, AVXRegSize * RAXMM_x.size());
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
movaps(ptr[rsp + i * 16], RAXMM_x[i]);
vmovups(ptr[rsp + i * AVXRegSize], ToYMM(RAXMM_x[i]));
}
for (auto &Reg : RA64)
for (const auto &Reg : RA64) {
push(Reg);
}
auto NumPush = RA64.size();
if (NumPush & 1)
sub(rsp, 8); // Align
const auto NumPush = RA64.size();
if ((NumPush & 1) != 0) {
// Align
sub(rsp, 8);
}
}
void X86JITCore::PopRegs() {
auto NumPush = RA64.size();
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
const auto NumPush = RA64.size();
if (NumPush & 1)
add(rsp, 8); // Align
for (uint32_t i = RA64.size(); i > 0; --i)
pop(RA64[i - 1]);
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
movaps(RAXMM_x[i], ptr[rsp + i * 16]);
if ((NumPush & 1) != 0) {
// Align
add(rsp, 8);
}
add(rsp, 16 * RAXMM_x.size());
for (uint32_t i = RA64.size(); i > 0; --i) {
pop(RA64[i - 1]);
}
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
vmovups(ToYMM(RAXMM_x[i]), ptr[rsp + i * AVXRegSize]);
}
add(rsp, AVXRegSize * RAXMM_x.size());
}
void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
@@ -293,7 +304,8 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
case FABI_UNKNOWN:
default:
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}", FEXCore::IR::GetName(IROp->Op), Info.ABI);
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}",
IR::GetName(IROp->Op), ToUnderlying(Info.ABI));
#endif
break;
}
@@ -597,7 +609,7 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
SpillSlots = RAData->SpillSlots();
if (SpillSlots) {
sub(rsp, SpillSlots * 16);
sub(rsp, SpillSlots * MaxSpillSlotSize);
}
#ifdef BLOCKSTATS
@@ -181,6 +181,10 @@ private:
[[nodiscard]] Xbyak::Xmm GetSrc(IR::NodeID Node) const;
[[nodiscard]] Xbyak::Xmm GetDst(IR::NodeID Node) const;
[[nodiscard]] static Xbyak::Ymm ToYMM(const Xbyak::Xmm& xmm) {
return Xbyak::Ymm{xmm.getIdx()};
}
[[nodiscard]] Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset,
IR::MemOffsetType OffsetType, uint8_t OffsetScale) const;
@@ -205,7 +209,7 @@ private:
* @brief Current guest RIP entrypoint
*/
uint8_t *GuestEntry{};
using SetCC = void (X86JITCore::*)(const Operand& op);
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
using JCC = void (X86JITCore::*)(const Label& label, LabelType type);
@@ -362,12 +366,10 @@ private:
///< Move ops
DEF_OP(ExtractElementPair);
DEF_OP(CreateElementPair);
DEF_OP(Mov);
///< Vector ops
DEF_OP(VectorZero);
DEF_OP(VectorImm);
DEF_OP(SplatVector);
DEF_OP(VMov);
DEF_OP(VAnd);
DEF_OP(VBic);
@@ -426,8 +428,6 @@ private:
DEF_OP(VUShrS);
DEF_OP(VSShrS);
DEF_OP(VInsElement);
DEF_OP(VInsScalarElement);
DEF_OP(VExtractElement);
DEF_OP(VDupElement);
DEF_OP(VExtr);
DEF_OP(VSLI);
@@ -437,7 +437,6 @@ private:
DEF_OP(VShlI);
DEF_OP(VUShrNI);
DEF_OP(VUShrNI2);
DEF_OP(VBitcast);
DEF_OP(VSXTL);
DEF_OP(VSXTL2);
DEF_OP(VUXTL);
@@ -333,10 +333,10 @@ DEF_OP(StoreContextIndexed) {
}
DEF_OP(SpillRegister) {
auto Op = IROp->C<IR::IROp_SpillRegister>();
uint8_t OpSize = IROp->Size;
const auto Op = IROp->C<IR::IROp_SpillRegister>();
const uint8_t OpSize = IROp->Size;
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
uint32_t SlotOffset = Op->Slot * 16;
if (Op->Class == FEXCore::IR::GPRClass) {
switch (OpSize) {
case 1: {
@@ -355,36 +355,44 @@ DEF_OP(SpillRegister) {
mov(qword [rsp + SlotOffset], GetSrc<RA_64>(Op->Value.ID()));
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
default:
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
break;
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
const auto Src = GetSrc(Op->Value.ID());
switch (OpSize) {
case 4: {
movss(dword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
movss(dword [rsp + SlotOffset], Src);
break;
}
case 8: {
movsd(qword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
movsd(qword [rsp + SlotOffset], Src);
break;
}
case 16: {
movaps(xword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
movaps(xword [rsp + SlotOffset], Src);
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
case 32: {
vmovaps(yword [rsp + SlotOffset], ToYMM(Src));
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
}
}
DEF_OP(FillRegister) {
auto Op = IROp->C<IR::IROp_FillRegister>();
uint8_t OpSize = IROp->Size;
const auto Op = IROp->C<IR::IROp_FillRegister>();
const uint8_t OpSize = IROp->Size;
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
uint32_t SlotOffset = Op->Slot * 16;
if (Op->Class == FEXCore::IR::GPRClass) {
switch (OpSize) {
case 1: {
@@ -403,23 +411,33 @@ DEF_OP(FillRegister) {
mov(GetDst<RA_64>(Node), qword [rsp + SlotOffset]);
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
default:
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
break;
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
const auto Dst = GetDst(Node);
switch (OpSize) {
case 4: {
movss(GetDst(Node), dword [rsp + SlotOffset]);
movss(Dst, dword [rsp + SlotOffset]);
break;
}
case 8: {
movsd(GetDst(Node), qword [rsp + SlotOffset]);
movsd(Dst, qword [rsp + SlotOffset]);
break;
}
case 16: {
movaps(GetDst(Node), xword [rsp + SlotOffset]);
movaps(Dst, xword [rsp + SlotOffset]);
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
case 32: {
vmovaps(ToYMM(Dst), yword [rsp + SlotOffset]);
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
@@ -47,7 +47,7 @@ DEF_OP(Break) {
auto Op = IROp->C<IR::IROp_Break>();
if (SpillSlots) {
add(rsp, SpillSlots * 16);
add(rsp, SpillSlots * MaxSpillSlotSize);
}
mov(byte [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.FaultToTopAndGeneratedException)], 1);
@@ -73,17 +73,11 @@ DEF_OP(CreateElementPair) {
}
}
DEF_OP(Mov) {
auto Op = IROp->C<IR::IROp_Mov>();
mov (GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Value.ID()));
}
#undef DEF_OP
void X86JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
REGISTER_OP(MOV, Mov);
#undef REGISTER_OP
}
}
File diff suppressed because it is too large. Load diff
+14 -4
View File
@@ -126,10 +126,20 @@ void OpDispatchBuilder::ThunkOp(OpcodeArgs) {
const uint8_t GPRSize = CTX->GetGPRSize();
uint8_t *sha256 = (uint8_t *)(Op->PC + 2);
_Thunk(
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RDI)),
*reinterpret_cast<SHA256Sum*>(sha256)
);
if (CTX->Config.Is64BitMode) {
// x86-64 ABI puts the function argument in RDI
_Thunk(
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RDI)),
*reinterpret_cast<SHA256Sum*>(sha256)
);
}
else {
// x86 fastcall ABI puts the function argument in ECX
_Thunk(
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RCX)),
*reinterpret_cast<SHA256Sum*>(sha256)
);
}
auto Constant = _Constant(GPRSize);
auto OldSP = _LoadContext(GPRSize, GPRClass, RSPOffset);
@@ -74,13 +74,12 @@ void OpDispatchBuilder::MOVLPOp(OpcodeArgs) {
// xmm, xmm is movhlps special case
if (Op->Src[0].IsGPR()) {
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, 8, 16);
Src = _VExtractElement(16, 8, Src, 1);
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
auto Result = _VInsElement(16, 8, 0, 1, Dest, Src);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result, 16, 16);
}
else {
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, 8, 16);
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
auto Result = _VInsElement(16, 8, 0, 0, Dest, Src);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result, 8, 16);
}
}
@@ -112,7 +111,7 @@ void OpDispatchBuilder::MOVSSOp(OpcodeArgs) {
// MOVSS xmm1, xmm2
OrderedNode *Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, 16, Op->Flags, -1);
OrderedNode *Src = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], 4, Op->Flags, -1);
auto Result = _VInsScalarElement(16, 4, 0, Dest, Src);
auto Result = _VInsElement(16, 4, 0, 0, Dest, Src);
StoreResult(FPRClass, Op, Result, -1);
}
else if (Op->Dest.IsGPR()) {
@@ -133,7 +132,7 @@ void OpDispatchBuilder::MOVSDOp(OpcodeArgs) {
// xmm1[63:0] <- xmm2[63:0]
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
auto Result = _VInsElement(16, 8, 0, 0, Dest, Src);
StoreResult(FPRClass, Op, Result, -1);
}
else if (Op->Dest.IsGPR()) {
@@ -335,7 +334,7 @@ void OpDispatchBuilder::VectorScalarALUOp(OpcodeArgs) {
if (Size != ElementSize) {
// Insert the lower bits
Result = _VInsScalarElement(Size, ElementSize, 0, Dest, Result);
Result = _VInsElement(Size, ElementSize, 0, 0, Dest, Result);
}
StoreResult(FPRClass, Op, Result, -1);
@@ -381,7 +380,7 @@ void OpDispatchBuilder::VectorUnaryOp(OpcodeArgs) {
if constexpr (Scalar) {
// Insert the lower bits
auto Result = _VInsScalarElement(GetSrcSize(Op), ElementSize, 0, Dest, ALUOp);
auto Result = _VInsElement(GetSrcSize(Op), ElementSize, 0, 0, Dest, ALUOp);
StoreResult(FPRClass, Op, Result, -1);
}
else {
@@ -978,7 +977,8 @@ void OpDispatchBuilder::PAVGOp<2>(OpcodeArgs);
void OpDispatchBuilder::MOVDDUPOp(OpcodeArgs) {
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Res = _SplatVector2(Src);
OrderedNode *Res = _VDupElement(16, GetSrcSize(Op), Src, 0);
StoreResult(FPRClass, Op, Res, -1);
}
@@ -992,7 +992,7 @@ void OpDispatchBuilder::CVTGPR_To_FPR(OpcodeArgs) {
OrderedNode *Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, 16, Op->Flags, -1);
Src = _VInsScalarElement(16, DstElementSize, 0, Dest, Src);
Src = _VInsElement(16, DstElementSize, 0, 0, Dest, Src);
StoreResult(FPRClass, Op, Src, -1);
}
@@ -1091,7 +1091,7 @@ void OpDispatchBuilder::Scalar_CVT_Float_To_Float(OpcodeArgs) {
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
Src = _Float_FToF(DstElementSize, SrcElementSize, Src);
Src = _VInsScalarElement(16, DstElementSize, 0, Dest, Src);
Src = _VInsElement(16, DstElementSize, 0, 0, Dest, Src);
StoreResult(FPRClass, Op, Src, -1);
}
@@ -1281,7 +1281,7 @@ void OpDispatchBuilder::VFCMPOp(OpcodeArgs) {
if constexpr (Scalar) {
// Insert the lower bits
Result = _VInsScalarElement(GetDstSize(Op), ElementSize, 0, Dest, Result);
Result = _VInsElement(GetDstSize(Op), ElementSize, 0, 0, Dest, Result);
}
StoreResult(FPRClass, Op, Result, -1);
@@ -1714,8 +1714,8 @@ void OpDispatchBuilder::PFNACCOp(OpcodeArgs) {
OrderedNode *ResSubSrc{};
OrderedNode *ResSubDest{};
auto UpperSubDest = _VExtractElement(Size, 4, Dest, 1);
auto UpperSubSrc = _VExtractElement(Size, 4, Src, 1);
auto UpperSubDest = _VDupElement(Size, 4, Dest, 1);
auto UpperSubSrc = _VDupElement(Size, 4, Src, 1);
ResSubDest = _VFSub(4, 4, Dest, UpperSubDest);
ResSubSrc = _VFSub(4, 4, Src, UpperSubSrc);
@@ -1733,7 +1733,7 @@ void OpDispatchBuilder::PFPNACCOp(OpcodeArgs) {
OrderedNode *ResAdd{};
OrderedNode *ResSub{};
auto UpperSubDest = _VExtractElement(Size, 4, Dest, 1);
auto UpperSubDest = _VDupElement(Size, 4, Dest, 1);
ResSub = _VFSub(4, 4, Dest, UpperSubDest);
ResAdd = _VFAddP(Size, 4, Src, Src);
@@ -1866,8 +1866,6 @@ void OpDispatchBuilder::PMADDWD(OpcodeArgs) {
if (Size == 8) {
Size <<= 1;
Src1 = _VBitcast(Size, 2, Src1);
Src2 = _VBitcast(Size, 2, Src2);
}
auto Src1_L = _VSXTL(Size, 2, Src1); // [15:0 ], [31:16], [32:47 ], [63:48 ]
@@ -1954,9 +1952,6 @@ void OpDispatchBuilder::PMULHW(OpcodeArgs) {
OrderedNode *Res{};
if (Size == 8) {
Dest = _VBitcast(Size * 2, 2, Dest);
Src = _VBitcast(Size * 2, 2, Src);
// Implementation is more efficient for 8byte registers
if (Signed)
Res = _VSMull(Size * 2, 2, Dest, Src);
@@ -2333,7 +2328,7 @@ void OpDispatchBuilder::VectorRound(OpcodeArgs) {
if constexpr (Scalar) {
// Insert the lower bits
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
auto Result = _VInsScalarElement(GetDstSize(Op), ElementSize, 0, Dest, Src);
auto Result = _VInsElement(GetDstSize(Op), ElementSize, 0, 0, Dest, Src);
StoreResult(FPRClass, Op, Result, -1);
}
else {
@@ -1185,7 +1185,7 @@ void OpDispatchBuilder::X87FNSAVE(OpcodeArgs) {
// upper 16 bits [79:64]
_StoreMem(FPRClass, 8, ST0Location, data, 1);
ST0Location = _Add(ST0Location, _Constant(8));
auto topBytes = _VExtractElement(16, 2, data, 4);
auto topBytes = _VDupElement(16, 2, data, 4);
_StoreMem(FPRClass, 2, ST0Location, topBytes, 1);
// reset to default
@@ -996,7 +996,7 @@ void OpDispatchBuilder::X87FNSAVEF64(OpcodeArgs) {
// upper 16 bits [79:64]
_StoreMem(FPRClass, 8, ST0Location, data, 1);
ST0Location = _Add(ST0Location, _Constant(8));
auto topBytes = _VExtractElement(16, 2, data, 4);
auto topBytes = _VDupElement(16, 2, data, 4);
_StoreMem(FPRClass, 2, ST0Location, topBytes, 1);
// reset to default
+19 -6
View File
@@ -28,7 +28,9 @@ $end_info$
#include <string>
#include <utility>
#ifdef ENABLE_JEMALLOC
#include "jemalloc/jemalloc.h"
#endif
struct LoadlibArgs {
const char *Name;
@@ -137,7 +139,7 @@ namespace FEXCore {
// sha256(fex:allocate_host_trampoline_for_guest_function)
{ 0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a },
&AllocateHostTrampolineForGuestFunction
}
},
};
// Can't be a string_view. We need to keep a copy of the library name in-case string_view pointer goes away.
@@ -241,12 +243,17 @@ namespace FEXCore {
* and host heap pointers.
*/
static void IsHostHeapAllocation(void* ArgsRV) {
#ifdef ENABLE_JEMALLOC
struct ArgsRV_t {
void* ptr;
bool rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = je_is_known_allocation(args->ptr);
#else
// Thunks usage without jemalloc isn't supported
ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information");
#endif
}
static void LoadLib(void *ArgsV) {
@@ -313,7 +320,7 @@ namespace FEXCore {
}
}
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) {
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) override {
std::shared_lock lk(ThunksMutex);
@@ -326,13 +333,19 @@ namespace FEXCore {
}
}
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) {
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) override {
::Thread = Thread;
}
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) override {
for (auto & Definition : Definitions) {
Thunks.emplace(Definition.Sum, Definition.ThunkFunction);
}
}
};
ThunkHandler* ThunkHandler::Create() {
return new ThunkHandler_impl();
return new ThunkHandler_impl();
}
/**
@@ -358,7 +371,7 @@ namespace FEXCore {
* is to allocate the trampoline for a given GuestTarget/GuestUnpacker on the guest-side,
* and provide the HostPacker host-side.
*/
__attribute__((visibility("default")))
FEX_DEFAULT_VISIBILITY
HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) {
LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function");
@@ -416,7 +429,7 @@ namespace FEXCore {
return HostTrampoline;
}
__attribute__((visibility("default")))
FEX_DEFAULT_VISIBILITY
void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) {
auto& Trampoline = GetInstanceInfo(TrampolineAddress);
+6
View File
@@ -6,6 +6,10 @@ $end_info$
#pragma once
#include <FEXCore/IR/IR.h>
#include <vector>
namespace FEXCore::Context {
struct Context;
}
@@ -28,5 +32,7 @@ namespace FEXCore {
virtual ~ThunkHandler() { }
static ThunkHandler* Create();
virtual void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) = 0;
};
};
+25 -53
View File
@@ -302,10 +302,6 @@
}
},
"Moves": {
"GPR = Mov GPR:$Value": {
"DestSize": "GetOpSize(_Value)"
},
"GPR = ExtractElementPair GPRPair:$Pair, u8:$Element": {
"Desc": ["Extracts a register for the register pair"],
"DestSize": "GetOpSize(_Pair) >> 1"
@@ -364,11 +360,11 @@
},
"StoreContext u8:#ByteSize, RegisterClass:$Class, SSA:$Value, u32:$Offset": {
"Desc": ["Stores a value to the context with offset",
"Ctx[Offset] = Value",
"Zero Extends if value's type is too small",
"Truncates if value's type is too large"
],
"Desc": ["Stores a value to the context with offset",
"Ctx[Offset] = Value",
"Zero Extends if value's type is too small",
"Truncates if value's type is too large"
],
"HasSideEffects": true,
"DestSize": "ByteSize",
"EmitValidation": [
@@ -845,27 +841,25 @@
"DestSize": "std::max<uint8_t>(4, std::max<uint8_t>(GetOpSize(_TrueVal), GetOpSize(_FalseVal)))"
},
"GPR = Extr GPR:$Upper, GPR:$Lower, u8:$LSB": {
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
"It then extracts a bitfield width that size of a GPR from the LSB",
"Valid LSB range is 0-31 for 32bit and 0-63 for 64bit",
"<Size * 2> ConcatValue = $Upper:$Lower",
"Result = ConcatValue<LSB+Size - 1: LSB>"
]
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
"It then extracts a bitfield width that size of a GPR from the LSB",
"Valid LSB range is 0-31 for 32bit and 0-63 for 64bit",
"<Size * 2> ConcatValue = $Upper:$Lower",
"Result = ConcatValue<LSB+Size - 1: LSB>"
]
},
"GPR = PDep GPR:$Input, GPR:$Mask": {
"Desc": [
"Performs a parallel bit deposit.",
"Takes the contiguous low-order bits and deposits them into",
"the destination at the locations specified by the Mask."
]
"Desc": ["Performs a parallel bit deposit.",
"Takes the contiguous low-order bits and deposits them into",
"the destination at the locations specified by the Mask."
]
},
"GPR = PExt GPR:$Input, GPR:$Mask": {
"Desc": [
"Performs a parallel bit extract.",
"Each bit set in the mask will select the corresponding bit in the Input",
"and transfers them to the lower contiguous bits in the destination."
]
"Desc": ["Performs a parallel bit extract.",
"Each bit set in the mask will select the corresponding bit in the Input",
"and transfers them to the lower contiguous bits in the destination."
]
},
"GPR = LDiv GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
@@ -924,15 +918,6 @@
}
},
"Vector": {
"FPR = SplatVector2 FPR:$Scalar": {
"NumElements": "2",
"DestSize": "GetOpSize(_Scalar) * 2"
},
"FPR = SplatVector4 FPR:$Scalar": {
"NumElements": "4",
"DestSize": "GetOpSize(_Scalar) * 4"
},
"FPR = VMov u8:#RegisterSize, FPR:$Source": {
"Desc" : ["Copy vector register",
"When Register size is smaller than Source register size,",
@@ -941,12 +926,6 @@
"DestSize": "RegisterSize"
},
"FPR = VBitcast u8:#RegisterSize, u8:#ElementSize, FPR:$Source": {
"Desc": ["Workaround for issue with LLVM breaking when loading scalar elements to vectors"],
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VectorZero u8:#RegisterSize": {
"Desc": ["Generates a vector zero",
"Useful to generate a zero vector without any previous dependencies"
@@ -1038,9 +1017,6 @@
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VExtractElement u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$Index": {
"DestSize": "ElementSize"
},
"FPR = VDupElement u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$Index": {
"Desc": ["Duplicates one element from the source register across the whole register"],
"DestSize": "RegisterSize",
@@ -1124,9 +1100,9 @@
},
"FPR = VRev64 u8:#RegisterSize, u8:#ElementSize, FPR:$Vector": {
"Desc" : ["Reverses elements in 64-bit halfwords",
"Available element size: 1byte, 2 byte, 4 byte"
],
"Desc" : ["Reverses elements in 64-bit halfwords",
"Available element size: 1byte, 2 byte, 4 byte"
],
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
@@ -1320,10 +1296,6 @@
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VInsScalarElement u8:#RegisterSize, u8:#ElementSize, u8:$DestIdx, FPR:$DestVector, FPR:$SrcScalar": {
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VInsGPR u8:#RegisterSize, u8:#ElementSize, u8:$DestIdx, FPR:$DestVector, GPR:$Src": {
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
@@ -1587,9 +1559,9 @@
"DestSize": "16"
},
"GPR = F80Cmp FPR:$X80Src1, FPR:$X80Src2, u32:$Flags": {
"Desc": ["Does a scalar unordered compare and stores the asked for flags in to a GPR",
"Ordering flag result is true if either float input is NaN"
],
"Desc": ["Does a scalar unordered compare and stores the asked for flags in to a GPR",
"Ordering flag result is true if either float input is NaN"
],
"DestSize": "4"
},
"FPR = F80BCDLoad FPR:$X80Src": {
+9 -9
View File
@@ -6,7 +6,7 @@ $end_info$
*/
#if defined(_M_ARM_64)
#if JIT_ARM64
//aarch64 heuristics
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
@@ -45,20 +45,20 @@ uint64_t getMask(IROp_Header* Op) {
return (~0ULL) >> (64 - NumBits);
}
#ifdef _M_X86_64
// very lazy heuristics
static bool IsImmLogical(uint64_t imm, unsigned width) { return imm < 0x8000'0000; }
static bool IsImmAddSub(uint64_t imm) { return imm < 0x8000'0000; }
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
return Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8;
}
#elif defined(_M_ARM_64)
#if JIT_ARM64
//aarch64 heuristics
static bool IsImmLogical(uint64_t imm, unsigned width) { if (width < 32) width = 32; return vixl::aarch64::Assembler::IsImmLogical(imm, width); }
static bool IsImmAddSub(uint64_t imm) { return vixl::aarch64::Assembler::IsImmAddSub(imm); }
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
return Scale == AccessSize;
}
#elif JIT_X86_64
// very lazy heuristics
static bool IsImmLogical(uint64_t imm, unsigned width) { return imm < 0x8000'0000; }
static bool IsImmAddSub(uint64_t imm) { return imm < 0x8000'0000; }
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
return Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8;
}
#else
#error No inline constant heuristics for this target
#endif
@@ -449,7 +449,6 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) {
* %ssa26 i128 = LoadMem %ssa25 i64, 0x10
* (%%ssa27) StoreContext %ssa26 i128, 0x10, 0xb0
* %ssa28 i128 = LoadContext 0x10, 0x90
* %ssa29 i128 = VBitcast %ssa26 i128
*
* eg.
* %ssa6 i128 = LoadContext 0x10, 0x90
@@ -462,13 +461,11 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) {
* eg.
* (%%ssa189) StoreContext %ssa188 i128, 0x10, 0xa0
* %ssa190 i128 = LoadContext 0x10, 0x90
* %ssa191 i128 = VBitcast %ssa188 i128
* %ssa192 i128 = VAdd %ssa191 i128, %ssa190 i128, 0x10, 0x4
* %ssa192 i128 = VAdd %ssa188 i128, %ssa190 i128, 0x10, 0x4
* (%%ssa193) StoreContext %ssa192 i128, 0x10, 0xa0
* Converts to
* %ssa173 i128 = LoadContext 0x10, 0x90
* %ssa174 i128 = VBitcast %ssa172 i128
* %ssa175 i128 = VAdd %ssa174 i128, %ssa173 i128, 0x10, 0x4
* %ssa175 i128 = VAdd %ssa172 i128, %ssa173 i128, 0x10, 0x4
* (%%ssa176) StoreContext %ssa175 i128, 0x10, 0xa0
*/
+109 -79
View File
@@ -6,6 +6,10 @@
#include <FEXHeaderUtils/TypeDefines.h>
#include <array>
#include <asm-generic/errno-base.h>
#include <cctype>
#include <cstdio>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/user.h>
#ifdef ENABLE_JEMALLOC
@@ -41,6 +45,8 @@ namespace FEXCore::Allocator {
FREE_Hook free {::free};
#endif
uint64_t HostVASize{};
using GLIBC_MALLOC_Hook = void*(*)(size_t, const void *caller);
using GLIBC_REALLOC_Hook = void*(*)(void*, size_t, const void *caller);
using GLIBC_FREE_Hook = void(*)(void*, const void *caller);
@@ -93,6 +99,10 @@ namespace FEXCore::Allocator {
#pragma GCC diagnostic pop
FEX_DEFAULT_VISIBILITY size_t DetermineVASize() {
if (HostVASize) {
return HostVASize;
}
static constexpr std::array<uintptr_t, 7> TLBSizes = {
57,
52,
@@ -123,6 +133,7 @@ namespace FEXCore::Allocator {
};
if (Find(Size)) {
HostVASize = Bits;
return Bits;
}
}
@@ -131,91 +142,119 @@ namespace FEXCore::Allocator {
FEX_UNREACHABLE;
}
PtrCache* StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
PtrCache *Cache{};
uint64_t CacheSize{};
uint64_t CurrentCacheOffset = 0;
constexpr std::array<size_t, 10> ReservedVMARegionSizes = {{
// Anything larger than 64GB fails out
64ULL * 1024 * 1024 * 1024, // 64GB
32ULL * 1024 * 1024 * 1024, // 32GB
16ULL * 1024 * 1024 * 1024, // 16GB
4ULL * 1024 * 1024 * 1024, // 4GB
1ULL * 1024 * 1024 * 1024, // 1GB
512ULL * 1024 * 1024, // 512MB
128ULL * 1024 * 1024, // 128MB
32ULL * 1024 * 1024, // 32MB
1ULL * 1024 * 1024, // 1MB
4096ULL // One page
}};
constexpr size_t AllocationSizeMaxIndex = ReservedVMARegionSizes.size() - 1;
uint64_t CurrentSizeIndex = 0;
#define STEAL_LOG(...) // fprintf(stderr, __VA_ARGS__)
int PROT_FLAGS = PROT_READ | PROT_WRITE;
for (size_t MemoryOffset = Begin; MemoryOffset < End;) {
size_t AllocationSize = ReservedVMARegionSizes[CurrentSizeIndex];
size_t MemoryOffsetUpper = MemoryOffset + AllocationSize;
std::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
std::vector<MemoryRegion> Regions;
int MapsFD = open("/proc/self/maps", O_RDONLY);
LogMan::Throw::AFmt(MapsFD != -1, "Failed to open /proc/self/maps");
// If we would go above the upper bound on size then try the next size
if (MemoryOffsetUpper > End) {
++CurrentSizeIndex;
continue;
enum {ParseBegin, ParseEnd, ScanEnd} State = ParseBegin;
uintptr_t RegionBegin = 0;
uintptr_t RegionEnd = 0;
char Buffer[2048];
const char *Cursor;
ssize_t Remaining = 0;
for(;;) {
if (Remaining == 0) {
do {
Remaining = read(MapsFD, Buffer, sizeof(Buffer));
} while ( Remaining == -1 && errno == EAGAIN);
Cursor = Buffer;
}
void *Ptr = ::mmap(reinterpret_cast<void*>(MemoryOffset), AllocationSize, PROT_FLAGS, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
if (Remaining == 0 && State == ParseBegin) {
STEAL_LOG("[%d] EndOfFile; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
// If we managed to allocate and not get the address we want then unmap it
// This happens with kernels older than 4.17
if (reinterpret_cast<uintptr_t>(Ptr) + AllocationSize > End) {
::munmap(Ptr, AllocationSize);
Ptr = reinterpret_cast<void*>(~0ULL);
}
auto MapBegin = std::max(RegionEnd, Begin);
auto MapEnd = End;
// If we failed to allocate and we are on the smallest allocation size then just continue onward
// This page was unmappable
if (reinterpret_cast<uintptr_t>(Ptr) == ~0ULL && CurrentSizeIndex == AllocationSizeMaxIndex) {
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
continue;
}
STEAL_LOG(" MapBegin: %016lX MapEnd: %016lX\n", MapBegin, MapEnd);
// Congratulations we were able to map this bit
// Reset and claim it was available
if (reinterpret_cast<uintptr_t>(Ptr) != ~0ULL) {
if (!Cache) {
Cache = reinterpret_cast<PtrCache *>(Ptr);
CacheSize = AllocationSize;
PROT_FLAGS = PROT_NONE;
}
else {
Cache[CurrentCacheOffset] = {
.Ptr = static_cast<uint64_t>(reinterpret_cast<uint64_t>(Ptr)),
.Size = static_cast<uint64_t>(AllocationSize)
};
++CurrentCacheOffset;
if (MapEnd > MapBegin) {
STEAL_LOG(" Reserving\n");
auto MapSize = MapEnd - MapBegin;
auto Alloc = mmap((void*)MapBegin, MapSize, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({:x},{:x}) failed", MapBegin, MapSize);
LogMan::Throw::AFmt(Alloc == (void*)MapBegin, "mmap({},{:x}) returned {} instead of {:x}", Alloc, MapBegin);
Regions.push_back({(void*)MapBegin, MapSize});
}
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
close(MapsFD);
return Regions;
}
LogMan::Throw::AFmt(Remaining > 0, "Failed to parse /proc/self/maps");
auto c = *Cursor++;
Remaining--;
if (State == ScanEnd) {
if (c == '\n') {
State = ParseBegin;
}
continue;
}
// Couldn't allocate at this size
// Increase and continue
++CurrentSizeIndex;
if (State == ParseBegin) {
if (c == '-') {
STEAL_LOG("[%d] ParseBegin; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
auto MapBegin = std::max(RegionEnd, Begin);
auto MapEnd = std::min(RegionBegin, End);
STEAL_LOG(" MapBegin: %016lX MapEnd: %016lX\n", MapBegin, MapEnd);
if (MapEnd > MapBegin) {
STEAL_LOG(" Reserving\n");
auto MapSize = MapEnd - MapBegin;
auto Alloc = mmap((void*)MapBegin, MapSize, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({:x},{:x}) failed", MapBegin, MapSize);
LogMan::Throw::AFmt(Alloc == (void*)MapBegin, "mmap({},{:x}) returned {} instead of {:x}", Alloc, MapBegin);
Regions.push_back({(void*)MapBegin, MapSize});
}
RegionBegin = 0;
RegionEnd = 0;
State = ParseEnd;
continue;
} else {
LogMan::Throw::AFmt(std::isalpha(c) || std::isdigit(c), "Unexpected char '{}' in ParseBegin", c);
RegionBegin = (RegionBegin << 4) | (c <= '9' ? (c - '0') : (c - 'a' + 10));
}
}
if (State == ParseEnd) {
if (c == ' ') {
STEAL_LOG("[%d] ParseEnd; RegionBegin: %016lX RegionEnd: %016lX\n", __LINE__, RegionBegin, RegionEnd);
State = ScanEnd;
continue;
} else {
LogMan::Throw::AFmt(std::isalpha(c) || std::isdigit(c), "Unexpected char '{}' in ParseEnd", c);
RegionEnd = (RegionEnd << 4) | (c <= '9' ? (c - '0') : (c - 'a' + 10));
}
}
}
Cache[CurrentCacheOffset] = {
.Ptr = static_cast<uint64_t>(reinterpret_cast<uint64_t>(Cache)),
.Size = CacheSize,
};
return Cache;
ERROR_AND_DIE_FMT("unreachable");
}
PtrCache* Steal48BitVA() {
std::vector<MemoryRegion> Steal48BitVA() {
size_t Bits = FEXCore::Allocator::DetermineVASize();
if (Bits < 48) {
return nullptr;
return {};
}
uintptr_t Begin48BitVA = 0x0'8000'0000'0000ULL;
@@ -223,18 +262,9 @@ namespace FEXCore::Allocator {
return StealMemoryRegion(Begin48BitVA, End48BitVA);
}
void ReclaimMemoryRegion(PtrCache* Regions) {
if (Regions == nullptr) {
return;
}
for (size_t i = 0;; ++i) {
void *Ptr = reinterpret_cast<void*>(Regions[i].Ptr);
size_t Size = Regions[i].Size;
::munmap(Ptr, Size);
if (Ptr == Regions) {
break;
}
void ReclaimMemoryRegion(const std::vector<MemoryRegion> &Regions) {
for (const auto &Region: Regions) {
::munmap(Region.Ptr, Region.Size);
}
}
}
+51 -97
View File
@@ -70,7 +70,10 @@ namespace Alloc::OSAllocator {
ReservedVMARegion *SlabInfo;
uint64_t FreeSpace{};
uint32_t LastPageAllocation{};
FEXCore::FlexBitSet<uint64_t> UsedPages;
// Align UsedPages so it pads to the next page.
// Necessary to take advantage of madvise zero page pooling.
alignas(4096) FEXCore::FlexBitSet<uint64_t> UsedPages;
// This returns the size of the LiveVMARegion in addition to the flex set that tracks the used data
// The LiveVMARegion lives at the start of the VMA region which means on initialization we need to set that
@@ -93,16 +96,23 @@ namespace Alloc::OSAllocator {
Region->FreeSpace = Region->SlabInfo->RegionSize - SizePlusManagedData;
size_t NumPages = SizePlusManagedData >> FHU::FEX_PAGE_SHIFT;
// Memset the full tracking to zero to state nothing used
Region->UsedPages.MemSet(Region->SlabInfo->RegionSize >> FHU::FEX_PAGE_SHIFT);
// Use madvise to set the full tracking region to zero.
// This ensures unused pages are zero, while not having the backing pages consuming memory.
::madvise(Region->UsedPages.Memory + (NumPages * 4096), (Region->SlabInfo->RegionSize >> FHU::FEX_PAGE_SHIFT) - (NumPages * 4096), MADV_DONTNEED);
// Use madvise to claim WILLNEED on the beginning pages for initial state tracking.
// Improves performance of the following MemClear by not doing a page level fault dance for data necessary to track >170TB of used pages.
::madvise(Region->UsedPages.Memory, NumPages * 4096, MADV_WILLNEED);
// Set our reserved pages
for (size_t i = 0; i < NumPages; ++i) {
// Set our used pages
Region->UsedPages.Set(i);
}
Region->UsedPages.MemSet(NumPages);
Region->LastPageAllocation = NumPages;
}
};
static_assert(sizeof(LiveVMARegion) == 4096, "Needs to be the size of a page");
static_assert(std::is_trivially_copyable<LiveVMARegion>::value, "Needs to be trivially copyable");
static_assert(offsetof(LiveVMARegion, UsedPages) == sizeof(LiveVMARegion), "FlexBitSet needs to be at the end");
@@ -131,23 +141,29 @@ namespace Alloc::OSAllocator {
// Copy over the reserved data
LiveRange->SlabInfo = ReservedRegion;
// Initialize VMA
LiveVMARegion::InitializeVMARegionUsed(LiveRange, UsedSize);
// Add to our active tracked ranges
auto LiveIter = LiveRegions->emplace_back(LiveRange);
return LiveIter;
}
// 32-bit old kernel workarounds
FEXCore::Allocator::PtrCache *Steal32BitIfOldKernel();
std::vector<FEXCore::Allocator::MemoryRegion> Steal32BitIfOldKernel();
};
void OSAllocator_64Bit::DetermineVASize() {
size_t Bits = FEXCore::Allocator::DetermineVASize();
uintptr_t Size = 1ULL << Bits;
UPPER_BOUND = Size;
#if _M_X86_64 // Last page cannot be allocated on x86
UPPER_BOUND -= FHU::FEX_PAGE_SIZE;
#endif
UPPER_BOUND_PAGE = UPPER_BOUND / FHU::FEX_PAGE_SIZE;
}
@@ -490,11 +506,11 @@ int OSAllocator_64Bit::Munmap(void *addr, size_t length) {
return 0;
}
FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
std::vector<FEXCore::Allocator::MemoryRegion> OSAllocator_64Bit::Steal32BitIfOldKernel() {
// First calculate kernel version
struct utsname buf{};
if (uname(&buf) == -1) {
return nullptr;
return {};
}
int32_t Major{};
@@ -512,7 +528,7 @@ FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
if (Version >= ((4 << 24) | (17 << 16) | 0)) {
// If the kernel is >= 4.17 then it supports MAP_FIXED_NOREPLACE
return nullptr;
return {};
}
constexpr size_t LOWER_BOUND_32 = 0x1'0000;
@@ -523,101 +539,39 @@ FEXCore::Allocator::PtrCache *OSAllocator_64Bit::Steal32BitIfOldKernel() {
OSAllocator_64Bit::OSAllocator_64Bit() {
DetermineVASize();
auto ArrayPtr = Steal32BitIfOldKernel();
auto LowMem = Steal32BitIfOldKernel();
// On allocation try and steal the entire upper 64bits of address space for mapping
constexpr std::array<size_t, 8> ReservedVMARegionSizes = {{
// Anything larger than 64GB fails out
64ULL * 1024 * 1024 * 1024, // 64GB
32ULL * 1024 * 1024 * 1024, // 32GB
16ULL * 1024 * 1024 * 1024, // 16GB
4ULL * 1024 * 1024 * 1024, // 4GB
1ULL * 1024 * 1024 * 1024, // 1GB
512ULL * 1024 * 1024, // 512MB
128ULL * 1024 * 1024, // 128MB
4096ULL // One page
}};
auto Ranges = FEXCore::Allocator::StealMemoryRegion(LOWER_BOUND, UPPER_BOUND);
constexpr size_t AllocationSizeMaxIndex = ReservedVMARegionSizes.size() - 1;
for (auto [Ptr, AllocationSize]: Ranges) {
if (!ObjectAlloc) {
auto MaxSize = std::min(size_t(64) * 1024 * 1024, AllocationSize);
// Have the first region only be 4GB VMA
// Avoids conflicts with some tests
uint64_t CurrentSizeIndex = 3;
ReservedVMARegion *PrevReserved{};
for (size_t MemoryOffset = LOWER_BOUND; MemoryOffset < UPPER_BOUND;) {
size_t AllocationSize = ReservedVMARegionSizes[CurrentSizeIndex];
size_t MemoryOffsetUpper = MemoryOffset + AllocationSize;
// Allocate up to 64 MiB the first allocation for an intrusive allocator
mprotect(Ptr, MaxSize, PROT_READ | PROT_WRITE);
// If we would go above the upper bound on size then try the next size
if (MemoryOffsetUpper > UPPER_BOUND) {
++CurrentSizeIndex;
continue;
}
// This enables the kernel to use transparent large pages in the allocator which can reduce memory pressure
::madvise(Ptr, MaxSize, MADV_HUGEPAGE);
void *Ptr = ::mmap(reinterpret_cast<void*>(MemoryOffset), AllocationSize, PROT_NONE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
ObjectAlloc = new (Ptr) Alloc::ForwardOnlyIntrusiveArenaAllocator(Ptr, MaxSize);
ReservedRegions = ObjectAlloc->new_construct(ReservedRegions, ObjectAlloc);
LiveRegions = ObjectAlloc->new_construct(LiveRegions, ObjectAlloc);
// If we managed to allocate and not get the address we want then unmap it
// This happens with kernels older than 4.17
if (reinterpret_cast<uintptr_t>(Ptr) != MemoryOffset &&
reinterpret_cast<uintptr_t>(Ptr) < LOWER_BOUND) {
::munmap(Ptr, AllocationSize);
Ptr = reinterpret_cast<void*>(~0ULL);
}
// If we failed to allocate and we are on the smallest allocation size then just continue onward
// This page was unmappable
if (reinterpret_cast<uintptr_t>(Ptr) == ~0ULL && CurrentSizeIndex == AllocationSizeMaxIndex) {
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
continue;
}
// Congratulations we were able to map this bit
// Reset and claim it was available
if (reinterpret_cast<uintptr_t>(Ptr) != ~0ULL) {
if (!ObjectAlloc) {
// Steal the first allocation for an intrusive allocator
// Will be mprotected correctly already
mprotect(Ptr, AllocationSize, PROT_READ | PROT_WRITE);
ObjectAlloc = new (Ptr) Alloc::ForwardOnlyIntrusiveArenaAllocator(Ptr, AllocationSize);
ReservedRegions = ObjectAlloc->new_construct(ReservedRegions, ObjectAlloc);
LiveRegions = ObjectAlloc->new_construct(LiveRegions, ObjectAlloc);
if (AllocationSize > MaxSize) {
AllocationSize -= MaxSize;
(uint8_t*&)Ptr += MaxSize;
} else {
continue;
}
else {
// If the allocation size is large than a page, then try allowing it to be a huge page
// This enables the kernel to use transparent large pages in the allocator which can reduce memory pressure
// Considering we are allocating the entire VA space, this is a good thing
// If MADV_HUGEPAGE isn't support then this will fail harmlessly
if (AllocationSize > 4096) {
::madvise(Ptr, AllocationSize, MADV_HUGEPAGE);
}
bool Merged = false;
if (PrevReserved) {
Merged = MergeReservedRegionIfPossible(PrevReserved, reinterpret_cast<uint64_t>(Ptr), AllocationSize);
}
if (!Merged) {
ReservedVMARegion *Region = ObjectAlloc->new_construct<ReservedVMARegion>();
Region->Base = reinterpret_cast<uint64_t>(Ptr);
Region->RegionSize = AllocationSize;
ReservedRegions->emplace_back(Region);
PrevReserved = Region;
}
}
CurrentSizeIndex = 0;
MemoryOffset += AllocationSize;
continue;
}
// Couldn't allocate at this size
// Increase and continue
++CurrentSizeIndex;
ReservedVMARegion *Region = ObjectAlloc->new_construct<ReservedVMARegion>();
Region->Base = reinterpret_cast<uint64_t>(Ptr);
Region->RegionSize = AllocationSize;
ReservedRegions->emplace_back(Region);
}
FEXCore::Allocator::ReclaimMemoryRegion(ArrayPtr);
FEXCore::Allocator::ReclaimMemoryRegion(LowMem);
}
OSAllocator_64Bit::~OSAllocator_64Bit() {
+4
View File
@@ -122,6 +122,10 @@ namespace CPU {
bool IsAddressInCodeBuffer(uintptr_t Address) const;
protected:
// Max spill slot size in bytes. We need at most 32 bytes
// to be able to handle a 256-bit vector store to a slot.
constexpr static uint32_t MaxSpillSlotSize = 32;
FEXCore::Core::InternalThreadState *ThreadState;
size_t InitialCodeSize, MaxCodeSize;
+9
View File
@@ -5,6 +5,7 @@
#include <FEXCore/Core/SignalDelegator.h>
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <istream>
@@ -280,4 +281,12 @@ namespace FEXCore::Context {
FEX_DEFAULT_VISIBILITY void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
FEX_DEFAULT_VISIBILITY CustomIRResult AddCustomIREntrypoint(FEXCore::Context::Context *CTX, uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr);
/**
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
*
* @param CTX A valid non-null context instance.
* @param Definitions A vector of thunk definitions that the frontend controls
*/
FEX_DEFAULT_VISIBILITY void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
}
+5
View File
@@ -21,7 +21,12 @@ class HostFeatures final {
bool SupportsRCPC{};
bool SupportsTSOImm9{};
bool SupportsRAND{};
bool Supports3DNow{};
bool SupportsSSE4A{};
bool SupportsAVX{};
bool SupportsSHA{};
bool SupportsBMI1{};
bool SupportsBMI2{};
// Float exception behaviour
bool SupportsFlushInputsToZero{};
+8
View File
@@ -58,6 +58,14 @@ namespace FEXCore {
FEATURE_XTILE_DATA = 1U << 18,
};
bool HasExtendedContext() const {
return magic1 == FP_XSTATE_MAGIC;
}
bool HasYMMH() const {
return (xfeatures & FEATURE_YMM) != 0;
}
// If magic1 is set to FP_XSTATE_MAGIC, then the encompassing
// frame is an xstate frame. If 0, then it's a legacy frame.
uint32_t magic1;
+7
View File
@@ -413,6 +413,13 @@ struct SHA256Sum final {
}
};
typedef void ThunkedFunction(void* ArgsRv);
struct ThunkDefinition final {
SHA256Sum Sum;
ThunkedFunction *ThunkFunction;
};
class NodeIterator;
/* This iterator can be used to step though nodes.
+8 -7
View File
@@ -5,6 +5,7 @@
#include <cstdint>
#include <functional>
#include <sys/types.h>
#include <vector>
namespace FEXCore::Allocator {
using MMAP_Hook = void*(*)(void*, size_t, int, int, int, off_t);
@@ -24,19 +25,19 @@ namespace FEXCore::Allocator {
FEX_DEFAULT_VISIBILITY void ClearHooks();
FEX_DEFAULT_VISIBILITY size_t DetermineVASize();
// 48-bit VA handling
struct PtrCache {
uint64_t Ptr;
uint64_t Size;
struct MemoryRegion {
void *Ptr;
size_t Size;
};
FEX_DEFAULT_VISIBILITY PtrCache* StealMemoryRegion(uintptr_t Begin, uintptr_t End);
FEX_DEFAULT_VISIBILITY void ReclaimMemoryRegion(PtrCache* Regions);
FEX_DEFAULT_VISIBILITY std::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End);
FEX_DEFAULT_VISIBILITY void ReclaimMemoryRegion(const std::vector<MemoryRegion> & Regions);
// When running a 64-bit executable on ARM then userspace guest only gets 47 bits of VA
// This is a feature of x86-64 where the kernel gets a full 128TB of VA space
// x86-64 canonical addresses with bit 48 set will sign extend the address (Ignoring LA57)
// AArch64 canonical addresses are only up to bits 48/52 with the remainder being other things
// Use this to reserve the top 128TB of VA so the guest never see it
// Returns nullptr on host VA < 48bits
FEX_DEFAULT_VISIBILITY PtrCache* Steal48BitVA();
FEX_DEFAULT_VISIBILITY std::vector<MemoryRegion> Steal48BitVA();
}
+1 -1
+33 -43
View File
@@ -1,74 +1,64 @@
add_library(FEXHeaderUtils INTERFACE)
# Check for syscall support here
check_cxx_source_compiles(
"
#include <sched.h>
int main() {
return ::getcpu(nullptr, nullptr);
}"
"
#include <sched.h>
int main() {
return ::getcpu(nullptr, nullptr);
}"
compiles)
if (compiles)
message(STATUS "Has getcpu helper")
add_definitions(-DHAS_SYSCALL_GETCPU=1)
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_GETCPU=1)
endif ()
check_cxx_source_compiles(
"
#include <unistd.h>
int main() {
return ::gettid();
}"
"
#include <unistd.h>
int main() {
return ::gettid();
}"
compiles)
if (compiles)
message(STATUS "Has gettid helper")
add_definitions(-DHAS_SYSCALL_GETTID=1)
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_GETTID=1)
endif ()
check_cxx_source_compiles(
"
#include <signal.h>
int main() {
return ::tgkill(0, 0, 0);
}"
"
#include <signal.h>
int main() {
return ::tgkill(0, 0, 0);
}"
compiles)
if (compiles)
message(STATUS "Has tgkill helper")
add_definitions(-DHAS_SYSCALL_TGKILL=1)
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_TGKILL=1)
endif ()
check_cxx_source_compiles(
"
#include <sys/stat.h>
int main() {
return ::statx(0, nullptr, 0, 0, nullptr);
}"
"
#include <sys/stat.h>
int main() {
return ::statx(0, nullptr, 0, 0, nullptr);
}"
compiles)
if (compiles)
message(STATUS "Has statx helper")
add_definitions(-DHAS_SYSCALL_STATX=1)
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_STATX=1)
endif ()
check_cxx_source_compiles(
"
#include <stdio.h>
int main() {
return ::renameat2(0, nullptr, 0, nullptr, 0);
}"
"
#include <stdio.h>
int main() {
return ::renameat2(0, nullptr, 0, nullptr, 0);
}"
compiles)
if (compiles)
message(STATUS "Has renameat2 helper")
add_definitions(-DHAS_SYSCALL_RENAMEAT2=1)
endif ()
check_cxx_source_compiles(
"
#include <stdio.h>
#include <syscall.h>
int main() {
return ::syscall(SYS_pidfd_open, ::getpid(), 0);
}"
compiles)
if (compiles)
message(STATUS "Has pidfd_open helper")
add_definitions(-DHAS_SYSCALL_PIDFD_OPEN=1)
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_RENAMEAT2=1)
endif ()
target_include_directories(FEXHeaderUtils INTERFACE .)
+9 -8
View File
@@ -38,10 +38,15 @@ namespace FHU::Syscalls {
#endif
#endif
// Common syscall numbers
#ifndef SYS_pidfd_open
#define SYS_pidfd_open 434
#endif
inline int32_t getcpu(uint32_t *cpu, uint32_t *node) {
// Third argument is unused
#if defined(HAS_SYSCALL_GETCPU) && HAS_SYSCALL_GETCPU
return ::getcpu(cpu, node, nullptr);
return ::getcpu(cpu, node);
#else
return ::syscall(SYS_getcpu, cpu, node, nullptr);
#endif
@@ -57,7 +62,7 @@ inline int32_t gettid() {
inline int32_t tgkill(pid_t tgid, pid_t tid, int sig) {
#if defined(HAS_SYSCALL_GETTID) && HAS_SYSCALL_GETTID
return ::tgkill(tggid, tid, sig);
return ::tgkill(tgid, tid, sig);
#else
return ::syscall(SYS_tgkill, tgid, tid, sig);
#endif
@@ -65,7 +70,7 @@ inline int32_t tgkill(pid_t tgid, pid_t tid, int sig) {
inline int32_t statx(int dirfd, const char *pathname, int32_t flags, uint32_t mask, void *statxbuf) {
#if defined(HAS_SYSCALL_STATX) && HAS_SYSCALL_STATX
return ::statx(dirfd, pathname, flags, mask, statxbuf);
return ::statx(dirfd, pathname, flags, mask, reinterpret_cast<struct statx *__restrict>(statxbuf));
#else
return ::syscall(SYS_statx, dirfd, pathname, flags, mask, statxbuf);
#endif
@@ -80,11 +85,7 @@ inline int32_t renameat2(int olddirfd, const char *oldpath, int newdirfd, const
}
inline int32_t pidfd_open(pid_t pid, unsigned int flags) {
#if defined(DHAS_SYSCALL_PIDFD_OPEN) && DHAS_SYSCALL_PIDFD_OPEN
return ::syscall(SYS_pidfd_open, pid_t pid, unsigned int flags);
#else
return -1;
#endif
return ::syscall(SYS_pidfd_open, pid, flags);
}
}
+2 -2
View File
@@ -30,11 +30,11 @@ On AArch64 hosts the user **MUST** have an x86-64 RootFS [Creating a RootFS](#Ro
See the [Source Outline](docs/SourceOutline.md) for more information.
### Building FEX
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.org/index.php/Development:Setting_up_FEX).
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX).
### RootFS generation
AArch64 hosts require a rootfs for running applications.
Follow the guide on the wiki page for seeing how to set up the rootfs from scratch
https://wiki.fex-emu.org/index.php/Development:Setting_up_RootFS
https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS
![FEX diagram](docs/Diagram.svg)
+26 -9
View File
@@ -83,6 +83,12 @@ def HashFile(file):
return int.from_bytes(x.digest(), "big")
def RemoveRootFSFolder(RootFSPath):
print("Removing previous rootfs extraction before copying")
shutil.rmtree(RootFSPath, ignore_errors = True)
# Recreate the folder
os.makedirs(RootFSPath)
def CheckFilesystemForFS(RootFSMountPath, RootFSPath, DistroFit):
# Check if rootfs mount path exists
if (not os.path.exists(RootFSMountPath) or
@@ -105,6 +111,7 @@ def CheckFilesystemForFS(RootFSMountPath, RootFSPath, DistroFit):
MountRootFSImagePath = RootFSMountPath + DistroFit[3]
RootFSImagePath = RootFSPath + "/" + os.path.basename(DistroFit[3])
NeedsExtraction = False
PreviouslyExistingRootFS = False
if not os.path.exists(MountRootFSImagePath):
print("Image {} doesn't exist".format(MountRootFSImagePath))
@@ -113,29 +120,39 @@ def CheckFilesystemForFS(RootFSMountPath, RootFSPath, DistroFit):
if not os.path.exists(RootFSImagePath):
# Copy over
print("RootFS image doesn't exist. Copying")
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
NeedsExtraction = True
# Now hash the image
RootFSHash = HashFile(RootFSImagePath)
if RootFSHash != DistroFit[4]:
print("Hash {} did not match {}, copying new image".format(hex(RootFSHash), hex(DistroFit[4])))
RemoveRootFSFolder(RootFSPath)
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
NeedsExtraction = True
# Check if the image needs to be extracted
if not os.path.exists(RootFSPath + "/usr"):
NeedsExtraction = True
else:
PreviouslyExistingRootFS = True
# Now hash the image
RootFSHash = HashFile(RootFSImagePath)
if RootFSHash != DistroFit[4]:
print("Hash {} did not match {}, copying new image".format(hex(RootFSHash), hex(DistroFit[4])))
if PreviouslyExistingRootFS:
RemoveRootFSFolder(RootFSPath)
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
NeedsExtraction = True
if NeedsExtraction:
print("Extracting rootfs")
CmdResult = subprocess.call(["unsquashfs", "-f", "-d", RootFSPath, RootFSImagePath])
if CmdResult != 0:
print("Couldn't extract squashfs")
print("Couldn't extract squashfs. Removing image file to be safe")
os.remove(RootFSImagePath)
return False
if not os.path.exists(RootFSPath + "/usr"):
print("Couldn't extract squashfs")
print("Couldn't extract squashfs. Removing image file to be safe")
os.remove(RootFSImagePath)
return False
print("RootFS successfully checked and extracted")
+4 -1
View File
@@ -154,6 +154,9 @@ def HandleFunctionDeclCursor(Arch, Cursor):
elif (Child.kind == CursorKind.ASM_LABEL_ATTR):
# Whatever you are we don't care about you
return Arch
elif (Child.kind == CursorKind.WARN_UNUSED_RESULT_ATTR):
# Whatever you are we don't care about you
return Arch
elif (Child.kind == CursorKind.VISIBILITY_ATTR):
pass
else:
@@ -165,7 +168,7 @@ def HandleFunctionDeclCursor(Arch, Cursor):
def PrintFunctionDecls():
for Decl in FunctionDecls:
print("fn(\"{0} {1}({2})\")".format(Decl.Ret, Decl.Name, ", ".join(Decl.Params)))
print("template<> struct fex_gen_config<{}> {{}};".format(Decl.Name))
def FindClangArguments(OriginalArguments):
AddedArguments = ["clang"]
+7 -1
View File
@@ -59,7 +59,13 @@ SyscallArm64File = "/include/uapi/asm-generic/unistd.h"
DefinitionRenameDict = {
"pread64": "pread_64",
"pwrite64": "pwrite_64",
"prlimit64": "prlimit_64"
"prlimit64": "prlimit_64",
# Shm symbols conflict with termux defines and FEX's syscall token pasting.
# Underscore at the start to avoid name collision
"shmget": "_shmget",
"shmctl": "_shmctl",
"shmat": "_shmat",
"shmdt": "_shmdt",
}
Definitions_x64 = []
+3 -1
View File
@@ -306,12 +306,14 @@ def GetRootFSPath():
return _RootFSPath
def CheckRootFSInstallStatus():
# Matches what is available on https://rootfs.fex-emu.org/file/fex-rootfs/RootFS_links_XXH3.txt
# Matches what is available on https://rootfs.fex-emu.com/file/fex-rootfs/RootFS_links.json
UbuntuVersionToRootFS = {
"20.04": "Ubuntu_21_04.sqsh",
"21.04": "Ubuntu_21_04.sqsh",
"21.10": "Ubuntu_21_10.sqsh",
"21.10": "Ubuntu_21_10.ero",
"22.04": "Ubuntu_22_04.sqsh",
"22.04": "Ubuntu_22_04.ero",
}
return os.path.exists(GetRootFSPath() + UbuntuVersionToRootFS[GetDistro()[1]])
+25 -16
View File
@@ -10,6 +10,18 @@ import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)
# These defines are temporarily defined since python3-clang doesn't yet support these.
# Once this tool gets switched over to C++ then this won't be an issue.
# Expression that references a C++20 concept.
CursorKind.CONCEPTSPECIALIZATIONEXPR = CursorKind(153),
# C++2a std::bit_cast expression.
CursorKind.BUILTINBITCASTEXPR = CursorKind(280)
# a concept declaration.
CursorKind.CONCEPTDECL = CursorKind(604),
@dataclass
class TypeDefinition:
TYPE_UNKNOWN = 0
@@ -268,7 +280,7 @@ def HandleTypeDefDeclCursor(Arch, Cursor):
if (len(TypeDefName) != 0):
HandleTypeDefDecl(Arch, Cursor, TypeDefName)
# Append namespace
# Append namespace
Arch.NamespaceScope.append(TypeDefName)
SetNamespace(Arch)
@@ -404,19 +416,20 @@ def HandleCursor(Arch, Cursor):
return
for Child in Cursor.get_children():
if (Child.kind == CursorKind.TRANSLATION_UNIT):
kind = Child.kind
if (kind == CursorKind.TRANSLATION_UNIT):
Arch = HandleCursor(Arch, Child)
elif (Child.kind == CursorKind.FIELD_DECL):
elif (kind == CursorKind.FIELD_DECL):
pass
elif (Child.kind == CursorKind.UNION_DECL):
elif (kind == CursorKind.UNION_DECL):
Arch = HandleUnionDeclCursor(Arch, Child)
elif (Child.kind == CursorKind.STRUCT_DECL):
elif (kind == CursorKind.STRUCT_DECL):
Arch = HandleStructDeclCursor(Arch, Child)
elif (Child.kind == CursorKind.TYPEDEF_DECL):
elif (kind == CursorKind.TYPEDEF_DECL):
Arch = HandleTypeDefDeclCursor(Arch, Child)
elif (Child.kind == CursorKind.VAR_DECL):
elif (kind == CursorKind.VAR_DECL):
Arch = HandleVarDeclCursor(Arch, Child)
elif (Child.kind == CursorKind.NAMESPACE):
elif (kind == CursorKind.NAMESPACE):
# Append namespace
Arch.NamespaceScope.append(Child.spelling)
SetNamespace(Arch)
@@ -427,7 +440,7 @@ def HandleCursor(Arch, Cursor):
# Pop namespace off
Arch.NamespaceScope.pop()
SetNamespace(Arch)
elif (Child.kind == CursorKind.TYPE_REF):
elif (kind == CursorKind.TYPE_REF):
# Safe to pass on
pass
else:
@@ -638,25 +651,21 @@ def main():
BaseArgs.append(sys.argv[ArgIndex])
args_x86_32 = [
"-I/usr/i686-linux-gnu/include/c++/10/i686-linux-gnu/",
"-I/usr/i686-linux-gnu/include/",
"-I/usr/i686-linux-gnu/include",
"-O2",
"-m32",
"--target=i686-linux-unknown",
]
args_x86_64 = [
"-I/usr/include/x86_64-linux-gnu",
"-I/usr/x86_64-linux-gnu/include/c++/10/x86_64-linux-gnu/",
"-I/usr/x86_64-linux-gnu/include/",
"-I/usr/x86_64-linux-gnu/include",
"-O2",
"--target=x86_64-linux-unknown",
"-D_M_X86_64",
]
args_aarch64 = [
"-I/usr/aarch64-linux-gnu/include/c++/10/aarch64-linux-gnu/",
"-I/usr/aarch64-linux-gnu/include/",
"-I/usr/aarch64-linux-gnu/include",
"-O2",
"--target=aarch64-linux-unknown",
"-D_M_ARM_64",
+59 -36
View File
@@ -3,44 +3,55 @@ import os
import sys
import subprocess
# Args: <Known Failures file> <ExpectedOutputsFile> <DisabledTestsFile> <TestName> <FexExecutable> <FexArgs>...
def LoadTestsFile(File):
Dict = {}
if not os.path.exists(File):
return Dict
with open(File) as dtf:
for line in dtf:
test = line.split("#")[0].strip() # remove comments and empty spaces
if len(test) > 0:
Dict[test] = 1
return Dict
def LoadTestsFileResults(File):
Dict = {}
if not os.path.exists(File):
return Dict
with open(File) as dtf:
for line in dtf:
test = line.split("#")[0].strip() # remove comments and empty spaces
if len(test) > 0:
parts = line.split(" ")
Dict[parts[0]] = int(parts[1])
return Dict
# Args: <Known Failures file> <ExpectedOutputsFile> <DisabledTestsFile> <FlakeTestsFile> <TestName> <Mode> <FexExecutable> <FexArgs>...
# fexargs should also include the test executable
if (len(sys.argv) < 6):
if (len(sys.argv) < 7):
sys.exit()
known_failures_file = sys.argv[1]
expected_output_file = sys.argv[2]
disabled_tests_file = sys.argv[3]
test_name = sys.argv[4]
mode = sys.argv[5]
fexecutable = sys.argv[6]
flake_tests_file = sys.argv[4]
test_name = sys.argv[5]
mode = sys.argv[6]
fexecutable = sys.argv[7]
StartingFEXArgsOffset = 8
known_failures = { }
expected_output = { }
disabled_tests = { }
# Open the known failures file and add it to a dictionary
with open(known_failures_file) as kff:
for line in kff:
test = line.split("#")[0].strip() # remove comments and empty spaces
if len(test) > 0:
known_failures[test] = 1
# Open expected outputs and add it to dictionary
with open(expected_output_file) as eof:
for line in eof:
line = test = line.split("#")[0].strip() # remove comments and empty spaces
if len(line) > 0:
parts = line.split(" ")
expected_output[parts[0]] = int(parts[1])
with open(disabled_tests_file) as dtf:
for line in dtf:
test = line.split("#")[0].strip() # remove comments and empty spaces
if len(test) > 0:
disabled_tests[test] = 1
# Open test expected information files and load in to dictionaries.
known_failures = LoadTestsFile(known_failures_file)
expected_output = LoadTestsFileResults(expected_output_file)
disabled_tests = LoadTestsFile(disabled_tests_file)
flake_tests = LoadTestsFile(flake_tests_file)
# run with timeout to avoid locking up
RunnerArgs = []
@@ -54,25 +65,37 @@ if (mode == "guest"):
RunnerArgs.append(ROOTFS_ENV)
# Add the rest of the arguments
for i in range(len(sys.argv) - 7):
RunnerArgs.append(sys.argv[7 + i])
for i in range(len(sys.argv) - StartingFEXArgsOffset):
RunnerArgs.append(sys.argv[StartingFEXArgsOffset + i])
#print(RunnerArgs)
ResultCode = 0
# Handle flakes
TryCount = 1
if (flake_tests.get(test_name)):
TryCount = 5
if (disabled_tests.get(test_name)):
ResultCode = -73
else:
# Run the test and wait for it to end to get the result
Process = subprocess.Popen(RunnerArgs)
Process.wait()
ResultCode = Process.returncode
# expect zero by default
if (not test_name in expected_output):
expected_output[test_name] = 0
if ResultCode == 0:
for Try in range(TryCount):
# Run the test and wait for it to end to get the result
print(RunnerArgs)
Process = subprocess.Popen(RunnerArgs)
Process.wait()
ResultCode = Process.returncode
# Break if the expected output is the result code
if (expected_output[test_name] == ResultCode):
break
if (expected_output[test_name] != ResultCode):
if (test_name in expected_output):
print("test failed, expected is", expected_output[test_name], "but got", ResultCode)
+27 -9
View File
@@ -63,8 +63,15 @@ class Mode(Flag) :
MODE_64 = 1
class HostFeatures(Flag) :
ANY = 0
AVX_ONLY = 1
FEATURE_ANY = 0
FEATURE_3DNOW = (1 << 0)
FEATURE_SSE4A = (1 << 1)
FEATURE_AVX = (1 << 2)
FEATURE_RAND = (1 << 3)
FEATURE_SHA = (1 << 4)
FEATURE_CLZERO = (1 << 5)
FEATURE_BMI1 = (1 << 6)
FEATURE_BMI2 = (1 << 7)
RegStringLookup = {
"NONE": Regs.REG_NONE,
@@ -128,8 +135,14 @@ ModeStringLookup = {
}
HostFeaturesLookup = {
"ANY" : HostFeatures.ANY,
"AVX" : HostFeatures.AVX_ONLY,
"3DNOW" : HostFeatures.FEATURE_3DNOW,
"SSE4A" : HostFeatures.FEATURE_SSE4A,
"AVX" : HostFeatures.FEATURE_AVX,
"RAND" : HostFeatures.FEATURE_RAND,
"SHA" : HostFeatures.FEATURE_SHA,
"CLZERO" : HostFeatures.FEATURE_CLZERO,
"BMI1" : HostFeatures.FEATURE_BMI1,
"BMI2" : HostFeatures.FEATURE_BMI2,
}
def parse_hexstring(s):
@@ -152,7 +165,7 @@ def parse_json(json_text, output_file):
OptionIgnore = Regs.REG_NONE
OptionABI = ABI.ABI_SYSTEMV
OptionMode = Mode.MODE_64
OptionHostFeatures = HostFeatures.ANY
OptionHostFeatures = HostFeatures.FEATURE_ANY
OptionStackSize = 4096
OptionEntryPoint = 1
OptionRegData = {}
@@ -208,10 +221,15 @@ def parse_json(json_text, output_file):
if ("HOSTFEATURES" in json_object):
data = json_object["HOSTFEATURES"]
data = data.upper()
if not (data in HostFeaturesLookup):
sys.exit("Invalid host feature")
OptionHostFeatures = HostFeaturesLookup[data]
if not (type(data) is list):
sys.exit("HostFeatures value must be list of features")
for data_key in data:
data_key = data_key.upper()
if not (data_key in HostFeaturesLookup):
sys.exit("Invalid host feature")
OptionHostFeatures |= HostFeaturesLookup[data_key]
if ("STACKSIZE" in json_object):
data = json_object["STACKSIZE"]
+1 -1
View File
@@ -8,6 +8,6 @@ set(SRCS
StringUtil.cpp)
add_library(${NAME} STATIC ${SRCS})
target_link_libraries(${NAME} FEXCore_Base cpp-optparse json-maker)
target_link_libraries(${NAME} FEXCore_Base cpp-optparse json-maker FEXHeaderUtils)
target_include_directories(${NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/External/cpp-optparse/)
target_include_directories(${NAME} PRIVATE ${CMAKE_BINARY_DIR}/generated)
+4 -3
View File
@@ -5,12 +5,13 @@
#include <fcntl.h>
#include <filesystem>
#include <linux/limits.h>
#include <optional>
#include <unistd.h>
namespace FEX {
[[maybe_unused]]
static
std::string get_fdpath(int fd) {
std::optional<std::string> get_fdpath(int fd) {
char SymlinkPath[PATH_MAX];
std::filesystem::path Path = std::filesystem::path("/proc/self/fd") / std::to_string(fd);
int Result = readlinkat(AT_FDCWD, Path.c_str(), SymlinkPath, sizeof(SymlinkPath));
@@ -18,8 +19,8 @@ std::string get_fdpath(int fd) {
return std::string(SymlinkPath, Result);
}
LOGMAN_MSG_A_FMT("Couldn't get symlink from /proc/self/fd/{}", fd);
return {};
// Not fatal if an FD doesn't point to a file
return std::nullopt;
}
}
+6 -1
View File
@@ -101,7 +101,12 @@ namespace FEXServerClient {
}
std::string GetServerSocketFile() {
return fmt::format("{}/{}.FEXServer.socket", std::filesystem::temp_directory_path().string(), ::geteuid());
FEX_CONFIG_OPT(ServerSocketPath, SERVERSOCKETPATH);
if (ServerSocketPath().empty()) {
return fmt::format("{}/{}.FEXServer.socket", std::filesystem::temp_directory_path().string(), ::geteuid());
}
return ServerSocketPath;
}
int GetServerFD() {
+73 -32
View File
@@ -2,8 +2,14 @@ add_subdirectory(LinuxSyscalls)
list(APPEND LIBS FEXCore Common)
if (TERMUX_BUILD)
# Termux needs android-shmem to get the shm emulation library.
list(APPEND LIBS android-shmem)
endif()
add_executable(FEXLoader
FEXLoader.cpp
VDSO_Emulation.cpp
AOT/AOTGenerator.cpp)
# Enable FEX APIs to be used by targets that use target_link_libraries on FEXLoader
@@ -45,10 +51,10 @@ if(TERMUX_BUILD)
)
install(
CODE "MESSAGE(\"-- Installing: ${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
CODE "
EXECUTE_PROCESS(COMMAND cp FEXLoader FEXInterpreter
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin/
WORKING_DIRECTORY $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/
)"
)
else()
@@ -58,12 +64,20 @@ else()
)
install(
CODE "MESSAGE(\"-- Installing: ${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
CODE "
EXECUTE_PROCESS(COMMAND ln -f FEXLoader FEXInterpreter
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin/
WORKING_DIRECTORY $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/
)"
)
if(TARGET uninstall)
add_custom_target(uninstall_FEXInterpreter
COMMAND "rm" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter"
)
add_dependencies(uninstall uninstall_FEXInterpreter)
endif()
endif()
install(PROGRAMS "${PROJECT_SOURCE_DIR}/Scripts/FEXUpdateAOTIRCache.sh" DESTINATION bin RENAME FEXUpdateAOTIRCache)
@@ -95,6 +109,17 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
COMMAND ${CMAKE_COMMAND} -E
echo "binfmt_misc FEX-x86_64 installed"
)
if(TARGET uninstall)
add_custom_target(uninstall_binfmt_misc_32
COMMAND update-binfmts --unimport FEX-x86 || (exit 0)
)
add_custom_target(uninstall_binfmt_misc_64
COMMAND update-binfmts --unimport FEX-x86_64 || (exit 0)
)
add_dependencies(uninstall uninstall_binfmt_misc_32)
add_dependencies(uninstall uninstall_binfmt_misc_64)
endif()
else()
# In the case of update-binfmts not being available (Arch for example) then we need to install manually
add_custom_target(binfmt_misc_32
@@ -123,6 +148,20 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
COMMAND ${CMAKE_COMMAND} -E
echo "binfmt_misc FEX-x86_64 installed"
)
if(TARGET uninstall)
add_custom_target(uninstall_binfmt_misc_32
COMMAND ${CMAKE_COMMAND} -E
echo -1 > /proc/sys/fs/binfmt_misc/FEX-x86 || (exit 0)
)
add_custom_target(uninstall_binfmt_misc_64
COMMAND ${CMAKE_COMMAND} -E
echo -1 > /proc/sys/fs/binfmt_misc/FEX-x86_64 || (exit 0)
)
add_dependencies(uninstall uninstall_binfmt_misc_32)
add_dependencies(uninstall uninstall_binfmt_misc_64)
endif()
endif()
add_custom_target(binfmt_misc
@@ -159,18 +198,20 @@ install(TARGETS FEXBash
COMPONENT runtime
)
add_executable(TestHarnessRunner TestHarnessRunner/HostRunner.cpp TestHarnessRunner.cpp)
target_include_directories(TestHarnessRunner
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(TestHarnessRunner
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
)
if (BUILD_TESTS)
add_executable(TestHarnessRunner TestHarnessRunner/HostRunner.cpp TestHarnessRunner.cpp)
target_include_directories(TestHarnessRunner
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(TestHarnessRunner
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
)
# add_executable(UnitTestGenerator UnitTestGenerator.cpp)
# target_include_directories(UnitTestGenerator
@@ -184,19 +225,19 @@ target_link_libraries(TestHarnessRunner
# )
#
add_executable(IRLoader
IRLoader.cpp
)
target_include_directories(IRLoader
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(IRLoader
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
fmt::fmt
)
add_executable(IRLoader
IRLoader.cpp
)
target_include_directories(IRLoader
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(IRLoader
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
fmt::fmt
)
endif()
+138 -41
View File
@@ -3,6 +3,7 @@
#include "Common/Config.h"
#include "Common/FDUtils.h"
#include "FEXCore/Utils/Allocator.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
#include "Linux/Utils/ELFParser.h"
#include "Linux/Utils/ELFSymbolDatabase.h"
@@ -13,6 +14,7 @@
#include <cstring>
#include <filesystem>
#include <fstream>
#include <random>
#include <string>
#include <vector>
@@ -21,6 +23,7 @@
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <FEXHeaderUtils/TypeDefines.h>
#include <elf.h>
#include <fcntl.h>
@@ -79,7 +82,9 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
return false;
} else {
auto Filename = FEX::get_fdpath(file.fd);
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0});
if (Filename.has_value()) {
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename.value(), (prot & PROT_EXEC) != 0});
}
return true;
}
@@ -101,7 +106,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
}
template <typename TMap, typename TUnmap>
std::optional<uintptr_t> LoadElfFile(ELFParser& Elf, uintptr_t *BrkBase, TMap Mapper, TUnmap Unmapper) {
std::optional<uintptr_t> LoadElfFile(ELFParser& Elf, uintptr_t *BrkBase, TMap Mapper, TUnmap Unmapper, uint64_t LoadHint = 0) {
uintptr_t LoadBase = 0;
@@ -112,14 +117,11 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
if (Elf.ehdr.e_type == ET_DYN) {
// needs base address
auto TotalSize = CalculateTotalElfSize(Elf.phdrs) + (BrkBase ? BRK_SIZE : 0);
LoadBase = (uintptr_t)Mapper(0, TotalSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
LoadBase = (uintptr_t)Mapper(reinterpret_cast<void*>(LoadHint), TotalSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if ((void*)LoadBase == MAP_FAILED) {
return {};
}
if (Unmapper((void*)LoadBase, TotalSize) == -1) {
return {};
}
//fprintf(stderr, "elf %d: %lx-%lx\n", Elf.fd, LoadBase, LoadBase + TotalSize);
if (BrkBase) {
*BrkBase = LoadBase + (TotalSize - BRK_SIZE);
@@ -130,10 +132,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
if (Header.p_type != PT_LOAD)
continue;
int MapProt = MapFlags(Header);
int MapType = MAP_PRIVATE | MAP_DENYWRITE | MAP_FIXED_NOREPLACE;
int MapProt = MapFlags(Header);
int MapType = MAP_PRIVATE | MAP_DENYWRITE | MAP_FIXED;
if (!MapFile(Elf, LoadBase, Header, MapProt, MapType, Mapper)) {
if (!MapFile(Elf, LoadBase, Header, MapProt, MapType, Mapper)) {
return {};
}
@@ -207,6 +209,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
};
std::vector<LoadedSection> Sections;
ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *AdditionalEnvp = nullptr) :
Args {args} {
@@ -357,33 +360,43 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
return false;
}
// load the main elf
uintptr_t BrkBase = 0;
uintptr_t LoadBase = 0;
if (auto elf = LoadElfFile(MainElf, &BrkBase, Mapper, Unmapper)) {
LoadBase = *elf;
if (MainElf.ehdr.e_type == ET_DYN) {
BaseOffset = LoadBase;
}
} else {
LogMan::Msg::EFmt("Failed to load elf file");
return false;
}
// XXX Randomise brk?
BrkStart = (uint64_t)Mapper((void*)BrkBase, BRK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
if ((void*)BrkStart == MAP_FAILED) {
LogMan::Msg::EFmt("Failed to allocate BRK @ {:x}, {}\n", BrkBase, errno);
return false;
}
MainElfBase = LoadBase + MainElf.phdrs.front().p_vaddr - MainElf.phdrs.front().p_offset;
MainElfEntrypoint = LoadBase + MainElf.ehdr.e_entry;
// Load the interpreter ELF first.
// This allows the top-down allocation of the kernel to put this at the top of the VA space.
// This matches behaviour of native execution more closely.
//
// eg:
// 555555554000-555555558000 r--p 00000000 103:0a 1311400 /usr/bin/ls
// 555555558000-55555556c000 r-xp 00004000 103:0a 1311400 /usr/bin/ls
// 55555556c000-555555574000 r--p 00018000 103:0a 1311400 /usr/bin/ls
// 555555575000-555555577000 rw-p 00020000 103:0a 1311400 /usr/bin/ls
// 555555577000-555555578000 rw-p 00000000 00:00 0 [heap]
// 7ffff7fbb000-7ffff7fbd000 rw-p 00000000 00:00 0
// 7ffff7fbd000-7ffff7fc1000 r--p 00000000 00:00 0 [vvar]
// 7ffff7fc1000-7ffff7fc3000 r-xp 00000000 00:00 0 [vdso]
// 7ffff7fc3000-7ffff7fc5000 r--p 00000000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7ffff7fc5000-7ffff7fef000 r-xp 00002000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7ffff7fef000-7ffff7ffa000 r--p 0002c000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7ffff7ffb000-7ffff7fff000 rw-p 00037000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
// ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
//
// ARM:
// 55ccaf8b1000-55ccaf8b5000 r--p 00000000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
// 55ccaf8b5000-55ccaf8c9000 r-xp 00004000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
// 55ccaf8c9000-55ccaf8d1000 r--p 00018000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
// 55ccaf8d1000-55ccaf8d2000 ---p 00000000 00:00 0
// 55ccaf8d2000-55ccaf8d4000 rw-p 00020000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
// 55ccaf8d4000-55ccb00d5000 rw-p 00000000 00:00 0
// <... Snip of misc allocations ...>
// 7fffff6c2000-7fffff6c4000 r--p 00000000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7fffff6c4000-7fffff6ee000 r-xp 00002000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7fffff6ee000-7fffff6f9000 r--p 0002c000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7fffff6f9000-7fffff6fa000 ---p 00000000 00:00 0
// 7fffff6fa000-7fffff6fe000 rw-p 00037000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
// 7fffff7fe000-7fffffffe000 rw-p 00000000 00:00 0
// 7fffffffe000-7ffffffff000 r--p 00000000 08:82 7082611 /usr/share/fex-emu/GuestThunks/libVDSO-guest.so
// 7ffffffff000-800000000000 rw-p 00000000 00:00 0
uint64_t ELFLoadHint = 0;
if (!MainElf.InterpreterElf.empty()) {
uint64_t InterpLoadBase = 0;
@@ -396,7 +409,83 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
InterpeterElfBase = InterpLoadBase + InterpElf.phdrs.front().p_vaddr - InterpElf.phdrs.front().p_offset;
Entrypoint = InterpLoadBase + InterpElf.ehdr.e_entry;
// If the ELF has an interpreter and is dynamic then we should provide a address hint for loading.
// The kernel calculates this `load_bias` by dividing the task size by three then multiplying by two.
// It then also offsets by a random number for ASLR purposes.
//
// Random number that gets added to the base needs to be in the number of bits (multiplied by pages):
// 64-bit: [28, 32] bits
// 32-bit: [8, 16] bits
// By default the /minimum/ number of bits is used here.
constexpr uint64_t TASK_SIZE_64 = (1ULL << 47);
constexpr uint64_t TASK_SIZE_32 = (1ULL << 32);
if (Is64BitMode()) {
// Ensure that if we are running on a 36-bit VA system, we don't try hinting that an ELF should
// live way outside the VA space.
uint64_t HostVASize = 1ULL << FEXCore::Allocator::DetermineVASize();
ELFLoadHint = std::min(HostVASize, TASK_SIZE_64) / 3 * 2;
}
else {
ELFLoadHint = TASK_SIZE_32 / 3 * 2;
}
#define ASLR_LOAD
#ifdef ASLR_LOAD
// Only enable ASLR randomization if the personality has it enabled.
uint32_t Personality = personality(~0ULL);
bool NoRandomize = (Personality & ADDR_NO_RANDOMIZE) == ADDR_NO_RANDOMIZE;
if (!NoRandomize) {
constexpr uint64_t ASLR_BITS_64 = 28;
constexpr uint64_t ASLR_BITS_32 = 8;
std::random_device rd;
std::uniform_int_distribution<uint64_t> d(0);
uint64_t ASLR_Offset = d(rd);
if (Is64BitMode()) {
ASLR_Offset &= (1ULL << ASLR_BITS_64) - 1;
}
else {
ASLR_Offset &= (1ULL << ASLR_BITS_32) - 1;
}
ASLR_Offset <<= FHU::FEX_PAGE_SHIFT;
ELFLoadHint += ASLR_Offset;
}
#endif
// Align the mapping
ELFLoadHint &= FHU::FEX_PAGE_MASK;
}
// load the main elf
uintptr_t BrkBase = 0;
uintptr_t LoadBase = 0;
if (auto elf = LoadElfFile(MainElf, &BrkBase, Mapper, Unmapper, ELFLoadHint)) {
LoadBase = *elf;
if (MainElf.ehdr.e_type == ET_DYN) {
BaseOffset = LoadBase;
}
} else {
LogMan::Msg::EFmt("Failed to load elf file");
return false;
}
// XXX Randomise brk?
BrkStart = (uint64_t)Mapper((void*)BrkBase, BRK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
if ((void*)BrkStart == MAP_FAILED) {
LogMan::Msg::EFmt("Failed to allocate BRK @ {:x}, {}\n", BrkBase, errno);
return false;
}
MainElfBase = LoadBase + MainElf.phdrs.front().p_vaddr - MainElf.phdrs.front().p_offset;
MainElfEntrypoint = LoadBase + MainElf.ehdr.e_entry;
if (MainElf.InterpreterElf.empty()) {
InterpeterElfBase = 0;
Entrypoint = MainElfEntrypoint;
}
@@ -424,17 +513,20 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
// On x86 only allows userspace to check for monitor and fs/gs base writing in CPL3
//AuxVariables.emplace_back(auxv_t{26, 0}); // AT_HWCAP2
// we don't support vsyscall or vDSO so we don't set those
// we don't support vsyscall so we don't set those
//AuxVariables.emplace_back(auxv_t{32, 0}); // AT_SYSINFO - Entry point to syscall
//AuxVariables.emplace_back(auxv_t{33, 0}); // AT_SYSINFO_EHDR - Address of the start of VDSO
}
else {
AuxVariables.emplace_back(auxv_t{4, 0x20}); // AT_PHENT
// we don't support vsyscall or vDSO so we don't set those
// we don't support vsyscall so we don't set those
//AuxVariables.emplace_back(auxv_t{32, 0}); // AT_SYSINFO - Entry point to syscall
//AuxVariables.emplace_back(auxv_t{33, 0}); // AT_SYSINFO_EHDR - Address of the start of VDSO
}
if (VDSOBase) {
AuxVariables.emplace_back(auxv_t{33, reinterpret_cast<uint64_t>(VDSOBase)}); // AT_SYSINFO_EHDR - Address of the start of VDSO
}
AuxVariables.emplace_back(auxv_t{3, MainElfBase + MainElf.ehdr.e_phoff}); // Program header
AuxVariables.emplace_back(auxv_t{7, InterpeterElfBase}); // AT_BASE - Interpreter address
AuxVariables.emplace_back(auxv_t{9, MainElfEntrypoint}); // AT_ENTRY
@@ -638,6 +730,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
return ElfValid;
}
void SetVDSOBase(void* Base) {
VDSOBase = Base;
}
constexpr static uint64_t BRK_SIZE = 8 * 1024 * 1024;
constexpr static uint64_t STACK_SIZE = 8 * 1024 * 1024;
@@ -650,6 +746,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
uint64_t ArgumentBackingSize{};
uint64_t EnvironmentBackingSize{};
uint64_t BaseOffset{};
FEX_CONFIG_OPT(AdditionalArguments, ADDITIONALARGUMENTS);
void* VDSOBase{};
FEX_CONFIG_OPT(AdditionalArguments, ADDITIONALARGUMENTS);
};
+12 -3
View File
@@ -9,6 +9,7 @@ $end_info$
#include "Common/ArgumentLoader.h"
#include "Common/FEXServerClient.h"
#include "ELFCodeLoader2.h"
#include "VDSO_Emulation.h"
#include "Tests/LinuxSyscalls/LinuxAllocator.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
@@ -327,11 +328,12 @@ int main(int argc, char **argv, char **const envp) {
return -ENOEXEC;
}
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.first).string());
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.first).string());
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.second);
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
FEXCore::Allocator::PtrCache *Base48Bit{};
std::vector<FEXCore::Allocator::MemoryRegion> Base48Bit;
if (Loader.Is64BitMode()) {
// Destroy the 48th bit if it exists
@@ -388,6 +390,10 @@ int main(int argc, char **argv, char **const envp) {
auto Mapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMmap, SyscallHandler.get());
auto Unmapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMunmap, SyscallHandler.get());
// Load VDSO in to memory prior to mapping our ELFs.
void* VDSOBase = FEX::VDSO::LoadVDSOThunks(Loader.Is64BitMode(), Mapper);
Loader.SetVDSOBase(VDSOBase);
if (!Loader.MapMemory(Mapper, Unmapper)) {
// failed to map
LogMan::Msg::EFmt("Failed to map %d-bit elf file.", Loader.Is64BitMode() ? 64 : 32);
@@ -404,6 +410,9 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Context::SetSyscallHandler(CTX, SyscallHandler.get());
FEXCore::Context::InitCore(CTX, Loader.DefaultRIP(), Loader.GetStackPointer());
// Pass in our VDSO thunks
FEXCore::Context::AppendThunkDefinitions(CTX, FEX::VDSO::GetVDSOThunkDefinitions());
FEXCore::Context::ExitReason ShutdownReason = FEXCore::Context::ExitReason::EXIT_SHUTDOWN;
// There might already be an exit handler, leave it installed
+29 -2
View File
@@ -374,7 +374,27 @@ namespace FEX::HarnessHelper {
}
bool Is64BitMode() const { return BaseConfig.OptionMode == 1; }
bool RequiresAVX() const { return BaseConfig.OptionHostFeatures == 1; }
enum HostFeatures {
FEATURE_ANY = 0,
FEATURE_3DNOW = (1 << 0),
FEATURE_SSE4A = (1 << 1),
FEATURE_AVX = (1 << 2),
FEATURE_RAND = (1 << 3),
FEATURE_SHA = (1 << 4),
FEATURE_CLZERO = (1 << 5),
FEATURE_BMI1 = (1 << 6),
FEATURE_BMI2 = (1 << 7),
};
bool Requires3DNow() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_3DNOW; }
bool RequiresSSE4A() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_SSE4A; }
bool RequiresAVX() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_AVX; }
bool RequiresRAND() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_RAND; }
bool RequiresSHA() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_SHA; }
bool RequiresCLZERO() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_CLZERO; }
bool RequiresBMI1() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI1; }
bool RequiresBMI2() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI2; }
private:
FEX_CONFIG_OPT(ConfigDumpGPRs, DUMPGPRS);
@@ -506,7 +526,14 @@ namespace FEX::HarnessHelper {
}
bool Is64BitMode() const { return Config.Is64BitMode(); }
bool RequiresAVX() const { return Config.RequiresAVX(); }
bool Requires3DNow() const { return Config.Requires3DNow(); }
bool RequiresSSE4A() const { return Config.RequiresSSE4A(); }
bool RequiresAVX() const { return Config.RequiresAVX(); }
bool RequiresRAND() const { return Config.RequiresRAND(); }
bool RequiresSHA() const { return Config.RequiresSHA(); }
bool RequiresCLZERO() const { return Config.RequiresCLZERO(); }
bool RequiresBMI1() const { return Config.RequiresBMI1(); }
bool RequiresBMI2() const { return Config.RequiresBMI2(); }
private:
constexpr static uint64_t STACK_SIZE = FHU::FEX_PAGE_SIZE;
@@ -204,10 +204,10 @@ enum Syscalls_Arm64 {
SYSCALL_Arm64_semctl = 191,
SYSCALL_Arm64_semtimedop = 192,
SYSCALL_Arm64_semop = 193,
SYSCALL_Arm64_shmget = 194,
SYSCALL_Arm64_shmctl = 195,
SYSCALL_Arm64_shmat = 196,
SYSCALL_Arm64_shmdt = 197,
SYSCALL_Arm64__shmget = 194,
SYSCALL_Arm64__shmctl = 195,
SYSCALL_Arm64__shmat = 196,
SYSCALL_Arm64__shmdt = 197,
SYSCALL_Arm64_socket = 198,
SYSCALL_Arm64_socketpair = 199,
SYSCALL_Arm64_bind = 200,
@@ -714,7 +714,7 @@ namespace FEX::EmulatedFile {
dirfs != AT_FDCWD) {
// Passed in a dirfd that isn't magic FDCWD
// We need to get the path from the fd now
Path = FEX::get_fdpath(dirfs);
Path = FEX::get_fdpath(dirfs).value_or("");
if (pathname) {
if (!Path.empty()) {
+83 -96
View File
@@ -7,6 +7,7 @@ $end_info$
#include "Common/FDUtils.h"
#include "FEXCore/Config/Config.h"
#include "Tests/LinuxSyscalls/FileManagement.h"
#include "Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.h"
#include "Tests/LinuxSyscalls/Syscalls.h"
@@ -222,16 +223,81 @@ void FileManager::LoadThunkDatabase(bool Global) {
FileManager::FileManager(FEXCore::Context::Context *ctx)
: EmuFD {ctx} {
bool LoadedThunkDatabase{};
auto ThunkConfigFile = ThunkConfig();
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs());
if (ThunkConfigFile.size()) {
auto LoadThunksDB = [this, ThunkGuestPath](bool *LoadedThunkDatabase, json_t const* ThunksDB) {
// If a thunks DB property exists then we pull in data from the thunks database
// Load the initial thunks database
if (LoadedThunkDatabase) {
LoadThunkDatabase(true);
LoadThunkDatabase(false);
*LoadedThunkDatabase = true;
}
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs());
// Now load this property
for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) {
const char *LibraryName = json_getName(Item);
int64_t LibraryEnabled = json_getInteger(Item);
if (LibraryEnabled != 0) {
// If the library is enabled then find it in the DB
// Enable the overlay and all the dependencies in one go
auto DBObject = ThunkDB.find(LibraryName);
if (DBObject != ThunkDB.end() &&
DBObject->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBObject->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
DBObject->second.Enabled = true;
// Now walk the dependencies and set them up as well
// Make sure to enable each one as we go to remove circular dependencies
std::function<void(std::unordered_set<std::string> &Depends)> InsertDependencies
= [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set<std::string> &Depends) -> void {
for (auto &Depend : Depends) {
auto DBDepend = ThunkDB.find(Depend);
if (DBDepend != ThunkDB.end() &&
DBDepend->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBDepend->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
// Enabled, now walk this dependencies
DBDepend->second.Enabled = true;
InsertDependencies(DBDepend->second.Depends);
}
}
};
InsertDependencies(DBObject->second.Depends);
}
}
}
};
// We try to load ThunksDB from {FEX global config, FEX user config, AppConfig Global, AppConfig Local, Defined ThunksConfig option}
// This doesn't support the classic thunks interface.
std::vector<std::string> ConfigPaths {
FEXCore::Config::GetConfigFileLocation(true),
FEXCore::Config::GetConfigFileLocation(false),
FEXCore::Config::GetApplicationConfig(AppConfigName(), true),
FEXCore::Config::GetApplicationConfig(AppConfigName(), false),
ThunkConfigFile,
};
for (const auto &Path : ConfigPaths) {
std::vector<char> FileData;
if (LoadFile(FileData, ThunkConfigFile)) {
FileData.push_back(0);
if (LoadFile(FileData, Path)) {
JSON::JsonAllocator Pool {
.PoolObject = {
.init = JSON::PoolInit,
@@ -240,101 +306,22 @@ FileManager::FileManager(FEXCore::Context::Context *ctx)
};
json_t const *json = json_createWithPool(&FileData.at(0), &Pool.PoolObject);
json_t const* thunks = json_getProperty( json, "thunks" );
if (thunks && json_getType(thunks) == JSON_OBJ) {
json_t const* thunk;
for( thunk = json_getChild( thunks ); thunk != 0; thunk = json_getSibling( thunk )) {
char const* GuestThunk = json_getName( thunk );
jsonType_t propertyType = json_getType( thunk );
if (propertyType == JSON_TEXT) {
char const* RootFSLib = json_getValue( thunk );
auto ThunkPath = ThunkGuestPath / GuestThunk;
if (std::filesystem::exists(ThunkPath)) {
ThunkOverlays.emplace(RootFSLib, ThunkPath);
}
} else if (propertyType == JSON_ARRAY) {
json_t const* child;
for( child = json_getChild( thunk ); child != 0; child = json_getSibling( child ) ) {
if (json_getType( child ) == JSON_TEXT) {
char const* RootFSLib = json_getValue( child );
auto ThunkPath = ThunkGuestPath / GuestThunk;
if (std::filesystem::exists(ThunkPath)) {
ThunkOverlays.emplace(RootFSLib, ThunkPath);
}
}
}
}
}
}
json_t const* ThunksDB = json_getProperty( json, "ThunksDB" );
if (ThunksDB) {
// If a thunks DB property exists then we pull in data from the thunks database
// Load the initial thunks database
LoadThunkDatabase(true);
LoadThunkDatabase(false);
// Now load this property
for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) {
const char *LibraryName = json_getName(Item);
int64_t LibraryEnabled = json_getInteger(Item);
if (LibraryEnabled != 0) {
// If the library is enabled then find it in the DB
// Enable the overlay and all the dependencies in one go
auto DBObject = ThunkDB.find(LibraryName);
if (DBObject != ThunkDB.end() &&
DBObject->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBObject->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
DBObject->second.Enabled = true;
// Now walk the dependencies and set them up as well
// Make sure to enable each one as we go to remove circular dependencies
std::function<void(std::unordered_set<std::string> &Depends)> InsertDependencies
= [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set<std::string> &Depends) -> void {
for (auto &Depend : Depends) {
auto DBDepend = ThunkDB.find(Depend);
if (DBDepend != ThunkDB.end() &&
DBDepend->second.Enabled == false) {
auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName;
if (std::filesystem::exists(ThunkPath)) {
for (auto Overlay : DBDepend->second.Overlays) {
// Direct full path in guest RootFS to our overlay file
ThunkOverlays.emplace(Overlay, ThunkPath);
}
}
// Enabled, now walk this dependencies
DBDepend->second.Enabled = true;
InsertDependencies(DBDepend->second.Depends);
}
}
};
InsertDependencies(DBObject->second.Depends);
}
}
}
// Now clear the thunk database since we're loaded
ThunkDB.clear();
LoadThunksDB(&LoadedThunkDatabase, ThunksDB);
}
}
}
if (false) {
// Useful for debugging
if (ThunkOverlays.size()) {
LogMan::Msg::IFmt("Thunk Overlays:");
for (const auto& [Overlay, ThunkPath] : ThunkOverlays) {
LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath);
}
// Now clear the thunk database since we're loaded
ThunkDB.clear();
if (false) {
// Useful for debugging
if (ThunkOverlays.size()) {
LogMan::Msg::IFmt("Thunk Overlays:");
for (const auto& [Overlay, ThunkPath] : ThunkOverlays) {
LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath);
}
}
}
@@ -575,7 +562,7 @@ uint64_t FileManager::Readlinkat(int dirfd, const char *pathname, char *buf, siz
dirfd != AT_FDCWD) {
// Passed in a dirfd that isn't magic FDCWD
// We need to get the path from the fd now
Path = FEX::get_fdpath(dirfd);
Path = FEX::get_fdpath(dirfd).value_or("");
if (pathname) {
if (!Path.empty()) {
@@ -80,6 +80,7 @@ private:
FEX_CONFIG_OPT(ThunkHostLibs, THUNKHOSTLIBS);
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
FEX_CONFIG_OPT(ThunkConfig, THUNKCONFIG);
FEX_CONFIG_OPT(AppConfigName, APP_CONFIG_NAME);
uint32_t CurrentPID{};
void LoadThunkDatabase(bool Global);
+17 -17
View File
@@ -44,11 +44,11 @@ public:
FindPageRangePtr = &MemAllocator32Bit::FindPageRange;
}
}
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override;
int munmap(void *addr, size_t length) override;
void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override;
uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override;
uint64_t shmdt(const void* shmaddr) override;
void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override;
int Munmap(void *addr, size_t length) override;
void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override;
uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override;
uint64_t Shmdt(const void* shmaddr) override;
static constexpr bool SearchDown = true;
// PageAddr is a page already shifted to page index
@@ -131,7 +131,7 @@ uint64_t MemAllocator32Bit::FindPageRange_TopDown(uint64_t Start, size_t Pages)
return 0;
}
void *MemAllocator32Bit::mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
void *MemAllocator32Bit::Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
std::scoped_lock<std::mutex> lk{AllocMutex};
size_t PagesLength = FEXCore::AlignUp(length, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
@@ -282,7 +282,7 @@ restart:
return 0;
}
int MemAllocator32Bit::munmap(void *addr, size_t length) {
int MemAllocator32Bit::Munmap(void *addr, size_t length) {
std::scoped_lock<std::mutex> lk{AllocMutex};
size_t PagesLength = FEXCore::AlignUp(length, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
@@ -327,7 +327,7 @@ int MemAllocator32Bit::munmap(void *addr, size_t length) {
return 0;
}
void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) {
void *MemAllocator32Bit::Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) {
size_t OldPagesLength = FEXCore::AlignUp(old_size, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
size_t NewPagesLength = FEXCore::AlignUp(new_size, FHU::FEX_PAGE_SIZE) >> FHU::FEX_PAGE_SHIFT;
@@ -404,7 +404,7 @@ void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_s
// New Size is >= old size
// First, try and allocate a region the size of the new size
void *MappedPtr = this->mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
void *MappedPtr = this->Mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
std::scoped_lock<std::mutex> lk{AllocMutex};
if (FEX::HLE::HasSyscallError(MappedPtr)) {
// Couldn't find a region that fit our space
@@ -435,7 +435,7 @@ void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_s
return reinterpret_cast<void*>(-errno);
}
uint64_t MemAllocator32Bit::shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) {
uint64_t MemAllocator32Bit::Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) {
std::scoped_lock<std::mutex> lk{AllocMutex};
if (shmaddr != nullptr) {
@@ -548,9 +548,9 @@ restart:
}
}
}
uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) {
uint64_t MemAllocator32Bit::Shmdt(const void* shmaddr) {
std::scoped_lock<std::mutex> lk{AllocMutex};
uint32_t AddrPage = reinterpret_cast<uint64_t>(shmaddr) >> FHU::FEX_PAGE_SHIFT;
auto it = PageToShm.find(AddrPage);
@@ -567,7 +567,7 @@ uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) {
class MemAllocatorPassThrough final : public FEX::HLE::MemAllocator {
public:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override {
void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override {
uint64_t Result = (uint64_t)::mmap(addr, length, prot, flags, fd, offset);
if (Result == ~0ULL) {
return reinterpret_cast<void*>(-errno);
@@ -575,12 +575,12 @@ public:
return reinterpret_cast<void*>(Result);
}
int munmap(void *addr, size_t length) override {
int Munmap(void *addr, size_t length) override {
uint64_t Result = (uint64_t)::munmap(addr, length);
SYSCALL_ERRNO();
}
void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override {
void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override {
uint64_t Result = (uint64_t)::mremap(old_address, old_size, new_size, flags, new_address);
if (Result == ~0ULL) {
return reinterpret_cast<void*>(-errno);
@@ -588,7 +588,7 @@ public:
return reinterpret_cast<void*>(Result);
}
uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override {
uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override {
uint64_t Result = (uint64_t)::shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg);
if (Result != ~0ULL) {
*ResultAddress = Result;
@@ -597,7 +597,7 @@ public:
SYSCALL_ERRNO();
}
uint64_t shmdt(const void* shmaddr) override {
uint64_t Shmdt(const void* shmaddr) override {
uint64_t Result = ::shmdt(shmaddr);
SYSCALL_ERRNO();
}
+5 -5
View File
@@ -9,11 +9,11 @@ namespace FEX::HLE {
class MemAllocator {
public:
virtual ~MemAllocator() = default;
virtual void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0;
virtual int munmap(void *addr, size_t length) = 0;
virtual void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0;
virtual uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0;
virtual uint64_t shmdt(const void* shmaddr) = 0;
virtual void *Mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0;
virtual int Munmap(void *addr, size_t length) = 0;
virtual void *Mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0;
virtual uint64_t Shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0;
virtual uint64_t Shmdt(const void* shmaddr) = 0;
};
std::unique_ptr<FEX::HLE::MemAllocator> Create32BitAllocator();
+108
View File
@@ -14,6 +14,8 @@ $end_info$
#include "Tests/LinuxSyscalls/Syscalls/Thread.h"
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Types.h"
#include "Tests/LinuxSyscalls/x64/Types.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/Context.h>
@@ -58,6 +60,112 @@ namespace FEX::HLE {
class SignalDelegator;
SyscallHandler *_SyscallHandler{};
template<bool IncrementOffset, typename T>
uint64_t GetDentsEmulation(int fd, T *dirp, uint32_t count) {
std::vector<uint8_t> TmpVector(count);
void *TmpPtr = reinterpret_cast<void*>(&TmpVector.at(0));
uint64_t Offset = 0;
uint64_t TmpOffset = 0;
// Copy the incoming structures to our temporary array
while (Offset < count) {
T *Incoming = (T*)(reinterpret_cast<uint64_t>(dirp) + Offset);
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Incoming->d_reclen ||
(Offset + Incoming->d_reclen) > count) {
break;
}
size_t NewRecLen = FEXCore::AlignUp(Incoming->d_reclen + (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Incoming)),
alignof(decltype(Tmp->d_ino)));
Tmp->d_ino = Incoming->d_ino;
Tmp->d_off = Incoming->d_off;
Tmp->d_reclen = NewRecLen;
// d_type is hidden at the very end of reclen
Tmp->d_type = Incoming->d_name[Incoming->d_reclen - offsetof(T, d_name) - 1];
// This actually copies one more byte than the string of d_name
// Copies a null byte for the string
size_t CopySize = std::clamp<uint32_t>(Incoming->d_reclen - offsetof(T, d_name) - 1, 0U, count - Offset);
memcpy(Tmp->d_name, Incoming->d_name, CopySize);
// We take up 8 more bytes of space
TmpOffset += NewRecLen;
Offset += Incoming->d_reclen;
}
uint64_t Result = syscall(SYSCALL_DEF(getdents64),
static_cast<uint64_t>(fd),
TmpPtr,
static_cast<uint64_t>(count));
// Now copy back in to the array we were given
if (Result != -1) {
// If the outgoing d_ino is smaller than the incoming d_ino from the kernel
// Then we need to check for overflow before writing any of the data back
if (sizeof(decltype(FEX::HLE::x64::linux_dirent_64::d_ino)) > sizeof(decltype(T::d_ino))) {
uint64_t TmpOffset = 0;
while (TmpOffset < Result) {
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
decltype(T::d_ino) Result_d_ino = Tmp->d_ino;
if (Result_d_ino != Tmp->d_ino) {
// The resulting d_ino truncated, return error
return -EOVERFLOW;
}
TmpOffset += Tmp->d_reclen;
}
}
uint64_t Offset = 0;
uint64_t TmpOffset = 0;
size_t OffsetIndex = 1;
// With how the emulation occurs we will always return a smaller buffer than what was given to us
while (TmpOffset < Result) {
T *Outgoing = (T*)(reinterpret_cast<uint64_t>(dirp) + Offset);
FEX::HLE::x64::linux_dirent_64 *Tmp = (FEX::HLE::x64::linux_dirent_64*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Tmp->d_reclen) {
break;
}
size_t NewRecLen = FEXCore::AlignUp(Tmp->d_reclen - (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Outgoing)),
alignof(decltype(Tmp->d_ino)));
Outgoing->d_ino = Tmp->d_ino;
// 32-bit getdents can't safely handle d_off
// A safe way of emulating this is to just use an incrementing offset from 1
Outgoing->d_off = IncrementOffset ? OffsetIndex : Tmp->d_off;
size_t OffsetOfName = offsetof(std::remove_reference<decltype(*Tmp)>::type, d_name);
Outgoing->d_reclen = NewRecLen;
// Copies null character as well
size_t NameLength = Tmp->d_reclen - OffsetOfName - 1;
memcpy(Outgoing->d_name, Tmp->d_name, NameLength);
// Copy the hidden d_type flag
Outgoing->d_name[Outgoing->d_reclen - offsetof(T, d_name) - 1] = Tmp->d_type;
TmpOffset += Tmp->d_reclen;
// Outgoing is 5 bytes smaller
Offset += NewRecLen;
++OffsetIndex;
}
Result = Offset;
}
SYSCALL_ERRNO();
}
template
uint64_t GetDentsEmulation<false>(int, FEX::HLE::x64::linux_dirent*, uint32_t);
template
uint64_t GetDentsEmulation<true>(int, FEX::HLE::x32::linux_dirent_32*, uint32_t);
static bool IsSupportedByInterpreter(std::string const &Filename) {
// If it is a supported ELF then we can
if (ELFLoader::ELFContainer::IsSupportedELF(Filename.c_str())) {
+2
View File
@@ -509,6 +509,8 @@ static bool HasSyscallError(const void* Result) {
return HasSyscallError(reinterpret_cast<uintptr_t>(Result));
}
template<bool IncrementOffset, typename T>
uint64_t GetDentsEmulation(int fd, T *dirp, uint32_t count);
}
// Registers syscall for both 32bit and 64bit
+2 -2
View File
@@ -18,14 +18,14 @@ namespace FEX::HLE {
void RegisterSHM(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
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,
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();
@@ -199,16 +199,17 @@ void SyscallHandler::TrackMmap(uintptr_t Base, uintptr_t Size, int Prot, int Fla
fstat64(fd, &buf);
MRID mrid {buf.st_dev, buf.st_ino};
auto [Iter, Inserted] = VMATracking.MappedResources.emplace(mrid, MappedResource {nullptr, nullptr, 0});
Resource = &Iter->second;
auto filename = FEX::get_fdpath(fd);
if (Inserted) {
auto filename = FEX::get_fdpath(fd);
if (filename.has_value()) {
auto [Iter, Inserted] = VMATracking.MappedResources.emplace(mrid, MappedResource {nullptr, nullptr, 0});
Resource = &Iter->second;
Resource->AOTIRCacheEntry = FEXCore::Context::LoadAOTIRCacheEntry(CTX, filename);
Resource->Iterator = Iter;
if (Inserted) {
Resource->AOTIRCacheEntry = FEXCore::Context::LoadAOTIRCacheEntry(CTX, filename.value());
Resource->Iterator = Iter;
}
}
} else if (Flags & MAP_SHARED) {
MRID mrid{SpecialDev::Anon, AnonSharedId++};
@@ -340,9 +341,12 @@ void SyscallHandler::TrackShmat(int shmid, uintptr_t Base, int shmflg) {
}
void SyscallHandler::TrackShmdt(uintptr_t Base) {
FHU::ScopedSignalMaskWithUniqueLock lk(_SyscallHandler->VMATracking.Mutex);
uintptr_t Length = 0;
{
FHU::ScopedSignalMaskWithUniqueLock lk(_SyscallHandler->VMATracking.Mutex);
auto Length = VMATracking.ClearShmUnsafe(CTX, Base);
Length = VMATracking.ClearShmUnsafe(CTX, Base);
}
if (SMCChecks != FEXCore::Config::CONFIG_SMC_NONE) {
// This might over flush if the shm has holes in it
+1 -69
View File
@@ -583,75 +583,7 @@ namespace FEX::HLE::x32 {
REGISTER_SYSCALL_IMPL_X32(ioctl, ioctl32);
REGISTER_SYSCALL_IMPL_X32(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
#ifdef SYS_getdents
std::vector<uint8_t> TmpVector(count);
void *TmpPtr = reinterpret_cast<void*>(&TmpVector.at(0));
// Copy the incoming structures to our temporary array
for (uint64_t Offset = 0, TmpOffset = 0;
Offset < count;) {
linux_dirent_32 *Incoming = (linux_dirent_32*)(reinterpret_cast<uint64_t>(dirp) + Offset);
linux_dirent *Tmp = (linux_dirent*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Incoming->d_reclen ||
(Offset + Incoming->d_reclen) > count) {
break;
}
size_t NewRecLen = Incoming->d_reclen + (sizeof(linux_dirent) - sizeof(linux_dirent_32));
Tmp->d_ino = Incoming->d_ino;
Tmp->d_off = Incoming->d_off;
Tmp->d_reclen = NewRecLen;
// This actually copies two more bytes than the string of d_name
// Copies a null byte for the string
// Copies a d_type flag that lives after the name
size_t CopySize = std::clamp<uint32_t>(Incoming->d_reclen - offsetof(linux_dirent_32, d_name), 0U, count - Offset);
memcpy(Tmp->d_name, Incoming->d_name, CopySize);
// We take up 8 more bytes of space
TmpOffset += NewRecLen;
Offset += Incoming->d_reclen;
}
uint64_t Result = syscall(SYSCALL_DEF(getdents),
static_cast<uint64_t>(fd),
TmpPtr,
static_cast<uint64_t>(count));
// Now copy back in to the array we were given
if (Result != -1) {
uint64_t Offset = 0;
// With how the emulation occurs we will always return a smaller buffer than what was given to us
for (uint64_t TmpOffset = 0, num = 0; TmpOffset < Result; ++num) {
linux_dirent_32 *Outgoing = (linux_dirent_32*)(reinterpret_cast<uint64_t>(dirp) + Offset);
linux_dirent *Tmp = (linux_dirent*)(reinterpret_cast<uint64_t>(TmpPtr) + TmpOffset);
if (!Tmp->d_reclen) {
break;
}
size_t NewRecLen = Tmp->d_reclen - (sizeof(std::remove_reference<decltype(*Tmp)>::type) - sizeof(*Outgoing));
Outgoing->d_ino = Tmp->d_ino;
// If we pass d_off directly then we seem to encounter issues?
Outgoing->d_off = num; //Tmp->d_off;
size_t OffsetOfName = offsetof(std::remove_reference<decltype(*Tmp)>::type, d_name);
Outgoing->d_reclen = NewRecLen;
// Copies null character and d_type flag as well
memcpy(Outgoing->d_name, Tmp->d_name, Tmp->d_reclen - OffsetOfName);
TmpOffset += Tmp->d_reclen;
// Outgoing is 8 bytes smaller
Offset += NewRecLen;
}
Result = Offset;
}
SYSCALL_ERRNO();
#else
// XXX: Emulate
return -ENOSYS;
#endif
return GetDentsEmulation<true>(fd, reinterpret_cast<FEX::HLE::x32::linux_dirent_32*>(dirp), count);
});
REGISTER_SYSCALL_IMPL_X32(getdents64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
+8 -8
View File
@@ -23,7 +23,7 @@ namespace FEX::HLE::x32 {
void *x32SyscallHandler::GuestMmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
LOGMAN_THROW_AA_FMT((length >> 32) == 0, "values must fit to 32 bits");
auto Result = (uint64_t)GetAllocator()->mmap((void*)addr, length, prot, flags, fd, offset);
auto Result = (uint64_t)GetAllocator()->Mmap((void*)addr, length, prot, flags, fd, offset);
LOGMAN_THROW_AA_FMT((Result >> 32) == 0|| (Result >> 32) == 0xFFFFFFFF, "values must fit to 32 bits");
@@ -40,7 +40,7 @@ namespace FEX::HLE::x32 {
LOGMAN_THROW_AA_FMT((uintptr_t(addr) >> 32) == 0, "values must fit to 32 bits");
LOGMAN_THROW_AA_FMT((length >> 32) == 0, "values must fit to 32 bits");
auto Result = GetAllocator()->munmap(addr, length);
auto Result = GetAllocator()->Munmap(addr, length);
if (Result == 0) {
FEX::HLE::_SyscallHandler->TrackMunmap((uintptr_t)addr, length);
@@ -92,7 +92,7 @@ namespace FEX::HLE::x32 {
REGISTER_SYSCALL_IMPL_X32(mremap, [](FEXCore::Core::CpuStateFrame *Frame, void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) -> uint64_t {
uint64_t Result = reinterpret_cast<uint64_t>(static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
mremap(old_address, old_size, new_size, flags, new_address));
Mremap(old_address, old_size, new_size, flags, new_address));
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackMremap((uintptr_t)old_address, old_size, new_size, flags, Result);
@@ -111,11 +111,11 @@ namespace FEX::HLE::x32 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(shmat, [](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(_shmat, [](FEXCore::Core::CpuStateFrame *Frame, int shmid, const void *shmaddr, int shmflg) -> uint64_t {
// also implemented in ipc:OP_SHMAT
uint32_t ResultAddr{};
uint64_t Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg, &ResultAddr);
Shmat(shmid, reinterpret_cast<const void*>(shmaddr), shmflg, &ResultAddr);
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmat(shmid, ResultAddr, shmflg);
@@ -126,11 +126,11 @@ namespace FEX::HLE::x32 {
}
});
REGISTER_SYSCALL_IMPL_X32(shmdt, [](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
REGISTER_SYSCALL_IMPL_X32(_shmdt, [](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
// also implemented in ipc:OP_SHMDT
uint64_t Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmdt(shmaddr);
Shmdt(shmaddr);
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmdt((uintptr_t)shmaddr);
}
+9 -9
View File
@@ -281,7 +281,7 @@ namespace FEX::HLE::x32 {
case OP_SHMAT: {
// also implemented in memory:shmat
Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmat(first, reinterpret_cast<const void*>(ptr), second, reinterpret_cast<uint32_t*>(third));
Shmat(first, reinterpret_cast<const void*>(ptr), second, reinterpret_cast<uint32_t*>(third));
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmat(first, *reinterpret_cast<uint32_t*>(third), second);
}
@@ -290,7 +290,7 @@ namespace FEX::HLE::x32 {
case OP_SHMDT: {
// also implemented in memory:shmdt
Result = static_cast<FEX::HLE::x32::x32SyscallHandler*>(FEX::HLE::_SyscallHandler)->GetAllocator()->
shmdt(reinterpret_cast<void*>(ptr));
Shmdt(reinterpret_cast<void*>(ptr));
if (!FEX::HLE::HasSyscallError(Result)) {
FEX::HLE::_SyscallHandler->TrackShmdt(ptr);
}
@@ -317,7 +317,7 @@ namespace FEX::HLE::x32 {
else {
buf = *shmun.buf32;
}
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, &buf);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, &buf);
// IPC_SET sets the internal data structure that the kernel uses
// No need to writeback
break;
@@ -326,7 +326,7 @@ namespace FEX::HLE::x32 {
case SHM_STAT_ANY:
case IPC_STAT: {
struct shmid64_ds buf{};
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, &buf);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, &buf);
if (Result != -1) {
if (IPC64) {
*shmun.buf64 = buf;
@@ -339,7 +339,7 @@ namespace FEX::HLE::x32 {
}
case IPC_INFO: {
struct shminfo si{};
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
if (Result != -1) {
if (IPC64) {
*shmun.__buf64 = si;
@@ -352,7 +352,7 @@ namespace FEX::HLE::x32 {
}
case SHM_INFO: {
struct shm_info si{};
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, reinterpret_cast<struct shmid_ds*>(&si));
if (Result != -1) {
// SHM_INFO doesn't follow IPC64 behaviour
*shmun.__buf_info_32 = si;
@@ -360,13 +360,13 @@ namespace FEX::HLE::x32 {
break;
}
case SHM_LOCK:
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
break;
case SHM_UNLOCK:
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
break;
case IPC_RMID:
Result = ::syscall(SYSCALL_DEF(shmctl), shmid, cmd, nullptr);
Result = ::syscall(SYSCALL_DEF(_shmctl), shmid, cmd, nullptr);
break;
default:
@@ -410,10 +410,10 @@ enum Syscalls_x86 {
SYSCALL_x86_rseq = 386,
SYSCALL_x86_semget = 393,
SYSCALL_x86_semctl = 394,
SYSCALL_x86_shmget = 395,
SYSCALL_x86_shmctl = 396,
SYSCALL_x86_shmat = 397,
SYSCALL_x86_shmdt = 398,
SYSCALL_x86__shmget = 395,
SYSCALL_x86__shmctl = 396,
SYSCALL_x86__shmat = 397,
SYSCALL_x86__shmdt = 398,
SYSCALL_x86_msgget = 399,
SYSCALL_x86_msgsnd = 400,
SYSCALL_x86_msgrcv = 401,
+2 -10
View File
@@ -10,6 +10,7 @@ $end_info$
#include "Tests/LinuxSyscalls/x64/Types.h"
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/MathUtils.h>
#include <fcntl.h>
#include <poll.h>
@@ -196,16 +197,7 @@ namespace FEX::HLE::x64 {
});
REGISTER_SYSCALL_IMPL_X64(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
#ifdef SYS_getdents
uint64_t Result = syscall(SYSCALL_DEF(getdents),
static_cast<uint64_t>(fd),
reinterpret_cast<uint64_t>(dirp),
static_cast<uint64_t>(count));
SYSCALL_ERRNO();
#else
// XXX: Emulate
return -EFAULT;
#endif
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 {
+4 -4
View File
@@ -30,7 +30,7 @@ namespace FEX::HLE::x64 {
bool Map32Bit = flags & FEX::HLE::X86_64_MAP_32BIT;
if (Map32Bit) {
Result = (uint64_t)Get32BitAllocator()->mmap(addr, length, prot,flags, fd, offset);
Result = (uint64_t)Get32BitAllocator()->Mmap(addr, length, prot,flags, fd, offset);
if (FEX::HLE::HasSyscallError(Result)) {
errno = -Result;
Result = -1;
@@ -49,7 +49,7 @@ namespace FEX::HLE::x64 {
int x64SyscallHandler::GuestMunmap(void *addr, uint64_t length) {
uint64_t Result{};
if (reinterpret_cast<uintptr_t>(addr) < 0x1'0000'0000ULL) {
Result = Get32BitAllocator()->munmap(addr, length);
Result = Get32BitAllocator()->Munmap(addr, length);
if (FEX::HLE::HasSyscallError(Result)) {
errno = -Result;
@@ -117,7 +117,7 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
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));
@@ -127,7 +127,7 @@ namespace FEX::HLE::x64 {
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmdt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_X64_FLAGS(_shmdt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, const void *shmaddr) -> uint64_t {
uint64_t Result = ::shmdt(shmaddr);
@@ -36,9 +36,9 @@ enum Syscalls_x64 {
SYSCALL_x64_msync = 26,
SYSCALL_x64_mincore = 27,
SYSCALL_x64_madvise = 28,
SYSCALL_x64_shmget = 29,
SYSCALL_x64_shmat = 30,
SYSCALL_x64_shmctl = 31,
SYSCALL_x64__shmget = 29,
SYSCALL_x64__shmat = 30,
SYSCALL_x64__shmctl = 31,
SYSCALL_x64_dup = 32,
SYSCALL_x64_dup2 = 33,
SYSCALL_x64_pause = 34,
@@ -74,7 +74,7 @@ enum Syscalls_x64 {
SYSCALL_x64_semget = 64,
SYSCALL_x64_semop = 65,
SYSCALL_x64_semctl = 66,
SYSCALL_x64_shmdt = 67,
SYSCALL_x64__shmdt = 67,
SYSCALL_x64_msgget = 68,
SYSCALL_x64_msgsnd = 69,
SYSCALL_x64_msgrcv = 70,
+33
View File
@@ -194,4 +194,37 @@ using __time_t = time_t;
// Original definition in `arch/x86/include/uapi/asm/stat.h` for future excavation
static_assert(std::is_trivial<FEX::HLE::x64::guest_stat>::value, "Needs to be trivial");
static_assert(sizeof(FEX::HLE::x64::guest_stat) == 144, "Incorrect size");
// There is no public definition of this struct
// Matches the definition of `struct linux_dirent` in fs/readdir.c
struct
FEX_ANNOTATE("fex-match")
linux_dirent {
uint64_t d_ino;
uint64_t d_off;
uint16_t d_reclen;
char d_name[1];
/* Has hidden null character and d_type */
};
static_assert(std::is_trivial<linux_dirent>::value, "Needs to be trivial");
static_assert(offsetof(linux_dirent, d_ino) == 0, "Incorrect offset");
static_assert(offsetof(linux_dirent, d_off) == 8, "Incorrect offset");
static_assert(offsetof(linux_dirent, d_reclen) == 16, "Incorrect offset");
static_assert(offsetof(linux_dirent, d_name) == 18, "Incorrect offset");
static_assert(sizeof(linux_dirent) == 24, "Incorrect size");
// There is no public definition of this struct
// Matches the definition of `struct linux_dirent64` in include/linux/dirent.h
struct
FEX_ANNOTATE("fex-match")
FEX_PACKED
linux_dirent_64 {
uint64_t d_ino;
uint64_t d_off;
uint16_t d_reclen;
uint8_t d_type;
char d_name[];
};
static_assert(std::is_trivial<linux_dirent_64>::value, "Needs to be trivial");
static_assert(sizeof(linux_dirent_64) == 19, "Incorrect size");
}
+32 -12
View File
@@ -139,7 +139,7 @@ int main(int argc, char **argv, char **const envp) {
FEX_CONFIG_OPT(Core, CORE);
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
if (!Loader.Is64BitMode()) {
// Setup our userspace allocator
uint32_t KernelVersion = FEX::HLE::SyscallHandler::CalculateHostKernelVersion();
@@ -159,6 +159,32 @@ int main(int argc, char **argv, char **const envp) {
bool DidFault = false;
bool SupportsAVX = false;
FEXCore::Core::CPUState State;
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
auto CTX = FEXCore::Context::CreateNewContext();
FEXCore::Context::InitializeContext(CTX);
// Skip any tests that the host doesn't support features for
auto HostFeatures = FEXCore::Context::GetHostFeatures(CTX);
SupportsAVX = HostFeatures.SupportsAVX;
bool TestUnsupported =
(!HostFeatures.Supports3DNow && Loader.Requires3DNow()) ||
(!HostFeatures.SupportsSSE4A && Loader.RequiresSSE4A()) ||
(!SupportsAVX && Loader.RequiresAVX()) ||
(!HostFeatures.SupportsRAND && Loader.RequiresRAND()) ||
(!HostFeatures.SupportsSHA && Loader.RequiresSHA()) ||
(!HostFeatures.SupportsCLZERO && Loader.RequiresCLZERO()) ||
(!HostFeatures.SupportsBMI1 && Loader.RequiresBMI1()) ||
(!HostFeatures.SupportsBMI2 && Loader.RequiresBMI2());
if (TestUnsupported) {
FEXCore::Context::DestroyContext(CTX);
return 0;
}
if (Core != FEXCore::Config::CONFIG_CUSTOM) {
jmp_buf LongJump{};
int LongJumpVal{};
@@ -175,12 +201,6 @@ int main(int argc, char **argv, char **const envp) {
}, true);
// Run through FEX
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
auto CTX = FEXCore::Context::CreateNewContext();
FEXCore::Context::InitializeContext(CTX);
auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX, SignalDelegation.get())
: FEX::HLE::x32::CreateHandler(CTX, SignalDelegation.get(), std::move(Allocator));
@@ -195,14 +215,13 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Context::SetSignalDelegator(CTX, SignalDelegation.get());
FEXCore::Context::SetSyscallHandler(CTX, SyscallHandler.get());
bool Result1 = FEXCore::Context::InitCore(CTX, Loader.DefaultRIP(), Loader.GetStackPointer());
if (!Result1) {
return 1;
}
SupportsAVX = FEXCore::Context::GetHostFeatures(CTX).SupportsAVX;
LongJumpVal = setjmp(LongJump);
if (!LongJumpVal) {
FEXCore::Context::RunUntilExit(CTX);
@@ -212,8 +231,6 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Context::GetCPUState(CTX, &State);
SyscallHandler.reset();
FEXCore::Context::DestroyContext(CTX);
FEXCore::Context::ShutdownStaticTables();
} else {
// Run as host
SupportsAVX = true;
@@ -227,12 +244,15 @@ int main(int argc, char **argv, char **const envp) {
RunAsHost(SignalDelegation, Loader.DefaultRIP(), Loader.GetStackPointer(), &State);
}
FEXCore::Context::DestroyContext(CTX);
FEXCore::Context::ShutdownStaticTables();
bool Passed = !DidFault && Loader.CompareStates(&State, nullptr, SupportsAVX);
LogMan::Msg::IFmt("Faulted? {}", DidFault ? "Yes" : "No");
LogMan::Msg::IFmt("Passed? {}", Passed ? "Yes" : "No");
SignalDelegation.reset();
FEXCore::Config::Shutdown();
+10 -2
View File
@@ -3,6 +3,7 @@
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Core/Context.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/UContext.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
@@ -152,9 +153,16 @@ public:
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
memcpy(&OutState->xmm.avx.data[i], &_mcontext->fpregs->_xmm[i], sizeof(_mcontext->fpregs->_xmm[0]));
}
const auto* xstate = reinterpret_cast<FEXCore::x86_64::xstate*>(_mcontext->fpregs);
const auto* reserved = &xstate->fpstate.sw_reserved;
if (reserved->HasExtendedContext() && reserved->HasYMMH()) {
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; i++) {
memcpy(&OutState->xmm.avx.data[i][2], &xstate->ymmh.ymmh_space[i],
sizeof(xstate->ymmh.ymmh_space[0]));
}
}
uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7;
const uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7;
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
memcpy(&OutState->mm[(i + CurrentOffset) % 8], &_mcontext->fpregs->_st[i], sizeof(_mcontext->fpregs->_st[0]));
}
+297
View File
@@ -0,0 +1,297 @@
#include "VDSO_Emulation.h"
#include "FEXCore/IR/IR.h"
#include "Tests/LinuxSyscalls/x32/Types.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <filesystem>
#include <sys/mman.h>
#include <sys/time.h>
#include <unistd.h>
namespace FEX::VDSO {
using TimeType = decltype(::time)*;
using GetTimeOfDayType = decltype(::gettimeofday)*;
using ClockGetTimeType = decltype(::clock_gettime)*;
using ClockGetResType = decltype(::clock_getres)*;
using GetCPUType = decltype(FHU::Syscalls::getcpu)*;
TimeType TimePtr = ::time;
GetTimeOfDayType GetTimeOfDayPtr = ::gettimeofday;
ClockGetTimeType ClockGetTimePtr = ::clock_gettime;
ClockGetResType ClockGetResPtr = ::clock_getres;
GetCPUType GetCPUPtr = FHU::Syscalls::getcpu;
static void time(void* ArgsRV) {
struct ArgsRV_t {
time_t *a_0;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = TimePtr(args->a_0);
}
static void gettimeofday(void* ArgsRV) {
struct ArgsRV_t {
struct timeval *tv;
struct timezone *tz;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = GetTimeOfDayPtr(args->tv, args->tz);
}
static void clock_gettime(void* ArgsRV) {
struct ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = ClockGetTimePtr(args->clk_id, args->tp);
}
static void clock_getres(void* ArgsRV) {
struct ArgsRV_t {
clockid_t clk_id;
struct timespec *tp;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = ClockGetResPtr(args->clk_id, args->tp);
}
static void getcpu(void* ArgsRV) {
struct ArgsRV_t {
uint32_t *cpu;
uint32_t *node;
uint64_t rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = GetCPUPtr(args->cpu, args->node);
}
namespace x32 {
static void time(void* ArgsRV) {
struct ArgsRV_t {
HLE::x32::compat_ptr<FEX::HLE::x32::old_time32_t> a_0;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
time_t Host{};
args->rv = TimePtr(&Host);
if (args->a_0) {
*args->a_0 = Host;
}
}
static void gettimeofday(void* ArgsRV) {
struct ArgsRV_t {
HLE::x32::compat_ptr<FEX::HLE::x32::timeval32> tv;
HLE::x32::compat_ptr<struct timezone> tz;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
struct timeval tv64{};
struct timeval *tv_ptr{};
if (args->tv) {
tv_ptr = &tv64;
}
args->rv = GetTimeOfDayPtr(tv_ptr, args->tz);
if (args->tv) {
*args->tv = tv64;
}
}
static void clock_gettime(void* ArgsRV) {
struct ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
struct timespec tp64{};
args->rv = ClockGetTimePtr(args->clk_id, &tp64);
if (args->tp) {
*args->tp = tp64;
}
}
static void clock_gettime64(void* ArgsRV) {
struct ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<struct timespec> tp;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = ClockGetTimePtr(args->clk_id, args->tp);
}
static void clock_getres(void* ArgsRV) {
struct ArgsRV_t {
clockid_t clk_id;
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
struct timespec tp64{};
args->rv = ClockGetResPtr(args->clk_id, &tp64);
if (args->tp) {
*args->tp = tp64;
}
}
static void getcpu(void* ArgsRV) {
struct ArgsRV_t {
HLE::x32::compat_ptr<uint32_t> cpu;
HLE::x32::compat_ptr<uint32_t> node;
int rv;
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
args->rv = GetCPUPtr(args->cpu, args->node);
}
}
void LoadHostVDSO() {
void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso) {
vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
}
if (!vdso) {
// We couldn't load VDSO, fallback to C implementations. Which will still be faster than emulated libc versions.
LogMan::Msg::IFmt("linux-vdso implementation falling back to libc. Consider enabling VDSO in your kernel.");
return;
}
auto SymbolPtr = dlsym(vdso, "__vdso_time");
if (SymbolPtr) {
TimePtr = reinterpret_cast<TimeType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_gettimeofday");
if (SymbolPtr) {
GetTimeOfDayPtr = reinterpret_cast<GetTimeOfDayType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_clock_gettime");
if (SymbolPtr) {
ClockGetTimePtr = reinterpret_cast<ClockGetTimeType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_clock_getres");
if (SymbolPtr) {
ClockGetResPtr = reinterpret_cast<ClockGetResType>(SymbolPtr);
}
SymbolPtr = dlsym(vdso, "__vdso_getcpu");
if (SymbolPtr) {
GetCPUPtr = reinterpret_cast<GetCPUType>(SymbolPtr);
}
dlclose(vdso);
}
static std::vector<FEXCore::IR::ThunkDefinition> VDSODefinitions = {
{
// sha256(libVDSO:time)
{ 0x37, 0x63, 0x46, 0xb0, 0x79, 0x06, 0x5f, 0x9d, 0x00, 0xb6, 0x8d, 0xfd, 0x9e, 0x4a, 0x62, 0xcd, 0x1e, 0x6c, 0xcc, 0x22, 0xcd, 0xb2, 0xc0, 0x17, 0x7d, 0x42, 0x6a, 0x40, 0xd1, 0xeb, 0xfa, 0xe0 },
nullptr,
},
{
// sha256(libVDSO:gettimeofday)
{ 0x77, 0x2a, 0xde, 0x1c, 0x13, 0x2d, 0xe9, 0x48, 0xaf, 0xe0, 0xba, 0xcc, 0x6a, 0x89, 0xff, 0xca, 0x4a, 0xdc, 0xd5, 0x63, 0x2c, 0xc5, 0x62, 0x8b, 0x5d, 0xde, 0x0b, 0x15, 0x35, 0xc6, 0xc7, 0x14 },
nullptr,
},
{
// sha256(libVDSO:clock_gettime)
{ 0x3c, 0x96, 0x9b, 0x2d, 0xc3, 0xad, 0x2b, 0x3b, 0x9c, 0x4e, 0x4d, 0xca, 0x1c, 0xe8, 0x18, 0x4a, 0x12, 0x8a, 0xe4, 0xc1, 0x56, 0x92, 0x73, 0xce, 0x65, 0x85, 0x5f, 0x65, 0x7e, 0x94, 0x26, 0xbe },
nullptr,
},
{
// sha256(libVDSO:clock_gettime64)
{ 0xba, 0xe9, 0x6d, 0x30, 0xc0, 0x68, 0xc6, 0xd7, 0x59, 0x04, 0xf7, 0x10, 0x06, 0x72, 0x88, 0xfd, 0x4c, 0x57, 0x0f, 0x31, 0xa5, 0xea, 0xa9, 0xb9, 0xd3, 0x8d, 0x03, 0x81, 0x50, 0x16, 0x22, 0x71 },
nullptr,
},
{
// sha256(libVDSO:clock_getres)
{ 0xe4, 0xa1, 0xf6, 0x23, 0x35, 0xae, 0xb7, 0xb6, 0xb0, 0x37, 0xc5, 0xc3, 0xa3, 0xfd, 0xbf, 0xa2, 0xa1, 0xc8, 0x95, 0x78, 0xe5, 0x76, 0x86, 0xdb, 0x3e, 0x6c, 0x54, 0xd5, 0x02, 0x60, 0xd8, 0x6d },
nullptr,
},
{
// sha256(libVDSO:getcpu)
{ 0x39, 0x83, 0x39, 0x36, 0x0f, 0x68, 0xd6, 0xfc, 0xc2, 0x3a, 0x97, 0x11, 0x85, 0x09, 0xc7, 0x25, 0xbb, 0x50, 0x49, 0x55, 0x6b, 0x0c, 0x9f, 0x50, 0x37, 0xf5, 0x9d, 0xb0, 0x38, 0x58, 0x57, 0x12 },
nullptr,
},
};
void* LoadVDSOThunks(bool Is64Bit, MapperFn Mapper) {
void* VDSOBase{};
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
FEX_CONFIG_OPT(ThunkGuestLibs32, THUNKGUESTLIBS32);
std::filesystem::path ThunkGuestPath{};
if (Is64Bit) {
ThunkGuestPath = std::filesystem::path(ThunkGuestLibs()) / "libVDSO-guest.so";
// Set the Thunk definition pointers for x86-64
VDSODefinitions[0].ThunkFunction = &FEX::VDSO::time;
VDSODefinitions[1].ThunkFunction = &FEX::VDSO::gettimeofday;
VDSODefinitions[2].ThunkFunction = &FEX::VDSO::clock_gettime;
VDSODefinitions[3].ThunkFunction = &FEX::VDSO::clock_gettime;
VDSODefinitions[4].ThunkFunction = &FEX::VDSO::clock_getres;
VDSODefinitions[5].ThunkFunction = &FEX::VDSO::getcpu;
}
else {
ThunkGuestPath = std::filesystem::path(ThunkGuestLibs32()) / "libVDSO-guest.so";
// Set the Thunk definition pointers for x86
VDSODefinitions[0].ThunkFunction = &FEX::VDSO::x32::time;
VDSODefinitions[1].ThunkFunction = &FEX::VDSO::x32::gettimeofday;
VDSODefinitions[2].ThunkFunction = &FEX::VDSO::x32::clock_gettime;
VDSODefinitions[3].ThunkFunction = &FEX::VDSO::x32::clock_gettime64;
VDSODefinitions[4].ThunkFunction = &FEX::VDSO::x32::clock_getres;
VDSODefinitions[5].ThunkFunction = &FEX::VDSO::x32::getcpu;
}
// Load VDSO if we can
int VDSOFD = ::open(ThunkGuestPath.string().c_str(), O_RDONLY);
if (VDSOFD != -1) {
// Get file size
size_t VDSOSize = lseek(VDSOFD, 0, SEEK_END);
if (VDSOSize >= 4) {
// Reset to beginning
lseek(VDSOFD, 0, SEEK_SET);
VDSOSize = FEXCore::AlignUp(VDSOSize, 4096);
// Map the VDSO file to memory
VDSOBase = Mapper(nullptr, VDSOSize, PROT_READ, MAP_PRIVATE, VDSOFD, 0);
// Since we found our VDSO thunk library, find our host VDSO function implementations.
LoadHostVDSO();
}
close(VDSOFD);
}
return VDSOBase;
}
std::vector<FEXCore::IR::ThunkDefinition> const& GetVDSOThunkDefinitions() {
return VDSODefinitions;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <FEXCore/IR/IR.h>
namespace FEX::VDSO {
using MapperFn = std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)>;
void* LoadVDSOThunks(bool Is64Bit, MapperFn Mapper);
std::vector<FEXCore::IR::ThunkDefinition> const& GetVDSOThunkDefinitions();
}
+2
View File
@@ -74,6 +74,7 @@ namespace {
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS_INTERPRETER);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_INTERPRETER_INSTALLED);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_FILENAME);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_CONFIG_NAME);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS64BIT_MODE);
}
@@ -106,6 +107,7 @@ namespace {
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS_INTERPRETER);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_INTERPRETER_INSTALLED);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_FILENAME);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_CONFIG_NAME);
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS64BIT_MODE);
return true;
+15 -3
View File
@@ -8,6 +8,7 @@
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <optional>
#include <sstream>
#include <sys/mman.h>
#include <sys/syscall.h>
@@ -441,7 +442,7 @@ namespace WebFileFetcher {
FileType Type;
};
const static std::string DownloadURL = "https://rootfs.fex-emu.org/file/fex-rootfs/RootFS_links.json";
const static std::string DownloadURL = "https://rootfs.fex-emu.com/file/fex-rootfs/RootFS_links.json";
std::string DownloadToString(const std::string &URL) {
std::string BigArgs =
@@ -514,10 +515,14 @@ namespace WebFileFetcher {
return &*alloc->json_objects->emplace(alloc->json_objects->end());
}
std::vector<FileTargets> GetRootFSLinks() {
std::optional<std::vector<FileTargets>> GetRootFSLinks() {
// Decode the filetargets
std::string Data = DownloadToString(DownloadURL);
if (Data.empty()) {
return std::nullopt;
}
JsonAllocator Pool {
.PoolObject = {
.init = PoolInit,
@@ -1121,7 +1126,14 @@ int main(int argc, char **argv, char **const envp) {
}
if (AskForConfirmation(Question)) {
auto Targets = WebFileFetcher::GetRootFSLinks();
auto TargetReturn = WebFileFetcher::GetRootFSLinks();
if (!TargetReturn.has_value()) {
ExecWithInfo("Couldn't download rootfs list from the server. Try again in a minute or report on the fex-emu issue tracker.");
return -1;
}
auto Targets = TargetReturn.value();
if (Targets.empty()) {
ExecWithInfo("Couldn't parse rootfs definition URL.");
return -1;
+22 -3
View File
@@ -401,10 +401,29 @@ namespace ProcessPipe {
case FEXServerClient::PacketType::TYPE_GET_PID_FD: {
int FD = FHU::Syscalls::pidfd_open(::getpid(), 0);
SendFDSuccessPacket(Socket, FD);
if (FD < 0) {
// Couldn't get PIDFD due to too old of kernel.
// Return a pipe to track the same information.
//
int fds[2];
pipe2(fds, O_CLOEXEC);
SendFDSuccessPacket(Socket, fds[0]);
// Close the FD now since we've sent it
close(FD);
// Close the read side now, doesn't matter to us
close(fds[0]);
// Check if we need to increase the FD limit.
++NumFilesOpened;
CheckRaiseFDLimit();
// Write side will naturally close on process exit, letting the other process know we have exited.
}
else {
SendFDSuccessPacket(Socket, FD);
// Close the FD now since we've sent it
close(FD);
}
CurrentOffset += sizeof(FEXServerClient::FEXServerRequestPacket::Header);
break;
Loaded 100 of 269 files, more files were not shown because too many files have changed in this diff. Show more