Compare commits

...
159 Commits
Author SHA1 Message Date
Ryan Houdek 3d930ee4b8 Docs: Update for release FEX-2302 2023-02-03 17:24:08 -08:00
Mai a7aeb4af7f Merge pull request #2368 from Sonicadvance1/fexrootfsfetcher_first_option
FEXRootFSFetcher: Support option to auto select first distro
2023-02-03 17:31:31 -05:00
Mai d2d528222c Merge pull request #2370 from Sonicadvance1/remove_pollremove
FEXServer: Remove POLLREMOVE usage
2023-02-03 17:30:45 -05:00
Ryan Houdek 6598eeee92 FEXServer: Remove POLLREMOVE usage
Fixes this file compiling on musl at least.

POLLREMOVE usage here is technically incorrect as it shouldn't be OR'd
with other flags.
But it is also additionally wrong here because the Linux kernel doesn't
even support this flag anymore, so it doesn't change behaviour.
2023-02-03 13:35:30 -08:00
Ryan Houdek c42fd4122b FEXRootFSFetcher: Support option to auto select first distro
Fixes #2356

In the case of the `-y` option being used, it will auto say "yes", but
when presented with the distro list this doesn't work. This happens when
used on a distro that doesn't have an exact match to what we provide.

Exposes a new option that when presented the distro list, auto select
the first option. Solving this issue when automating.
2023-02-03 10:48:23 -08:00
Ryan Houdek 9d33bba1c8 Merge pull request #2366 from lioncash/addsub
ARMEmitter: Handle integer add/subtract vectors (predicated) instruction class
2023-02-03 10:31:56 -08:00
Ryan Houdek a899f9f824 Merge pull request #2367 from lioncash/rmif
ARMEmitter: Handle RMIF, SETF8/SETF16
2023-02-02 20:55:54 -08:00
Lioncache 8c09356bd7 ARMEmitter: Handle SETF16 2023-02-02 23:27:40 -05:00
Lioncache 50bcc1b96f ARMEmitter: Handle SETF8 2023-02-02 23:26:07 -05:00
Lioncache 36831ebc37 ARMEmitter: Handle RMIF 2023-02-02 23:18:22 -05:00
Lioncache 44f5d788c8 ARMEmitter: Handle SUBR (vector, predicated) 2023-02-02 21:44:44 -05:00
Lioncache a42ae7d385 ARMEmitter: Handle SUB (vector, predicated) 2023-02-02 21:42:57 -05:00
Lioncache 5cf9bb2613 ARMEmitter: Handle ADD (vector, predicated) 2023-02-02 21:41:12 -05:00
Ryan Houdek 1cda029ed7 Merge pull request #2365 from lioncash/reduce
ARMEmitter: Handle SVE floating-point recursive reduction
2023-02-02 17:59:29 -08:00
Lioncache 4001dc1219 ARMEmitter: Handle SVE FMINV 2023-02-02 20:42:54 -05:00
Lioncache f77de7f283 ARMEmitter: Handle SVE FMAXV 2023-02-02 20:41:10 -05:00
Lioncache ac9f9d291b ARMEmitter: Handle SVE FMINNMV 2023-02-02 20:35:43 -05:00
Lioncache 25f97065df ARMEmitter: Handle SVE FMAXNMV 2023-02-02 20:33:37 -05:00
Lioncache 6fcbce0c52 ARMEmitter: Handle SVE FADDV 2023-02-02 20:28:11 -05:00
Ryan Houdek 2c9f99e5d6 Merge pull request #2364 from lioncash/hist
ARMEmitter: Add a few missing instructions
2023-02-02 13:31:08 -08:00
Lioncache 4c647a2e02 ARMEmitter: Handle NMATCH 2023-02-02 15:40:34 -05:00
Lioncache d0f00d53d3 ARMEmitter: Handle MATCH 2023-02-02 15:40:34 -05:00
Lioncache 6174437667 ARMEmitter: Handle SVE FCMLA 2023-02-02 15:40:34 -05:00
Lioncache 9c762861f6 ARMEmitter: Handle SVE FCADD 2023-02-02 15:40:34 -05:00
Lioncache 448785e693 ARMEmitter: Handle HISTSEG 2023-02-02 15:40:26 -05:00
Lioncache f8c68acc09 ARMEmitter: Handle HISTCNT 2023-02-02 15:40:18 -05:00
Ryan Houdek 65971effc7 Merge pull request #2363 from Sonicadvance1/fix_relative_execve
Config: Fix relative execve applications.
2023-02-02 05:01:32 -08:00
Ryan Houdek d5e7af5b96 Config: Fix relative execve applications.
I made the assumption from some bad historical knowledge that the kernel
will canonicalize relative filenames and symlinks for applications that
execute through execve.

This turns out to not be true. In fact it passes pathname untouched to
the interpreter. So we need to do an additional fix up on relative paths
to ensure glibc doesn't break.

Fixes a major bug that breaks a bunch of games.
2023-02-02 04:42:29 -08:00
Ryan Houdek 62e6ada112 Merge pull request #2362 from lioncash/blendd
OpcodeDispatcher: Handle VPBLENDD/VBLENDPS
2023-02-01 20:16:09 -08:00
Lioncache 2e232ac3dd OpcodeDispatcher: Handle VBLENDPS 2023-02-01 20:36:23 -05:00
Lioncache 88ff0db12a OpcodeDispatcher: Handle VPBLENDD 2023-02-01 20:36:15 -05:00
Ryan Houdek 9d35bc01c7 Merge pull request #2361 from Sonicadvance1/fix_global_symbol_overrides
Thunks: Fixes host symbol overrides
2023-02-01 07:09:25 -08:00
Ryan Houdek c8c1ebad01 unittests: Updates tests to have a dlsym_default function 2023-02-01 06:48:26 -08:00
Ryan Houdek cb5573995d Thunks: Fixes host symbol overrides
1) The host library needs to be loaded in the global namespace.

2) We need to use `RTLD_DEFAULT` instead of querying the object
   directly.

We need to load the host library in the global namespace so the symbols
end up in the global symbol table. This follows how all these symbols
/usually/ get loaded. Either by linking directly to the library or how
loaders will end up loading these.

We need to use RTLD_DEFAULT to follow symbol overriding rules that tend
to occur. For example, MangoHUD will LD_PRELOAD a library that provides
GLX and EGL symbols. Which FEX's thunk libraries need to pick up this
override.
If we are querying the host library directly then we fail to pickup
these overrides, thus breaking MangoHUD and other overlays.
2023-02-01 06:47:41 -08:00
Ryan Houdek fa1193f14c Merge pull request #2344 from Sonicadvance1/siginfo_32
FEXCore: Fixup 32-bit signal handling
2023-01-31 20:26:36 -08:00
Ryan Houdek 9a318cad95 Merge pull request #2360 from lioncash/ravd-adj
Arm64/VectorOps: Clamp shift amount to esize-1 for VSShr
2023-01-31 20:25:46 -08:00
Lioncache 4177d5c185 Arm64/VectorOps: Clamp shift amount to esize-1 for VSShr
Makes the behavior consistent with the x86 JIT.

We need to treat values larger than 31 as if they were 31 bit shifts in
order to handle sign-extending behavior properly.
2023-01-31 22:53:51 -05:00
Ryan Houdek fe79f61fc3 Merge pull request #2359 from lioncash/ravd
OpcodeDispatcher: Handle VPSRAVD
2023-01-31 18:32:48 -08:00
Lioncache d5316c8c7e OpcodeDispatcher: Handle VPSRAVD 2023-01-31 17:31:24 -05:00
Lioncache cc65f3e788 Arm64/VectorOps: Implement VSShr
This will be used for implementing VPSRAVD
2023-01-31 17:31:20 -05:00
Mai 787b6895e8 Merge pull request #2337 from Sonicadvance1/optimize_frontend
Frontend: Various optimizations
2023-01-31 14:46:11 +00:00
Mai 7be2e1ad34 Merge pull request #2330 from Sonicadvance1/implement_flushes
OpDispatcher: Adds support for CLWB and CLFLUSHOPT
2023-01-31 04:01:26 +00:00
Mai 9403c662a3 Merge pull request #2320 from Sonicadvance1/remove_numargs
IR: Removes NumArgs member from IR ops
2023-01-31 04:00:57 +00:00
Ryan Houdek 15f2b30a5b FEXLinuxTests: Adds 32-bit signal tests 2023-01-30 13:30:15 -08:00
Ryan Houdek d75e1f996f FEXCore: Fixup 32-bit signal handling
Follow-up to #2327.

Split off from #2176 and improved.

32-bit signals are a bit more complex than 64-bit due to behaviour
changing depending on if `rt_sigaction` and `sigaction` syscall is used
and if `SA_SIGINFO` is passed in to the flags.

With `SA_SIGINFO` used, both turn in to an `RT` frame, which is encoded
differently than without `SA_SIGINFO`.
Additionally 32-bit signals support both regular Linux stack ABI and
`regparm(3)` ABI.

Without `SA_SIGINFO` then `siginfo_t` is removed from the signal handler
arguments, but most of the rest still remains.
Also two of the arguments to the signal handler are forced to be nullptr
with `regparm(3)`.
2023-01-30 13:30:15 -08:00
Ryan Houdek 14fe95bd14 IR: Removes NumArgs member from IR ops
Split off from #2243 to remove each member individually.

Shaves 8-bits off of each IR op.
No need to cart around this data when it is constant for each operation.
Especially since most optimization passes don't need the data anyway.

Needed to add a new `GetRAArgs` to get the number of SSA arguments that
get RA versus `GetArgs` which returns all SSA arguments the IR operation
owns. This is what was causing #2243 to fail CI since it needs to know
the difference in some places.
2023-01-30 11:53:05 -08:00
Ryan Houdek 65b6b6d5dd Merge pull request #2355 from Sonicadvance1/siginfo_64
Dispatcher: Extract 64-bit signal frame save and restore
2023-01-30 11:50:03 -08:00
Mai f8e762fcfb Merge pull request #2319 from Sonicadvance1/remove_has_dest
IR: Remove HasDest member
2023-01-30 16:25:09 +00:00
Ryan Houdek 9cfd169fb8 Dispatcher: Extract 64-bit signal frame save and restore
Stripped from #2344 at request to ensure 64-bit code hasn't changed in a
meaningful way. So that PR can focus on 32-bit.
2023-01-27 01:17:32 -08:00
Ryan Houdek 3d29dac1b1 Merge pull request #2354 from neobrain/fix_single_line_shebang
Syscalls: Fix out-of-bounds read when handling single-line shebang files
2023-01-26 02:38:38 -08:00
Tony Wasserka 94ef3729bf Syscalls: Avoid unnecessary string copies and clean up error handling 2023-01-26 11:20:29 +01:00
Tony Wasserka 420c4ca08f Syscalls: Fix out-of-bounds read when handling single-line shebang files
string::find() returns npos (-1) if the given character was not found, so
it can't be used to construct a string like this. Luckily, the use of
std::span allows this code to be written such that it's both correct and
simpler than before.
2023-01-26 11:20:29 +01:00
Mai 477d4b6de8 Merge pull request #2353 from Sonicadvance1/fix_shebang_execve
Linux: Fixes shebang file execution
2023-01-26 05:19:45 +00:00
Ryan Houdek 2b318d276f Linux: Fixes shebang file execution
Somewhere during the refactoring/review process, failed to strip the
shebang prefix off of the arguments.
Causing shebang files to always fail as if the file never existed.

Fixes steam execution.
2023-01-25 20:25:51 -08:00
Mai da88c68e12 Merge pull request #2332 from Sonicadvance1/emitter_test_ci
Github: Add ARM emitter tests to CI
2023-01-25 23:47:39 +00:00
Mai 7f6a620c9e Merge pull request #2349 from Sonicadvance1/virtual_mem_size_32bit
Core: Adjust virtual memory size for 32-bit
2023-01-24 21:12:36 +00:00
Mai 1e90ebb400 Merge pull request #2323 from Sonicadvance1/pool_inline_constants
ConstProp: Pool inline constants
2023-01-24 21:11:56 +00:00
Ryan Houdek c6d46801ad ConstProp: Pool inline constants
In large blocks we can be generating a ton of inline constants. But in
most cases these end up being 0, 1, or (1 << N).
Add these to a map and reuse if possible. Makes some IR blocks
significantly smaller for later optimization passes.
2023-01-24 12:58:29 -08:00
Mai afaff9293b Merge pull request #2316 from Sonicadvance1/fix_negative_ficomi_f64
X87_F64: Fixes FICOM
2023-01-24 17:31:05 +00:00
Ryan Houdek dcce9add60 Merge pull request #2334 from Sonicadvance1/fix_execveat
FEXLoader: Adds support for execveat with AT_EMPTY_PATH
2023-01-23 02:32:15 -08:00
Ryan Houdek 472675d471 FEXLoader: Adds support for execveat with AT_EMPTY_PATH
Fixes #2136

This is a fairly tricky edge case to support with FEX.
If execveat is used with AT_EMPTY_PATH then the application can pass an
FD to execve instead of a filename. This includes FDs that have been
deleted from the disk so the child process can't open it by filename
anymore.

To work around this limitation, we need to pass the FD to the new FEX
process and open it directly, similar to how binfmt_misc works with FDs.
The FD will get passed through environment variables, which the new
process will check for and then remove the variable from the
environment.

Lots of prickly edge cases to support here.

Without binfmt_misc:
- Passes the FD to FEXLoader directly.
  - Requires duplicating the FD if it has O_CLOEXEC on the FD.

With binfmt_misc:
- Shebang file, pass directly to FEXLoader, just like without binfmt.
- x86 ELF Files, rely on the kernel's binfmt_misc support here.
- Unsupported ELF files, let kernel handle it through binfmt_misc

Argument handling:
- The application can pass in no arguments.
  - Means our application configurations were failing to find a config
  - Also various checks in the frontend were failing.
  - If opened through an FD, find the symlink for that FD for the
    application configuration instead.

Side note:
Fixed a performance issue in execve where when we were checking for file
format support. Either ELF or Shebang files, we were reading the /whole/
file upfront. We only need to read a header worth of ELF files, and only
257 bytes if it is potentially a shebang file. Should dramatically
reduce some application's execve times.
2023-01-23 02:06:50 -08:00
Mai a28039f7cd Merge pull request #2350 from Sonicadvance1/optimize_dispatcher_slightly
Arm64: Merge two loads in to an LDP
2023-01-23 08:35:13 +00:00
Mai f8d56a8170 Merge pull request #2351 from Sonicadvance1/support_long_address_generation
ARMEmitter: Support helper for long address generation
2023-01-23 02:05:06 +00:00
Ryan Houdek db4cb497e0 unittests: New emitter tests for LongAddressGen 2023-01-22 16:03:17 -08:00
Ryan Houdek a823d918c2 ARMEmitter: Support helper for long address generation
The current separated adr and adrp handlers are difficult to use if you
don't know if the resulting address is going to be within 1MB or 4GB.

Adds a `LongAddressGen` helper that will generate the various pieces of
code that will need to be emitted.

Backward labels:
 - Can generate three different code segments depending on distance to
   label
   - adr if label is within 1MB
   - adrp if label is 4K page aligned and within 4GB
   - adrp+add if label is within 4GB

Forward labels:
- Can generate three different code segments depending on distance to
  label
  - nop+adr if label is within 1MB
  - nop+adrp if label is 4K page aligned and within 4GB
  - adrp+add if label is within 4GB

There is still the limitation that this can't generate addresses to
labels that are >4GB away. Which is fine.
2023-01-22 16:03:17 -08:00
Ryan Houdek 79b8442dbc FHU: Add helpers for symlink checking 2023-01-22 14:23:55 -08:00
Ryan Houdek 7bf1742434 Arm64: Merge two loads in to an LDP
We can do a single LDP upfront when loading from the code cache, which
saves an instruction and one LDP costs the same as a single LDR.

Itty bitty optimization in the hot dispatcher.
2023-01-20 19:19:47 -08:00
Ryan Houdek 09f720d1cc Core: Adjust virtual memory size for 32-bit
We only need a 32-bit virtual memory size when running a 32-bit
application.

Just lowers some virtual memory space that we need to allocate.
2023-01-20 19:18:52 -08:00
Ryan Houdek 28dd94642a Merge pull request #2339 from Sonicadvance1/optimize_loadfile
FileLoading: Optimize FileLoad
2023-01-20 13:35:14 -08:00
Ryan Houdek 8dae785e9e Merge pull request #2327 from Sonicadvance1/siginfo
Dispatcher: Fixes x86-64 SA_SIGINFO generation
2023-01-20 13:34:56 -08:00
Ryan Houdek 7ef9189910 FEXLinuxTests: Fixup the tests
These were having some issues executing. 32-bit ones were getting
skipped even.
2023-01-20 12:55:38 -08:00
Ryan Houdek c5d0fe6999 FEXLinuxTests: Adds 64-bit siginfo test. 2023-01-20 11:08:57 -08:00
Ryan Houdek ac1bf0683d FEXLinuxTests: Adds support for 64-bit only tests 2023-01-20 11:08:57 -08:00
Ryan Houdek 7897803753 Dispatcher: Fixes x86-64 SA_SIGINFO generation
Pulled from #2176.

On x86-64 the SA_SIGINFO sa_flag is actually a no-op. It is always used
even if not set.

Ensure that we setup siginfo_t regardless of flag being set.

On 32-bit x86 this still needs to be adhered to.

Little side bits that don't change anything
- EFLAGS is passed in signfo correctly.
- User provided restorer usage locations is documented but not
  implemented.
2023-01-20 11:08:57 -08:00
Ryan Houdek 40e5690e3a Dispatcher: Encode eflags in uc_mcontext
We were missing this.
2023-01-20 11:08:57 -08:00
Ryan Houdek 8d0329ddaf Merge pull request #2348 from stevenvandenbrandenstift/fixupTgkill
fix ifdef to use HAS_SYSCALL_TGKILL for tgkill as it was intented
2023-01-19 13:42:28 -08:00
Steven Vanden Branden fd5bfd9e40 fix ifdef to use HAS_SYSCALL_TGKILL for tgkill as it was intented 2023-01-19 22:29:12 +01:00
Mai 5fd8fdbf5c Merge pull request #2346 from Sonicadvance1/armemitter_warnings
ARMEmitter: Removes some warnings that cropped up
2023-01-19 19:04:42 +00:00
Mai a486797e59 Merge pull request #2347 from Sonicadvance1/fix_jitsymbols
JitSymbols: Fixes file opening and writing
2023-01-19 19:04:22 +00:00
Ryan Houdek 6193bddaa5 JitSymbols: Fixes file opening and writing
We shouldn't use O_EXCL, since we need to overwrite previous entry PIDs
if they happen to exist. The kernel ensures that PIDs don't overlap, but
in some kernel configurations PIDs are aggressively reused, resulting
in O_EXCL quickly hitting an issue when writing stale files.

Additionally O_DIRECT, this doesn't allow us to write to files, so all
write functions were failing.

Additionally use O_APPEND, we are only ever appending, so let the kernel
know.

Additionally use O_TRUNC, in the case that a stale perf file exists,
this will immediately truncate the file to zero.
2023-01-19 01:13:44 -08:00
Ryan Houdek 87609b2938 CPUID: Only expose CLWB if supported 2023-01-18 17:56:21 -08:00
Ryan Houdek bd36bd55ca HostFeatures: Adds support for querying CLWB availability
The x86 runner doesn't support CLWB natively.
2023-01-18 17:56:21 -08:00
Ryan Houdek 965c6ff6cc unittests: Adds tests for CLWB and CLFLUSHOPT 2023-01-18 17:56:21 -08:00
Ryan Houdek 7450b5d406 OpDispatcher: Adds support for CLWB and CLFLUSHOPT
These fairly trivially map to AArch64 operations.

CLWB just maps to `dc cvac`
CLFLUSHOPT just maps to `dc civac` without the final `dsb`.

Also captures if something tries using XSAVEOPT without checking.
2023-01-18 17:56:21 -08:00
Ryan Houdek 4582c8d380 IR: Adds support for CacheLineClean and non-serializing clear
These will be used in the next commit.
2023-01-18 17:56:21 -08:00
Ryan Houdek b621b61d92 HostRunner: Fix for new xbyak 2023-01-18 17:53:52 -08:00
Ryan Houdek 7d3e7d2ab4 Externals: Update xbyak to v6.68 2023-01-18 17:53:52 -08:00
Ryan Houdek c4a1d7e0cd FileLoading: Optimize FileLoad
Optimize `FileLoad` by not using fstream.
For some reason fstream is just really bad.

Switching over to raw pread cuts the amount of time it takes to read
files by a quarter of CPU time.
2023-01-18 17:51:38 -08:00
Ryan Houdek bf4c5797db ARMEmitter: Removes some warnings that cropped up 2023-01-18 17:48:44 -08:00
Mai 4aa984aed9 Merge pull request #2322 from Sonicadvance1/opdispatcher_helpers
OpDispatcher: Fixes a few missing GPR/XMM helper usages
2023-01-19 00:05:11 +00:00
Mai 95e544c840 Merge pull request #2342 from Sonicadvance1/more_asimd_ops_pt2
ArmEmitter: Adds two more classes of ASIMD instructions
2023-01-18 20:33:40 +00:00
Mai 81e0ac7e0b Merge pull request #2331 from Sonicadvance1/more_asimd_ops
ArmEmitter: Adds three more classes of ASIMD instructions
2023-01-18 20:32:46 +00:00
Mai f8d92aa121 Merge pull request #2329 from Sonicadvance1/fix_cache_invalidation
Arm64: Fixes incorrect operation for CacheLineClear
2023-01-18 20:29:44 +00:00
Mai ee58c5de1d Merge pull request #2315 from Sonicadvance1/add_negative_unittests
unittests: Adds negative integer x87 tests
2023-01-18 20:27:59 +00:00
Mai 565ed450aa Merge pull request #2310 from Sonicadvance1/aarch64_move_to_switch
Arm64: Use switch statement for op handlers instead of jump table
2023-01-18 20:26:16 +00:00
Mai 90bcb8c70b Merge pull request #2309 from Sonicadvance1/remove_header
Emitter: Remove unused header
2023-01-18 20:25:07 +00:00
Ryan Houdek bbf9198cba Merge pull request #2324 from Sonicadvance1/jemalloc_disable_16k
External: Update JEMalloc to disable 16k pages
2023-01-17 12:56:23 -08:00
Ryan Houdek 9c93c6ffcd Merge pull request #2317 from Sonicadvance1/fix_spill_register
Arm64: Fix SpillRegister C&P error
2023-01-17 12:56:14 -08:00
Ryan Houdek 8974509c52 Merge pull request #2343 from Sonicadvance1/fexinterpreter_heartburn
FEXLoader: Build FEXInterpreter and FEXLoader independently
2023-01-17 02:25:54 -08:00
Ryan Houdek abc5aa6aa0 FEXLoader: Build FEXInterpreter and FEXLoader independently
This is causing some heartburn with the hardlink.

- Removes some termux cmake list hacking.
- Removes the need to do post-install packaging fixups when hardlinks get dropped.
- Removes a custom uninstall target that was necessary before.
2023-01-16 13:08:43 -08:00
Ryan Houdek a668c34dec unittests: Adds tests for two new subclasses 2023-01-15 20:16:10 -08:00
Ryan Houdek 0ef8574a56 ARMEmitter: Adds two instruction classes 2023-01-15 20:16:10 -08:00
Ryan Houdek e66ad12fa8 unittests: Adds unittests for added ops 2023-01-15 20:16:10 -08:00
Ryan Houdek f1e1eaa8e5 ArmEmitter: Adds four missing ASIMD Shift by Imm ops 2023-01-15 20:16:10 -08:00
Ryan Houdek f614fc6fac Merge pull request #2338 from Sonicadvance1/optimize_cpuid
CPUID: Optimize initialization
2023-01-14 14:15:35 -08:00
Ryan Houdek d9a1bb9c35 CPUID: Optimize initialization
Map lookup was quite expensive, switched over to three small vectors
that are constexpr instead.

Some file querying and parsing was fairly slow as well. Optimized to
make that CPU time to go away.

This improves initialization time of CPUIDEmu by 33%
2023-01-14 13:37:26 -08:00
Ryan Houdek f36bbf0f59 FileLoading: Add a very quick fixed size small file loading helper
If we have a fixed file to read, we can read it in three syscalls.
fstream is...weirdly slow in the other implementation.

Theoretically in the future this can be improved to a single syscall if
the `readfile` syscall ends up in upstream Linux.
2023-01-14 13:33:32 -08:00
Ryan Houdek f5e97f3542 Merge pull request #2333 from Sonicadvance1/use_rng_syscall
ELFCodeLoader: Don't use std::random_device for RNG
2023-01-14 12:27:05 -08:00
Ryan Houdek 7664359410 Merge pull request #2326 from Sonicadvance1/debug_cookie
MContext: Insert a stack cookie with assertions enabled
2023-01-14 12:13:54 -08:00
Ryan Houdek df8704215b Merge pull request #2328 from Sonicadvance1/update_install_script_links
Scripts: Update InstallFEX.py rootfs links
2023-01-14 12:13:40 -08:00
Ryan Houdek 34e1ba6129 Merge pull request #2318 from Sonicadvance1/fix_jit_symbol_crash
JitSymbols: Fixes a crash that can occur
2023-01-14 12:07:22 -08:00
Ryan Houdek fcddf86352 ELFCodeLoader: Don't use std::random_device for RNG
Fixes #2095

std::random_device can fail to find a random source and throw an assert
during initialization.

The constructor allows you to provide a token string to select an
explicit random source, but this is c++ library specific and will still
assert if the source isn't found. Also specific tokens are very much
target and library specific, so it is unsafe to use.

In the libstdc++ case, the default implementation will try to open
`/dev/urandom` which might not be available on all targets.

Instead of relying on this C++ object, use the `getrandom` syscall
directly to generate our RNG used in the ELFCodeLoader.
Resolves any sort of asserting case, blocks on RNG generation, and is
guaranteed to be available since this syscall has been available since a
very old kernel version.
2023-01-14 12:06:12 -08:00
Ryan Houdek d69afaf925 MContext: Insert a stack cookie with assertions enabled
Pulled from #2176.
Ensures that when we are handling signals we are actually restoring a
stack state that is what we expect..

While this could randomly intersect with other stack data, it is highly
unlikely and will still capture incorrect stack frames otherwise.

Keeps it out of release build to ensure we aren't sticking random data
in the stack when it wouldn't have even been checked.
2023-01-14 11:59:10 -08:00
Ryan Houdek dc5e739628 JitSymbols: Fixes a crash that can occur
When a process is in the process of forking and getting ready for
execve, it is common practice to do a `close_range` or close loop to
close all file descriptors before the execve.

This is a security/sanitization feature to ensure that FDs aren't leaked
to the child process. While it is more reliable to have these FDs opened
with O_CLOEXEC, people get it wrong all the time so this feature has
been put in place. Both python and glibc wrappers for launching
applications do this.

The problem with this for FEX is that we were using a FILE handle for
emitting JIT symbols to the perf file. When the underlying FD is ripped
out from under the FILE handle, it throws an assert that we can't
recover from.

Switching to a raw FD and checking to ensure the FD is still open on
writes means that we can safely stop JIT symbol logging when a process
is closing FDs under us.

Fixes a crash in Steam early startup where a python script is run for
checking if packages are installed.
2023-01-14 11:56:12 -08:00
Ryan Houdek b57a8ac086 IR: Update tests for new GPR offsets 2023-01-13 19:35:12 -08:00
Ryan Houdek 7696641b4c Frontend: Add some switch statement hints for most command paths. 2023-01-13 19:23:31 -08:00
Ryan Houdek b9fedfff7c Frontend: Optimize that Dst RAX/RCX and REX in byte is mutually exclusive. 2023-01-13 19:23:31 -08:00
Ryan Houdek 30dc92cdfd Frontend: Set Is8Bit{Dest,Src} immediately rather than query flags after the fact. 2023-01-13 19:23:31 -08:00
Ryan Houdek ed0d46e51a Frontend: Optimize NormalOp to most likely pick a NormalOp. It's the most common. 2023-01-13 19:23:30 -08:00
Ryan Houdek a065849e39 Frontend: Only add contained code pages at the end 2023-01-13 18:27:27 -08:00
Ryan Houdek c61ce1fe11 Frontend: Remove unnecessary checks 2023-01-13 18:26:51 -08:00
Ryan Houdek c98816b350 OpDispatcher: Make ReadByte check only happen with assertions enabled. 2023-01-13 18:26:14 -08:00
Ryan Houdek 2866dda73f Frontend: Optimize MapModRMToReg and MapVEXToReg 2023-01-13 18:24:36 -08:00
Ryan Houdek 47bd119c9c OpDispatcher: Fix MOVSeg from previous reordering 2023-01-13 18:24:20 -08:00
Ryan Houdek a5cc2536cb X86Enums: Sort GPRs by their encoding order. 2023-01-13 17:54:56 -08:00
Ryan Houdek 130dcb1704 HarnessHelper: Ensure placement of greg offsets 2023-01-13 17:54:18 -08:00
Ryan Houdek 777390c62a Github: Add ARM emitter tests to CI 2023-01-12 13:55:48 -08:00
Ryan Houdek fb3c8b3491 CMake: Add an option for compiling vixl disassembler 2023-01-12 13:55:00 -08:00
Ryan Houdek c229f906f8 External: Update vixl 2023-01-12 13:54:20 -08:00
Ryan Houdek d787a38744 ArmEmitter: Adds three more classes of ASIMD instructions
Adds three classes:
- Advanced SIMD three same (FP16)
- Advanced SIMD two-register miscellaneous (FP16)
- Advanced SIMD three-register extension

A handful of the three-register extension unit tests are disabled
because the vixl disassembler doesn't support them.

Only six more classes of ASIMD operations remaining once this is merged.
2023-01-12 13:38:48 -08:00
Ryan Houdek b2f7f526f8 Arm64: Fixes incorrect operation for CacheLineClear
CIVAU does Clean+Invalidate to `Point Of Unification`
CIVAC does Clean+Invalidate to `Point of Coherency`

`Point of Unification` means to L2/L3, so unification of core
visibility.

`Point of Coherency` means SLC/RAM, All cores, DNA engines, etc must be
coherent.
2023-01-11 19:53:33 -08:00
Ryan Houdek ab512b6ffa Scripts: Update InstallFEX.py rootfs links
This was never updated for 22.10, Updated list.
2023-01-10 23:44:34 -08:00
Ryan Houdek 1521e0a248 Merge pull request #2325 from cobalt2727/patch-1
fix tgkill
2023-01-10 20:07:50 -08:00
cobalt2727 0f131c4c1a fix tgkill
long time no see!
2023-01-10 22:21:25 -05:00
Ryan Houdek 716cafe6f7 External: Update JEMalloc to disable 16k pages
When tinkering I had enabled 16k page support in jemalloc.
This broke pressure-vessel/Proton executing. Back it back down to 4k page size
to fix this.

We'll need to come back to this to see if enabling this can be done
without breaking these projects.
2023-01-09 18:10:46 -08:00
Ryan Houdek bd55ed51b0 OpDispatcher: Moves a few missing XMM loadstores to helper usage
These were missed initially, these need to all be using the helper for
future optimizations.
2023-01-09 08:23:26 -08:00
Ryan Houdek 6d912be31e OpDispatcher: Moves a few missing GPR loadstores to helper usage
These were missed initially, these need to all be using the helper for
future optimizations.
2023-01-09 08:23:26 -08:00
Ryan Houdek 632add660c Merge pull request #2321 from CallumDev/f64-fprem-fix
Fix FPREM flags calculation in F64
2023-01-09 04:06:10 -08:00
CallumDev 806587d6ae Fix FPREM flags calculation in F64 2023-01-09 22:21:23 +10:30
Ryan Houdek 5c98db5f47 IR: Remove HasDest member
Split off from #2243 to remove each member individually.

IR ops are hardcoded by operation to have a destination or not.
No need to have each operation have a boolean for determining if the
operation has a destination or not.

The number of places things need to know if the operation has
destination or not is better served by using a lookup instead.
2023-01-08 17:57:47 -08:00
Ryan Houdek 4daf2f0793 Jit64: Fixes incorrect sign extension
We were accidentally zero extending.
2023-01-08 13:29:38 -08:00
Ryan Houdek 676cf59198 Jit64: Fixes incorrect sign extension
We were accidentally zero extending.
2023-01-08 13:28:17 -08:00
Ryan Houdek 5aacdd744c Arm64: Fixes incorrect sign extension
We were accidentally zero extending.
2023-01-08 13:25:25 -08:00
Ryan Houdek c3c68afc3c Arm64: Fixes incorrect sign extension
We were accidentally zero extending.
2023-01-08 13:23:40 -08:00
Ryan Houdek c7262120a6 Arm64: Fix SpillRegister C&P error
Was using the wrong sized registers in spill which was breaking Steam.
Oops.
2023-01-08 12:47:02 -08:00
Ryan Houdek f156615a3a unittests: Adds unittests to ensure FICOM works
Both x80 and x64 variants.
2023-01-08 11:04:28 -08:00
Ryan Houdek 6977ae6b79 X87_F64: Fixes FICOM
This was not correctly converting both 32-bit and 16-bit integers over
to 64-bit double.
2023-01-08 11:02:52 -08:00
Ryan Houdek 555d2e5b0b unittests: Adds negative integer x87 tests
All of these operations were only testing positive integers which is why
they didn't show 16-bit failures.

Adds a bunch of negative tests to each ones now that #2314 is merged,
which would have caught them.
2023-01-08 10:44:46 -08:00
Ryan Houdek c2325e1772 Merge pull request #2314 from CallumDev/f64-integer-fix
F64: Fix integer immediates for add,mul,div,sub
2023-01-08 10:42:21 -08:00
Ryan Houdek 9acb513393 Merge pull request #2313 from Sonicadvance1/fix_large_spills
Arm64: Fixes large offset spill slots
2023-01-08 08:47:14 -08:00
Ryan Houdek dfc3297192 Merge pull request #2311 from Sonicadvance1/optimize_struct_layout
X86Tables: Optimize struct layouts
2023-01-08 08:33:32 -08:00
Ryan Houdek 9322e55a3f Merge pull request #2312 from Sonicadvance1/update_jemalloc
Externals: Update jemalloc to 5.3.0
2023-01-08 08:33:19 -08:00
CallumDev 9373fa0c06 F64: Fix integer immediates for add,mul,div,sub 2023-01-09 01:29:17 +10:30
Ryan Houdek ca9400ba52 Arm64: Fixes large offset spill slots
Found an application today (hashtree tests) that causes us to spill a
large amount of values on to the stack.

We were encoding larger offsets than what unsigned offset load and store
can handle.

If the offset is too large for the loadstore, use a temporary to put the
offset in to first.
2023-01-07 18:03:24 -08:00
Ryan Houdek 2a6937fe59 Core: Fixes uninitialized ParentThread variable
Can cause crashes by not zero initializing. ParentThread isn't
initialized in the TestHarnessRunner when an unsupported test is ran.
2023-01-07 12:09:09 -08:00
Ryan Houdek 138752d512 Externals: Update jemalloc to 5.3.0
Apparently this has some tcache fixes and performance improvements
2023-01-07 11:58:48 -08:00
Ryan Houdek 6b63f9fa89 X86Tables: Optimize struct layouts
We were leaving some ugly holes in a couple of these structs.
Reorder them so they are packed more efficiently.
2023-01-06 18:48:23 -08:00
Ryan Houdek 9a748c020d Arm64: Use switch statement for op handlers instead of jump table
Removes some startup time where we are copying nearly a page worth of
16byte vtable pointers at startup.

Also allows the compiler to choose to inline functions if it wants to.
2023-01-06 17:44:28 -08:00
Ryan Houdek 842e36e9b2 Emitter: Remove unused header 2023-01-06 10:34:41 -08:00
165 changed files with 7321 additions and 1718 deletions

No files matched your search

+11
View File
@@ -166,6 +166,17 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_APITests.log || true
- name: ARMEmitter tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target emitter_tests
- name: ARMEmitter 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_ARMEmitterTests.log || true
- name: FEXLinuxTests
working-directory: ${{runner.workspace}}/build
shell: bash
+6
View File
@@ -30,6 +30,7 @@ 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)
option(ENABLE_VIXL_DISASSEMBLER "Enables debug disassembler output with VIXL" FALSE)
option(COMPILE_VIXL_DISASSEMBLER "Compiles the vixl disassembler in to vixl" FALSE)
option(ENABLE_FEXCORE_PROFILER "Enables use of the FEXCore timeline profiling capabilities" FALSE)
set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend you want to use for the FEXCore profiler")
@@ -197,6 +198,11 @@ set (CMAKE_LINKER_FLAGS_RELEASE "${CMAKE_LINKER_FLAGS_RELEASE} -fomit-frame-poin
include_directories(External/robin-map/include/)
if (BUILD_TESTS)
# Enable vixl disassembler if tests are enabled.
set(COMPILE_VIXL_DISASSEMBLER TRUE)
endif()
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
+38 -11
View File
@@ -281,9 +281,7 @@ def print_ir_structs(defines):
output_file.write("\tvoid* Data[0];\n")
output_file.write("\tIROps Op;\n\n")
output_file.write("\tuint8_t Size;\n")
output_file.write("\tuint8_t NumArgs;\n")
output_file.write("\tuint8_t ElementSize : 7;\n")
output_file.write("\tbool HasDest : 1;\n")
output_file.write("\tuint8_t ElementSize;\n")
output_file.write("\ttemplate<typename T>\n")
output_file.write("\tT const* C() const { return reinterpret_cast<T const*>(Data); }\n")
@@ -358,8 +356,10 @@ def print_ir_sizes():
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] std::string_view const& GetName(IROps Op);\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] uint8_t GetArgs(IROps Op);\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] uint8_t GetRAArgs(IROps Op);\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] bool HasSideEffects(IROps Op);\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] bool GetHasDest(IROps Op);\n")
output_file.write("#undef IROP_SIZES\n")
output_file.write("#endif\n\n")
@@ -417,7 +417,7 @@ def print_ir_getname():
def print_ir_getraargs():
output_file.write("#ifdef IROP_GETRAARGS_IMPL\n")
output_file.write("constexpr std::array<uint8_t, OP_LAST + 1> IRArgs = {\n")
output_file.write("constexpr std::array<uint8_t, OP_LAST + 1> IRRAArgs = {\n")
for op in IROps:
SSAArgs = op.SSAArgNum
@@ -430,6 +430,18 @@ def print_ir_getraargs():
output_file.write("};\n\n")
output_file.write("constexpr std::array<uint8_t, OP_LAST + 1> IRArgs = {\n")
for op in IROps:
SSAArgs = op.SSAArgNum
output_file.write("\t{},\n".format(SSAArgs))
output_file.write("};\n\n")
output_file.write("uint8_t GetRAArgs(IROps Op) {\n")
output_file.write(" return IRRAArgs[Op];\n")
output_file.write("}\n")
output_file.write("uint8_t GetArgs(IROps Op) {\n")
output_file.write(" return IRArgs[Op];\n")
output_file.write("}\n")
@@ -453,6 +465,25 @@ def print_ir_hassideeffects():
output_file.write("#undef IROP_HASSIDEEFFECTS_IMPL\n")
output_file.write("#endif\n\n")
def print_ir_gethasdest():
output_file.write("#ifdef IROP_GETHASDEST_IMPL\n")
output_file.write("constexpr std::array<bool, OP_LAST + 1> IRDest = {\n")
for op in IROps:
if op.HasDest:
output_file.write("\ttrue,\n")
else:
output_file.write("\tfalse,\n")
output_file.write("};\n\n")
output_file.write("bool GetHasDest(IROps Op) {\n")
output_file.write(" return IRDest[Op];\n")
output_file.write("}\n")
output_file.write("#undef IROP_GETHASDEST_IMPL\n")
output_file.write("#endif\n\n")
# Print out IR argument printing
def print_ir_arg_printer():
output_file.write("#ifdef IROP_ARGPRINTER_HELPER\n")
@@ -547,13 +578,13 @@ def print_ir_allocator_helpers():
output_file.write("\tuint8_t GetOpElements(const OrderedNode *Op) const {\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
output_file.write("\t\tLOGMAN_THROW_A_FMT(HeaderOp->HasDest, \"Op {} has no dest\\n\", GetName(HeaderOp->Op));\n")
output_file.write("\t\tLOGMAN_THROW_A_FMT(OpHasDest(Op), \"Op {} has no dest\\n\", GetName(HeaderOp->Op));\n")
output_file.write("\t\treturn HeaderOp->Size / HeaderOp->ElementSize;\n")
output_file.write("\t}\n\n")
output_file.write("\tbool OpHasDest(const OrderedNode *Op) const {\n")
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
output_file.write("\t\treturn HeaderOp->HasDest;\n")
output_file.write("\t\treturn GetHasDest(HeaderOp->Op);\n")
output_file.write("\t}\n\n")
output_file.write("\tIROps GetOpType(const OrderedNode *Op) const {\n")
@@ -631,8 +662,6 @@ def print_ir_allocator_helpers():
output_file.write("\t\tOp.first->Header.Size = InferSize;\n")
output_file.write("\t\tOp.first->Header.NumArgs = {};\n".format(op.SSAArgNum))
# Some ops without a destination still need an operating size
# Effectively reusing the destination size value for operation size
if op.DestSize != None:
@@ -643,9 +672,6 @@ def print_ir_allocator_helpers():
else:
output_file.write("\t\tOp.first->Header.ElementSize = Op.first->Header.Size / ({});\n".format(op.NumElements))
if (op.HasDest):
output_file.write("\t\tOp.first->Header.HasDest = true;\n")
# Insert validation here
if op.EmitValidation != None:
output_file.write("\t\t#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED\n")
@@ -733,6 +759,7 @@ print_ir_reg_classes()
print_ir_getname()
print_ir_getraargs()
print_ir_hassideeffects()
print_ir_gethasdest()
print_ir_arg_printer()
print_ir_allocator_helpers()
print_ir_parser_switch_helper()
+42 -20
View File
@@ -1,64 +1,86 @@
#include "Common/JitSymbols.h"
#include <fcntl.h>
#include <string>
#include <unistd.h>
#include <fmt/format.h>
namespace FEXCore {
JITSymbols::JITSymbols() : fp{nullptr, std::fclose} {
JITSymbols::JITSymbols() {
}
JITSymbols::~JITSymbols() = default;
void JITSymbols::InitFile() {
const auto PerfMap = fmt::format("/tmp/perf-{}.map", getpid());
fp.reset(fopen(PerfMap.c_str(), "wb"));
if (fp) {
// Disable buffering on this file
setvbuf(fp.get(), nullptr, _IONBF, 0);
JITSymbols::~JITSymbols() {
if (fd != -1) {
close(fd);
}
}
void JITSymbols::InitFile() {
// We can't use FILE here since we must be robust against forking processes closing our FD from under us.
const auto PerfMap = fmt::format("/tmp/perf-{}.map", getpid());
fd = open(PerfMap.c_str(), O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
}
void JITSymbols::Register(const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
if (!fp) return;
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
fmt::print(fp.get(), "{} {:x} JIT_0x{:x}_{}\n", HostAddr, CodeSize, GuestAddr, HostAddr);
const auto Buffer = fmt::format("{} {:x} JIT_0x{:x}_{}\n", HostAddr, CodeSize, GuestAddr, HostAddr);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
if (!fp) return;
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
fmt::print(fp.get(), "{} {:x} {}_{}\n", HostAddr, CodeSize, Name, HostAddr);
const auto Buffer = fmt::format("{} {:x} {}_{}\n", HostAddr, CodeSize, Name, HostAddr);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset) {
if (!fp) return;
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
fmt::print(fp.get(), "{} {:x} {}+0x{:x} ({})\n", HostAddr, CodeSize, Name, Offset, HostAddr);
const auto Buffer = fmt::format("{} {:x} {}+0x{:x} ({})\n", HostAddr, CodeSize, Name, Offset, HostAddr);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::RegisterNamedRegion(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
if (!fp) return;
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
fmt::print(fp.get(), "{} {:x} {}\n", HostAddr, CodeSize, Name);
const auto Buffer = fmt::format("{} {:x} {}\n", HostAddr, CodeSize, Name);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::RegisterJITSpace(const void *HostAddr, uint32_t CodeSize) {
if (!fp) return;
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
fmt::print(fp.get(), "{} {:x} FEXJIT\n", HostAddr, CodeSize);
const auto Buffer = fmt::format("{} {:x} FEXJIT\n", HostAddr, CodeSize);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
} // namespace FEXCore
+1 -3
View File
@@ -19,8 +19,6 @@ public:
void RegisterJITSpace(const void *HostAddr, uint32_t CodeSize);
private:
using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>;
FILEPtr fp;
int fd{-1};
};
}
+1 -1
View File
@@ -123,7 +123,7 @@ namespace FEXCore::Context {
FEXCore::HostFeatures HostFeatures;
std::mutex ThreadCreationMutex;
FEXCore::Core::InternalThreadState* ParentThread;
FEXCore::Core::InternalThreadState* ParentThread{};
std::vector<FEXCore::Core::InternalThreadState*> Threads;
std::atomic_bool CoreShuttingDown{false};
bool NeedToCheckXID{true};
@@ -10,6 +10,16 @@
* FEX-Emu ALU operations usually have a 32-bit or 64-bit operating size encoded in the IR operation,
* This allows FEX to use a single helper function which decodes to both handlers.
*/
private:
static bool IsADRRange(int64_t Imm) {
return Imm >= -1048576 && Imm <= 1048575;
}
static bool IsADRPRange(int64_t Imm) {
return Imm >= -4294967296 && Imm <= 4294963200;
}
static bool IsADRPAligned(int64_t Imm) {
return (Imm & 0xFFF) == 0;
}
public:
// PC relative
void adr(FEXCore::ARMEmitter::Register rd, uint32_t Imm) {
@@ -19,7 +29,7 @@ public:
void adr(FEXCore::ARMEmitter::Register rd, BackwardLabel const* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575, "Unscaled offset too large");
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
constexpr uint32_t Op = 0b0001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, Imm);
@@ -46,7 +56,7 @@ public:
void adrp(FEXCore::ARMEmitter::Register rd, BackwardLabel const* Label) {
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>() & ~0xFFFLL);
LOGMAN_THROW_A_FMT(Imm >= -4294967296 && Imm <= 4294963200 && (Imm & 0xFFF) == 0, "Unscaled offset too large");
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
constexpr uint32_t Op = 0b1001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, Imm);
@@ -66,6 +76,49 @@ public:
}
}
void LongAddressGen(FEXCore::ARMEmitter::Register rd, BackwardLabel const* Label) {
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>());
if (IsADRRange(Imm)) {
// If the range is in ADR range then we can just use ADR.
adr(rd, Label);
}
else if (IsADRPRange(Imm)) {
int64_t ADRPImm = (reinterpret_cast<int64_t>(Label->Location) & ~0xFFFLL)
- (GetCursorAddress<int64_t>() & ~0xFFFLL);
// If the range is in the ADRP range then we can use ADRP.
bool NeedsOffset = !IsADRPAligned(reinterpret_cast<uint64_t>(Label->Location));
uint64_t AlignedOffset = reinterpret_cast<uint64_t>(Label->Location) & 0xFFFULL;
// First emit ADRP
adrp(rd, ADRPImm >> 12);
if (NeedsOffset) {
// Now even an add
add(ARMEmitter::Size::i64Bit, rd, rd, AlignedOffset);
}
}
else {
LOGMAN_MSG_A_FMT("Unscaled offset too large");
FEX_UNREACHABLE;
}
}
void LongAddressGen(FEXCore::ARMEmitter::Register rd, ForwardLabel* Label) {
Label->Insts.emplace_back(ForwardLabel::Instructions{ .Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::Instructions::InstType::LONG_ADDRESS_GEN });
// Emit a register index and a nop. These will be backpatched.
dc32(rd.Idx());
nop();
}
void LongAddressGen(FEXCore::ARMEmitter::Register rd, BiDirectionalLabel *Label) {
if (Label->Backward.Location) {
LongAddressGen(rd, &Label->Backward);
}
else {
LongAddressGen(rd, &Label->Forward);
}
}
// Add/subtract immediate
void add(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t Imm, bool LSL12 = false) {
constexpr uint32_t Op = 0b0001'0001'0 << 23;
@@ -584,10 +637,30 @@ public:
constexpr uint32_t Op = 0b0111'1010'000U << 21;
DataProcessing_Extended_Reg(Op, s, rd, rn, rm, FEXCore::ARMEmitter::ExtendedType::UXTB, 0);
}
// Rotate right into flags
// TODO
void rmif(XRegister rn, uint32_t shift, uint32_t mask) {
LOGMAN_THROW_AA_FMT(shift <= 63, "Shift must be within 0-63. Shift: {}", shift);
LOGMAN_THROW_AA_FMT(mask <= 15, "Mask must be within 0-15. Mask: {}", mask);
uint32_t Op = 0b1011'1010'0000'0000'0000'0100'0000'0000;
Op |= rn.Idx() << 5;
Op |= shift << 15;
Op |= mask;
dc32(Op);
}
// Evaluate into flags
// TODO
void setf8(WRegister rn) {
constexpr uint32_t Op = 0b0011'1010'0000'0000'0000'1000'0000'1101;
EvaluateIntoFlags(Op, 0, rn);
}
void setf16(WRegister rn) {
constexpr uint32_t Op = 0b0011'1010'0000'0000'0000'1000'0000'1101;
EvaluateIntoFlags(Op, 1, rn);
}
// Conditional compare - register
void ccmn(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm, FEXCore::ARMEmitter::StatusFlags flags, FEXCore::ARMEmitter::Condition Cond) {
constexpr uint32_t Op = 0b0011'1010'010 << 21;
@@ -909,4 +982,11 @@ private:
dc32(Instr);
}
void EvaluateIntoFlags(uint32_t op, uint32_t size, WRegister rn) {
uint32_t Instr = op;
Instr |= size << 14;
Instr |= rn.Idx() << 5;
dc32(Instr);
}
File diff suppressed because it is too large. Load diff
@@ -87,6 +87,11 @@ namespace FEXCore::ARMEmitter {
return Size;
}
template<typename T>
size_t GetCursorOffsetFromAddress(const T* Address) const {
return static_cast<size_t>(reinterpret_cast<const uint8_t*>(Address) - BufferBase);
}
protected:
void ResetBuffer() {
@@ -519,6 +519,7 @@ namespace FEXCore::ARMEmitter {
BC,
TEST_BRANCH,
RELATIVE_LOAD,
LONG_ADDRESS_GEN,
};
uint8_t *Location{};
InstType Type;
@@ -535,6 +536,14 @@ namespace FEXCore::ARMEmitter {
ForwardLabel Forward;
};
// Some FCMA ASIMD instructions support a rotation argument.
enum class Rotation : uint32_t {
ROTATE_0 = 0b00,
ROTATE_90 = 0b01,
ROTATE_180 = 0b10,
ROTATE_270 = 0b11,
};
// This is an emitter that is designed around the smallest code bloat as possible.
// Eschewing most developer convenience in order to keep code as small as possible.
@@ -571,7 +580,7 @@ namespace FEXCore::ARMEmitter {
case ForwardLabel::Instructions::InstType::ADR: {
uint32_t *Instruction = reinterpret_cast<uint32_t*>(Inst.Location);
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575, "Unscaled offset too large");
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
uint32_t Inst = *Instruction & ~InstMask;
@@ -583,7 +592,7 @@ namespace FEXCore::ARMEmitter {
case ForwardLabel::Instructions::InstType::ADRP: {
uint32_t *Instruction = reinterpret_cast<uint32_t*>(Inst.Location);
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
LOGMAN_THROW_A_FMT(Imm >= -4294967296 && Imm <= 4294963200 && (Imm & 0xFFF) == 0, "Unscaled offset too large");
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
Imm >>= 12;
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
@@ -634,6 +643,47 @@ namespace FEXCore::ARMEmitter {
*Instruction = Inst;
break;
}
case ForwardLabel::Instructions::InstType::LONG_ADDRESS_GEN: {
uint32_t *Instructions = reinterpret_cast<uint32_t*>(Inst.Location);
int64_t ImmInstOne = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(&Instructions[0]);
int64_t ImmInstTwo = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(&Instructions[1]);
auto OriginalOffset = GetCursorOffset();
auto InstOffset = GetCursorOffsetFromAddress(Instructions);
SetCursorOffset(InstOffset);
// We encoded the destination register in to the first instruction space.
// Read it back.
ARMEmitter::Register DestReg(Instructions[0]);
if (IsADRRange(ImmInstTwo)) {
// If within ADR range from the second instruction, then we can emit NOP+ADR
nop();
adr(DestReg, static_cast<uint32_t>(ImmInstTwo) & 0x7FFF);
}
else if (IsADRPRange(ImmInstOne)) {
// If within ADRP range from the first instruction, then we are /definitely/ in range for the second instruction.
// First check if we are in non-offset range for second instruction.
if (IsADRPAligned(reinterpret_cast<uint64_t>(CurrentAddress))) {
// We can emit nop + adrp
nop();
adrp(DestReg, static_cast<uint32_t>(ImmInstTwo >> 12) & 0x7FFF);
}
else {
// Not aligned, need adrp + add
adrp(DestReg, static_cast<uint32_t>(ImmInstOne >> 12) & 0x7FFF);
add(ARMEmitter::Size::i64Bit, DestReg, DestReg, ImmInstOne & 0xFFF);
}
}
else {
LOGMAN_MSG_A_FMT("Unscaled offset is too large");
FEX_UNREACHABLE;
}
SetCursorOffset(OriginalOffset);
break;
}
default: LOGMAN_MSG_A_FMT("Unexpected inst type in label fixup");
}
}
@@ -1,6 +1,5 @@
#pragma once
#include <FEXCore/Utils/EnumUtils.h>
#include <compare>
#include <cstdint>
namespace FEXCore::ARMEmitter {
@@ -66,9 +66,87 @@ public:
SVESel(Op, size, zd, pv, zn, zd);
}
// TODO: HISTCNT
// TODO: FCMLA
// TODO: FCADD
void histcnt(SubRegSize size, ZRegister zd, PRegisterZero pv, ZRegister zn, ZRegister zm) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i32Bit || size == SubRegSize::i64Bit, "SubRegSize must be 32-bit or 64-bit");
LOGMAN_THROW_AA_FMT(pv <= PReg::p7, "histcnt can only use p0 to p7");
uint32_t Op = 0b0100'0101'0010'0000'1100'0000'0000'0000;
Op |= FEXCore::ToUnderlying(size) << 22;
Op |= zm.Idx() << 16;
Op |= pv.Idx() << 10;
Op |= zn.Idx() << 5;
Op |= zd.Idx();
dc32(Op);
}
void histseg(ZRegister zd, ZRegister zn, ZRegister zm) {
uint32_t Op = 0b0100'0101'0010'0000'1010'0000'0000'0000;
Op |= zm.Idx() << 16;
Op |= zn.Idx() << 5;
Op |= zd.Idx();
dc32(Op);
}
void fcmla(SubRegSize size, ZRegister zda, ZRegister zn, ZRegister zm, uint32_t index, Rotation rot) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit,
"SubRegSize must be 16-bit or 32-bit");
const auto IsHalfPrecision = size == SubRegSize::i16Bit;
if (IsHalfPrecision) {
LOGMAN_THROW_AA_FMT(index <= 3, "Index for half-precision fcmla must be within 0-3. Index={}", index);
LOGMAN_THROW_AA_FMT(zm.Idx() <= 7, "zm must be within z0-z7. zm=z{}", zm.Idx());
} else {
LOGMAN_THROW_AA_FMT(index <= 1, "Index for single-precision fcmla must be within 0-1. Index={}", index);
LOGMAN_THROW_AA_FMT(zm.Idx() <= 15, "zm must be within z0-z15. zm=z{}", zm.Idx());
}
uint32_t Op = 0b0110'0100'1010'0000'0001'0000'0000'0000;
Op |= (IsHalfPrecision ? 0 : 1) << 22;
Op |= index << (19 + int(!IsHalfPrecision));
Op |= zm.Idx() << 16;
Op |= FEXCore::ToUnderlying(rot) << 10;
Op |= zn.Idx() << 5;
Op |= zda.Idx();
dc32(Op);
}
void fcmla(SubRegSize size, ZRegister zda, PRegisterMerge pv, ZRegister zn, ZRegister zm, Rotation rot) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit || size == SubRegSize::i64Bit,
"SubRegSize must be 16-bit, 32-bit, or 64-bit");
LOGMAN_THROW_AA_FMT(pv <= PReg::p7, "fcmla can only use p0 to p7");
uint32_t Op = 0b0110'0100'0000'0000'0000'0000'0000'0000;
Op |= FEXCore::ToUnderlying(size) << 22;
Op |= zm.Idx() << 16;
Op |= FEXCore::ToUnderlying(rot) << 13;
Op |= pv.Idx() << 10;
Op |= zn.Idx() << 5;
Op |= zda.Idx();
dc32(Op);
}
void fcadd(SubRegSize size, ZRegister zd, PRegisterMerge pv, ZRegister zn, ZRegister zm, Rotation rot) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit || size == SubRegSize::i64Bit,
"SubRegSize must be 16-bit, 32-bit, or 64-bit");
LOGMAN_THROW_AA_FMT(pv <= PReg::p7, "fcadd can only use p0 to p7");
LOGMAN_THROW_AA_FMT(rot == Rotation::ROTATE_90 || rot == Rotation::ROTATE_270,
"fcadd rotation may only be 90 or 270 degrees");
LOGMAN_THROW_AA_FMT(zd.Idx() == zn.Idx(), "fcadd zd and zn must be the same register");
const uint32_t ConvertedRotation = rot == Rotation::ROTATE_90 ? 0 : 1;
uint32_t Op = 0b0110'0100'0000'0000'1000'0000'0000'0000;
Op |= FEXCore::ToUnderlying(size) << 22;
Op |= ConvertedRotation << 16;
Op |= pv.Idx() << 10;
Op |= zm.Idx() << 5;
Op |= zd.Idx();
dc32(Op);
}
// SVE integer add/subtract vectors (unpredicated)
void add(FEXCore::ARMEmitter::SubRegSize size, FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::ZRegister zn, FEXCore::ARMEmitter::ZRegister zm) {
@@ -314,12 +392,21 @@ public:
SVEPredicateLogical(Op, 1, 1, 1, 1, pm, pg, pn, pd);
}
// XXX:
// SVE broadcast predicate element
// XXX:
// SVE integer clamp
// XXX:
// SVE2 character match
// XXX:
void match(SubRegSize size, PRegister pd, PRegisterZero pg, ZRegister zn, ZRegister zm) {
constexpr uint32_t Op = 0b0100'0101'0010'0000'1000'0000'0000'0000;
SVECharacterMatch(Op, 0, size, pd, pg, zn, zm);
}
void nmatch(SubRegSize size, PRegister pd, PRegisterZero pg, ZRegister zn, ZRegister zm) {
constexpr uint32_t Op = 0b0100'0101'0010'0000'1000'0000'0000'0000;
SVECharacterMatch(Op, 1, size, pd, pg, zn, zm);
}
// SVE floating-point convert precision odd elements
void fcvtxnt(FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::PRegisterMerge pg, FEXCore::ARMEmitter::ZRegister zn) {
constexpr uint32_t Op = 0b0110'0100'0000'1000'101 << 13;
@@ -486,7 +573,26 @@ public:
}
// SVE floating-point recursive reduction
// XXX:
void faddv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
SVEFPRecursiveReduction(Op, 0b000, size, vd, pg, zn);
}
void fmaxnmv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
SVEFPRecursiveReduction(Op, 0b100, size, vd, pg, zn);
}
void fminnmv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
SVEFPRecursiveReduction(Op, 0b101, size, vd, pg, zn);
}
void fmaxv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
SVEFPRecursiveReduction(Op, 0b110, size, vd, pg, zn);
}
void fminv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
SVEFPRecursiveReduction(Op, 0b111, size, vd, pg, zn);
}
// SVE integer Multiply-Add - Predicated
// SVE integer multiply-accumulate writing addend (predicated)
@@ -496,7 +602,19 @@ public:
// SVE Integer Binary Arithmetic - Predicated
// SVE integer add/subtract vectors (predicated)
// XXX:
void add(SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
constexpr uint32_t Op = 0b0000'0100'0000'0000'0000'0000'0000'0000;
SVEAddSubVectorsPredicated(Op, 0b000, size, zd, pg, zn, zm);
}
void sub(SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
constexpr uint32_t Op = 0b0000'0100'0000'0000'0000'0000'0000'0000;
SVEAddSubVectorsPredicated(Op, 0b001, size, zd, pg, zn, zm);
}
void subr(SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
constexpr uint32_t Op = 0b0000'0100'0000'0000'0000'0000'0000'0000;
SVEAddSubVectorsPredicated(Op, 0b011, size, zd, pg, zn, zm);
}
// SVE integer min/max/difference (predicated)
void smax(FEXCore::ARMEmitter::SubRegSize size, FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::PRegisterMerge pg, FEXCore::ARMEmitter::ZRegister zdn, FEXCore::ARMEmitter::ZRegister zm) {
LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i128Bit, "Can't use 128-bit size");
@@ -3827,6 +3945,48 @@ private:
dc32(Instr);
}
void SVECharacterMatch(uint32_t op, uint32_t opc, SubRegSize size, PRegister pd, PRegisterZero pg, ZRegister zn, ZRegister zm) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i8Bit || size == SubRegSize::i16Bit,
"match/nmatch can only use 8-bit or 16-bit element sizes");
LOGMAN_THROW_AA_FMT(pg <= PReg::p7, "match/nmatch can only use p0-p7 as a governing predicate");
uint32_t Instr = op;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= opc << 4;
Instr |= zm.Idx() << 16;
Instr |= pg.Idx() << 10;
Instr |= zn.Idx() << 5;
Instr |= pd.Idx();
dc32(Instr);
}
void SVEFPRecursiveReduction(uint32_t op, uint32_t opc, SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit || size == SubRegSize::i64Bit,
"FP reduction operation can only use 16-bit, 32-bit, or 64-bit element sizes");
LOGMAN_THROW_AA_FMT(pg <= PReg::p7, "FP reduction operation can only use p0-p7 as a governing predicate");
uint32_t Instr = op;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= opc << 16;
Instr |= pg.Idx() << 10;
Instr |= zn.Idx() << 5;
Instr |= vd.Idx();
dc32(Instr);
}
void SVEAddSubVectorsPredicated(uint32_t op, uint32_t opc, SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
LOGMAN_THROW_AA_FMT(zd.Idx() == zn.Idx(), "zd and zn must be the same register");
LOGMAN_THROW_AA_FMT(pg <= PReg::p7, "Add/Sub operation can only use p0-p7 as a governing predicate");
uint32_t Instr = op;
Instr |= FEXCore::ToUnderlying(size) << 22;
Instr |= opc << 16;
Instr |= pg.Idx() << 10;
Instr |= zm.Idx() << 5;
Instr |= zd.Idx();
dc32(Instr);
}
// SVE floating-point round to integral value
void frintX(uint32_t opc, FEXCore::ARMEmitter::SubRegSize size, FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::PRegister pg, FEXCore::ARMEmitter::ZRegister zn) {
// opc = round mode
@@ -18,8 +18,19 @@ enum ContextFlags : uint32_t {
CONTEXT_FLAG_32BIT = (1U << 1),
};
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
constexpr uint64_t STACK_COOKIE_MAGIC = 0x4142434445464748ULL;
#endif
struct X86ContextBackup {
// Host State
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
// During debug builds, insert a cookie on the stack.
// This is useful for validation that the stack is trying to be restored from the correct location.
// During stack restore, we ensure this is set to the value we expect.
// If given an incorrect stack location, or corrupted stack then this cookie will be wrong.
uint64_t StackCookie;
#endif
// RIP and RSP is stored in GPRs here
uint64_t GPRs[23];
FEXCore::x86_64::_libc_fpstate FPRState;
@@ -39,6 +50,9 @@ struct X86ContextBackup {
struct ArmContextBackup {
// Host State
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
uint64_t StackCookie;
#endif
uint64_t GPRs[31];
uint64_t PrevSP;
uint64_t PrevPC;
@@ -211,6 +225,10 @@ static inline void BackupContext(void* ucontext, T *Backup) {
// Save the signal mask so we can restore it
memcpy(&Backup->sa_mask, &_ucontext->uc_sigmask, sizeof(uint64_t));
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
Backup->StackCookie = STACK_COOKIE_MAGIC;
#endif
} else {
// This must be a runtime error
ERROR_AND_DIE_FMT("Wrong context type");
@@ -237,6 +255,8 @@ static inline void RestoreContext(void* ucontext, T *Backup) {
// Restore the signal mask now
memcpy(&_ucontext->uc_sigmask, &Backup->sa_mask, sizeof(uint64_t));
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
} else {
// This must be a runtime error
ERROR_AND_DIE_FMT("Wrong context type");
@@ -302,6 +322,10 @@ static inline void BackupContext(void* ucontext, T *Backup) {
// Save the signal mask so we can restore it
memcpy(&Backup->sa_mask, &_ucontext->uc_sigmask, sizeof(uint64_t));
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
Backup->StackCookie = STACK_COOKIE_MAGIC;
#endif
} else {
// This must be a runtime error
ERROR_AND_DIE_FMT("Wrong context type");
@@ -321,6 +345,8 @@ static inline void RestoreContext(void* ucontext, T *Backup) {
// Restore the signal mask now
memcpy(&_ucontext->uc_sigmask, &Backup->sa_mask, sizeof(uint64_t));
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
} else {
// This must be a runtime error
ERROR_AND_DIE_FMT("Wrong context type");
+18 -98
View File
@@ -88,7 +88,6 @@ static uint32_t CalculateNumberOfCPUs() {
// when AVX implementations are further along.
constexpr uint32_t SUPPORTS_AVX = 0;
// #define CPUID_AMD
#ifdef CPUID_AMD
constexpr uint32_t FAMILY_IDENTIFIER =
0 | // Stepping
@@ -122,25 +121,24 @@ void CPUIDEmu::SetupHostHybridFlag() {
uint64_t MIDR{};
for (size_t i = 0; i < CPUs; ++i) {
std::error_code ec{};
std::string MIDRPath = "/sys/devices/system/cpu/cpu" + std::to_string(i) + "/regs/identification/midr_el1";
if (std::filesystem::exists(MIDRPath, ec)) {
std::vector<char> Data{};
// Needs to be a fixed size since depending on kernel it will try to read a full page of data and fail
// Only read 18 bytes for a 64bit value prefixed with 0x
if (FEXCore::FileLoading::LoadFile(Data, MIDRPath, 18)) {
uint64_t NewMIDR{};
std::string_view MIDRView(&Data.at(0), 18);
if (FEXCore::StrConv::Conv(MIDRView, &NewMIDR)) {
if (MIDR != 0 && MIDR != NewMIDR) {
// CPU mismatch, claim hybrid
Hybrid = true;
}
std::string MIDRPath = fmt::format("/sys/devices/system/cpu/cpu{}/regs/identification/midr_el1", i);
// Truncate to 32-bits, top 32-bits are all reserved in MIDR
PerCPUData[i].ProductName = ProductNames::ARM_UNKNOWN;
PerCPUData[i].MIDR = NewMIDR;
MIDR = NewMIDR;
std::array<char, 18> Data;
// Needs to be a fixed size since depending on kernel it will try to read a full page of data and fail
// Only read 18 bytes for a 64bit value prefixed with 0x
if (FEXCore::FileLoading::LoadFileToBuffer(MIDRPath, Data) == sizeof(Data)) {
uint64_t NewMIDR{};
std::string_view MIDRView(Data.data(), sizeof(Data));
if (FEXCore::StrConv::Conv(MIDRView, &NewMIDR)) {
if (MIDR != 0 && MIDR != NewMIDR) {
// CPU mismatch, claim hybrid
Hybrid = true;
}
// Truncate to 32-bits, top 32-bits are all reserved in MIDR
PerCPUData[i].ProductName = ProductNames::ARM_UNKNOWN;
PerCPUData[i].MIDR = NewMIDR;
MIDR = NewMIDR;
}
}
}
@@ -657,8 +655,8 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) {
(0 << 20) | // SMAP Supervisor mode access prevention and CLAC/STAC instructions
(0 << 21) | // Reserved
(0 << 22) | // Reserved
(0 << 23) | // CLFLUSHOPT instruction
(0 << 24) | // CLWB instruction
(1 << 23) | // CLFLUSHOPT instruction
(CTX->HostFeatures.SupportsCLWB << 24) | // CLWB instruction
(0 << 25) | // Intel processor trace
(0 << 26) | // Reserved
(0 << 27) | // Reserved
@@ -1215,84 +1213,6 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_Reserved(uint32_t Leaf) {
void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
CTX = ctx;
RegisterFunction(0, &CPUIDEmu::Function_0h);
RegisterFunction(1, &CPUIDEmu::Function_01h);
RegisterFunction(2, &CPUIDEmu::Function_02h);
// 3: Serial Number(previously), now reserved
#ifndef CPUID_AMD
// Deterministic cache parameters for each level
RegisterFunction(0x4, &CPUIDEmu::Function_04h);
#endif
// 5: Monitor/mwait
// Thermal and power management
RegisterFunction(6, &CPUIDEmu::Function_06h);
// Extended feature flags
RegisterFunction(7, &CPUIDEmu::Function_07h);
// 9: Direct Cache Access information
// 0x0A: Architectural performance monitoring
// 0x0B: Extended topology enumeration
// 0x0D: Processor extended state enumeration
RegisterFunction(0x0D, &CPUIDEmu::Function_0Dh);
// 0x0F: Intel RDT monitoring
// 0x10: Intel RDT allocation enumeration
// 0x12: Intel SGX capability enumeration
// 0x13: Reserved
// 0x14: Intel Processor trace
#ifndef CPUID_AMD
// Timestamp counter information
// Doesn't exist on AMD hardware
RegisterFunction(0x15, &CPUIDEmu::Function_15h);
#endif
// 0x16: Processor frequency information
// 0x17: SoC vendor attribute enumeration
// 0x1A: Hybrid Information Sub-leaf
#ifndef CPUID_AMD
RegisterFunction(0x1A, &CPUIDEmu::Function_1Ah);
#endif
// Hypervisor CPUID information leaf
RegisterFunction(0x4000'0000, &CPUIDEmu::Function_4000_0000h);
RegisterFunction(0x4000'0001, &CPUIDEmu::Function_4000_0001h);
// Largest extended function number
RegisterFunction(0x8000'0000, &CPUIDEmu::Function_8000_0000h);
// Processor vendor
RegisterFunction(0x8000'0001, &CPUIDEmu::Function_8000_0001h);
// Processor brand string
RegisterFunction(0x8000'0002, &CPUIDEmu::Function_8000_0002h);
// Processor brand string continued
RegisterFunction(0x8000'0003, &CPUIDEmu::Function_8000_0003h);
// Processor brand string continued
RegisterFunction(0x8000'0004, &CPUIDEmu::Function_8000_0004h);
// 0x8000'0005: L1 Cache and TLB identifiers
#ifdef CPUID_AMD
RegisterFunction(0x8000'0005, &CPUIDEmu::Function_8000_0005h);
#else
// This is full reserved on Intel platforms
RegisterFunction(0x8000'0005, &CPUIDEmu::Function_Reserved);
#endif
// 0x8000'0006: L2 Cache identifiers
RegisterFunction(0x8000'0006, &CPUIDEmu::Function_8000_0006h);
// Advanced power management information
RegisterFunction(0x8000'0007, &CPUIDEmu::Function_8000_0007h);
// Virtual and physical address sizes
RegisterFunction(0x8000'0008, &CPUIDEmu::Function_8000_0008h);
// 0x8000'000A: SVM Revision
// TLB 1GB page identifiers
RegisterFunction(0x8000'0019, &CPUIDEmu::Function_8000_0019h);
// 0x8000'001A: Performance optimization identifiers
// 0x8000'001B: Instruction based sampling identifiers
// 0x8000'001C: Lightweight profiling capabilities
// 0x8000'001D: Cache properties
#ifdef CPUID_AMD
// Deterministic cache parameters for each level
RegisterFunction(0x8000'001D, &CPUIDEmu::Function_8000_001Dh);
#endif
// 0x8000'001E: Extended APIC ID
// 0x8000'001F: AMD Secure Encryption
// Setup some state tracking
SetupHostHybridFlag();
}
+169 -11
View File
@@ -13,6 +13,9 @@ namespace Context {
struct Context;
}
// Debugging define to switch what family of CPU we execute as.
// Might be useful if an application makes an assumption about a CPU.
// #define CPUID_AMD
class CPUIDEmu final {
private:
constexpr static uint32_t CPUID_VENDOR_INTEL1 = 0x756E6547; // "Genu"
@@ -31,13 +34,24 @@ public:
void Init(FEXCore::Context::Context *ctx);
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, uint32_t Leaf) {
const auto Handler = FunctionHandlers.find(Function);
if (Handler == FunctionHandlers.end()) {
return Function_Reserved(Leaf);
if (Function < Primary.size()) {
const auto Handler = Primary[Function];
return (this->*Handler)(Leaf);
}
return (this->*Handler->second)(Leaf);
constexpr uint32_t HypervisorBase = 0x4000'0000;
if (Function >= HypervisorBase && Function < (HypervisorBase + Hypervisor.size())) {
const auto Handler = Hypervisor[Function - HypervisorBase];
return (this->*Handler)(Leaf);
}
constexpr uint32_t ExtendedBase = 0x8000'0000;
if (Function >= ExtendedBase && Function < (ExtendedBase + Extended.size())) {
const auto Handler = Extended[Function - ExtendedBase];
return (this->*Handler)(Leaf);
}
return Function_Reserved(Leaf);
}
FEXCore::CPUID::FunctionResults RunFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) {
@@ -55,11 +69,6 @@ private:
FEX_CONFIG_OPT(Cores, THREADS);
using FunctionHandler = FEXCore::CPUID::FunctionResults (CPUIDEmu::*)(uint32_t Leaf);
void RegisterFunction(uint32_t Function, FunctionHandler Handler) {
FunctionHandlers.insert_or_assign(Function, Handler);
}
std::unordered_map<uint32_t, FunctionHandler> FunctionHandlers;
struct CPUData {
const char *ProductName{};
#ifdef _M_ARM_64
@@ -95,12 +104,161 @@ private:
FEXCore::CPUID::FunctionResults Function_8000_0006h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0007h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0008h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0009h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0019h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_001Dh(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_Reserved(uint32_t Leaf);
void SetupHostHybridFlag();
static constexpr std::array<FunctionHandler, 27> Primary = {
// 0: Highest function parameter and ID
&CPUIDEmu::Function_0h,
// 1: Processor info
&CPUIDEmu::Function_01h,
// 2: Cache and TLB info
&CPUIDEmu::Function_02h,
// 3: Serial Number(previously), now reserved
&CPUIDEmu::Function_Reserved,
#ifndef CPUID_AMD
// 4: Deterministic cache parameters for each level
&CPUIDEmu::Function_04h,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 5: Monitor/mwait
&CPUIDEmu::Function_Reserved,
// 6: Thermal and power management
&CPUIDEmu::Function_06h,
// 7: Extended feature flags
&CPUIDEmu::Function_07h,
// 0x08: Reserved?
&CPUIDEmu::Function_Reserved,
// 9: Direct Cache Access information
&CPUIDEmu::Function_Reserved,
// 0x0A: Architectural performance monitoring
&CPUIDEmu::Function_Reserved,
// 0x0B: Extended topology enumeration
&CPUIDEmu::Function_Reserved,
// 0x0C: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x0D: Processor extended state enumeration
&CPUIDEmu::Function_0Dh,
// 0x0E: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x0F: Intel RDT monitoring
&CPUIDEmu::Function_Reserved,
// 0x10: Intel RDT allocation enumeration
&CPUIDEmu::Function_Reserved,
// 0x12: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x12: Intel SGX capability enumeration
&CPUIDEmu::Function_Reserved,
// 0x13: Reserved
&CPUIDEmu::Function_Reserved,
// 0x14: Intel Processor trace
&CPUIDEmu::Function_Reserved,
#ifndef CPUID_AMD
// Timestamp counter information
// Doesn't exist on AMD hardware
&CPUIDEmu::Function_15h,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 0x16: Processor frequency information
&CPUIDEmu::Function_Reserved,
// 0x17: SoC vendor attribute enumeration
&CPUIDEmu::Function_Reserved,
// 0x18: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x19: Reserved?
&CPUIDEmu::Function_Reserved,
#ifndef CPUID_AMD
// 0x1A: Hybrid Information Sub-leaf
&CPUIDEmu::Function_1Ah,
#else
&CPUIDEmu::Function_Reserved,
#endif
};
static constexpr std::array<FunctionHandler, 2> Hypervisor = {
// Hypervisor CPUID information leaf
&CPUIDEmu::Function_4000_0000h,
// FEX-Emu specific leaf
&CPUIDEmu::Function_4000_0001h,
};
static constexpr std::array<FunctionHandler, 32> Extended = {
// Largest extended function number
&CPUIDEmu::Function_8000_0000h,
// Processor vendor
&CPUIDEmu::Function_8000_0001h,
// Processor brand string
&CPUIDEmu::Function_8000_0002h,
// Processor brand string continued
&CPUIDEmu::Function_8000_0003h,
// Processor brand string continued
&CPUIDEmu::Function_8000_0004h,
#ifdef CPUID_AMD
// 0x8000'0005: L1 Cache and TLB identifiers
&CPUIDEmu::Function_8000_0005h,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 0x8000'0006: L2 Cache identifiers
&CPUIDEmu::Function_8000_0006h,
// 0x8000'0007: Advanced power management information
&CPUIDEmu::Function_8000_0007h,
// 0x8000'0008: Virtual and physical address sizes
&CPUIDEmu::Function_8000_0008h,
// 0x8000'0009: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000A: SVM Revision
&CPUIDEmu::Function_Reserved,
// 0x8000'000B: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000C: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000D: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000E: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000F: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0010: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0011: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0012: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0013: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0014: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0015: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0016: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0017: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0018: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0019: TLB 1GB page identifiers
&CPUIDEmu::Function_8000_0019h,
// 0x8000'001A: Performance optimization identifiers
&CPUIDEmu::Function_Reserved,
// 0x8000'001B: Instruction based sampling identifiers
&CPUIDEmu::Function_Reserved,
// 0x8000'001C: Lightweight profiling capabilities
&CPUIDEmu::Function_Reserved,
#ifdef CPUID_AMD
// 0x8000'001D: Cache properties
&CPUIDEmu::Function_8000_001Dh,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 0x8000'001E: Extended APIC ID
&CPUIDEmu::Function_Reserved,
// 0x8000'001F: AMD Secure Encryption
&CPUIDEmu::Function_Reserved,
};
};
}
+5
View File
@@ -159,6 +159,11 @@ namespace FEXCore::Context {
HostFeatures.SupportsAVX = false;
}
if (!Config.Is64BitMode()) {
// When operating in 32-bit mode, the virtual memory we care about is only the lower 32-bits.
Config.VirtualMemSize = 1ULL << 32;
}
if (Config.BlockJITNaming() ||
Config.GlobalJITNaming() ||
Config.LibraryJITNaming()) {
@@ -155,14 +155,16 @@ void Arm64Dispatcher::EmitDispatcher() {
// Shift the offset by the size of the block cache entry
add(ARMEmitter::XReg::x0, ARMEmitter::XReg::x0, ARMEmitter::XReg::x1, ARMEmitter::ShiftType::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
// Load the guest address first to ensure it maps to the address we are currently at
// The the full LookupCacheEntry with a single LDP.
// Check the guest address first to ensure it maps to the address we are currently at.
// This fixes aliasing problems
ldr(ARMEmitter::XReg::x1, ARMEmitter::Reg::r0, offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode));
ldp<ARMEmitter::IndexType::OFFSET>(ARMEmitter::XReg::x3, ARMEmitter::XReg::x1, ARMEmitter::Reg::r0, 0);
// If the guest address doesn't match, Compile the block.
cmp(ARMEmitter::XReg::x1, RipReg);
b(ARMEmitter::Condition::CC_NE, &NoBlock);
// Now load the actual host block to execute if we can
ldr(ARMEmitter::XReg::x3, ARMEmitter::Reg::r0, offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode));
// Check the host address to see if it matches, else compile the block.
cbz(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r3, &NoBlock);
// If we've made it here then we have a real compiled block
@@ -318,6 +320,14 @@ void Arm64Dispatcher::EmitDispatcher() {
hlt(0);
}
{
SignalHandlerReturnAddressRT = GetCursorAddress<uint64_t>();
// Now to get back to our old location we need to do a fault dance
// We can't use SIGTRAP here since gdb catches it and never gives it to the application!
hlt(0);
}
{
// Guest SIGILL handler
// Needs to be distinct from the SignalHandlerReturnAddress
@@ -652,6 +662,7 @@ void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thr
Common.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
Common.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV;
Common.SignalReturnHandler = SignalHandlerReturnAddress;
Common.SignalReturnHandlerRT = SignalHandlerReturnAddressRT;
auto &AArch64 = Thread->CurrentFrame->Pointers.AArch64;
AArch64.LUDIVHandler = LUDIVHandlerAddress;
File diff suppressed because it is too large. Load diff
@@ -44,6 +44,7 @@ public:
uint64_t ThreadPauseHandlerAddressSpillSRA{};
uint64_t ExitFunctionLinkerAddress{};
uint64_t SignalHandlerReturnAddress{};
uint64_t SignalHandlerReturnAddressRT{};
uint64_t GuestSignal_SIGILL{};
uint64_t GuestSignal_SIGTRAP{};
uint64_t GuestSignal_SIGSEGV{};
@@ -90,8 +91,71 @@ protected:
, config {Config}
{}
void RestoreFrame_x64(ArchHelpers::Context::ContextBackup* Context, FEXCore::Core::CpuStateFrame *Frame, void *ucontext);
void RestoreFrame_ia32(ArchHelpers::Context::ContextBackup* Context, FEXCore::Core::CpuStateFrame *Frame, void *ucontext);
void RestoreRTFrame_ia32(ArchHelpers::Context::ContextBackup* Context, FEXCore::Core::CpuStateFrame *Frame, void *ucontext);
const bool incomplete_guest_restorer_support = false;
///< Setup the signal frame for x64.
uint64_t SetupFrame_x64(FEXCore::Core::InternalThreadState *Thread, ArchHelpers::Context::ContextBackup* ContextBackup, FEXCore::Core::CpuStateFrame *Frame,
int Signal, siginfo_t *HostSigInfo, void *ucontext,
GuestSigAction *GuestAction, stack_t *GuestStack,
uint64_t NewGuestSP, const uint32_t eflags);
///< Setup the signal frame for a 32-bit signal without SA_SIGINFO.
uint64_t SetupFrame_ia32(ArchHelpers::Context::ContextBackup* ContextBackup, FEXCore::Core::CpuStateFrame *Frame,
int Signal, siginfo_t *HostSigInfo, void *ucontext,
GuestSigAction *GuestAction, stack_t *GuestStack,
uint64_t NewGuestSP, const uint32_t eflags);
///< Setup the signal frame for a 32-bit signal with SA_SIGINFO.
uint64_t SetupRTFrame_ia32(ArchHelpers::Context::ContextBackup* ContextBackup, FEXCore::Core::CpuStateFrame *Frame,
int Signal, siginfo_t *HostSigInfo, void *ucontext,
GuestSigAction *GuestAction, stack_t *GuestStack,
uint64_t NewGuestSP, const uint32_t eflags);
ArchHelpers::Context::ContextBackup* StoreThreadState(FEXCore::Core::InternalThreadState *Thread, int Signal, void *ucontext);
void RestoreThreadState(FEXCore::Core::InternalThreadState *Thread, void *ucontext);
enum class RestoreType {
TYPE_REALTIME, ///< Signal restore type is from a `realtime` signal.
TYPE_NONREALTIME, ///< Signal restore type is from a `non-realtime` signal.
TYPE_PAUSE, ///< Signal restore type is from a GDB pause event.
};
/*
* Signal frames on 32-bit architecture needs to match exactly how the kernel generates the frame.
* This is because large parts of the signal frame definition is part of the UAPI.
* This means that when FEX sets up the signal frame, it needs to match the UAPI stack setup.
*
* The two signal stack frame types below describe the two different 32-bit frame types.
*/
// The 32-bit non-realtime signal frame.
// This frame type is used when the guest signal is used without the `SA_SIGINFO` flag.
struct SigFrame_i32 {
uint32_t pretcode; ///< sigreturn return branch point.
int32_t Signal; ///< The signal hit.
FEXCore::x86::sigcontext sc; ///< The signal context.
x86::_libc_fpstate fpstate_unused; ///< Unused fpstate. Retained for backwards compatibility.
uint32_t extramask[1]; ///< Upper 32-bits of the signal mask. Lower 32-bits is in the sigcontext.
char retcode[8]; ///< Unused but needs to be filled. GDB seemingly uses as a debug marker.
///< FP state now follows after this.
};
// The 32-bit realtime signal frame.
// This frame type is used when the guest signal is used with the `SA_SIGINFO` flag.
struct RTSigFrame_i32 {
uint32_t pretcode; ///< sigreturn return branch point.
int32_t Signal; ///< The signal hit.
uint32_t pinfo; ///< Pointer to siginfo_t
uint32_t puc; ///< Pointer to ucontext_t
FEXCore::x86::siginfo_t info;
FEXCore::x86::ucontext_t uc;
char retcode[8]; ///< Unused but needs to be filled. GDB seemingly uses as a debug marker.
///< FP state now follows after this.
};
void RestoreThreadState(FEXCore::Core::InternalThreadState *Thread, void *ucontext, RestoreType Type);
std::stack<uint64_t, std::vector<uint64_t>> SignalFrames;
virtual void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) {}
@@ -344,6 +344,12 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherCon
ud2();
}
{
// RT Signal return handler
SignalHandlerReturnAddressRT = getCurr<uint64_t>();
ud2();
}
{
// Guest SIGILL handler
// Needs to be distinct from the SignalHandlerReturnAddress
@@ -486,6 +492,7 @@ void X86Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Threa
Common.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
Common.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV;
Common.SignalReturnHandler = SignalHandlerReturnAddress;
Common.SignalReturnHandlerRT = SignalHandlerReturnAddressRT;
auto &Interpreter = Thread->CurrentFrame->Pointers.Interpreter;
(uintptr_t&)Interpreter.CallbackReturn = IntCallbackReturnAddress;
+127 -232
View File
@@ -32,26 +32,6 @@ using namespace FEXCore::X86Tables;
static uint32_t MapModRMToReg(uint8_t REX, uint8_t bits, bool HighBits, bool HasREX, bool HasXMM, bool HasMM, uint8_t InvalidOffset = 16) {
using GPRArray = std::array<uint32_t, 16>;
static constexpr GPRArray GPRIndexes = {
// Classical ordering?
FEXCore::X86State::REG_RAX,
FEXCore::X86State::REG_RCX,
FEXCore::X86State::REG_RDX,
FEXCore::X86State::REG_RBX,
FEXCore::X86State::REG_RSP,
FEXCore::X86State::REG_RBP,
FEXCore::X86State::REG_RSI,
FEXCore::X86State::REG_RDI,
FEXCore::X86State::REG_R8,
FEXCore::X86State::REG_R9,
FEXCore::X86State::REG_R10,
FEXCore::X86State::REG_R11,
FEXCore::X86State::REG_R12,
FEXCore::X86State::REG_R13,
FEXCore::X86State::REG_R14,
FEXCore::X86State::REG_R15,
};
static constexpr GPRArray GPR8BitHighIndexes = {
// Classical ordering?
FEXCore::X86State::REG_RAX,
@@ -72,108 +52,30 @@ static uint32_t MapModRMToReg(uint8_t REX, uint8_t bits, bool HighBits, bool Has
FEXCore::X86State::REG_R15,
};
static constexpr GPRArray XMMIndexes = {
FEXCore::X86State::REG_XMM_0,
FEXCore::X86State::REG_XMM_1,
FEXCore::X86State::REG_XMM_2,
FEXCore::X86State::REG_XMM_3,
FEXCore::X86State::REG_XMM_4,
FEXCore::X86State::REG_XMM_5,
FEXCore::X86State::REG_XMM_6,
FEXCore::X86State::REG_XMM_7,
FEXCore::X86State::REG_XMM_8,
FEXCore::X86State::REG_XMM_9,
FEXCore::X86State::REG_XMM_10,
FEXCore::X86State::REG_XMM_11,
FEXCore::X86State::REG_XMM_12,
FEXCore::X86State::REG_XMM_13,
FEXCore::X86State::REG_XMM_14,
FEXCore::X86State::REG_XMM_15,
};
static constexpr GPRArray MMIndexes = {
FEXCore::X86State::REG_MM_0,
FEXCore::X86State::REG_MM_1,
FEXCore::X86State::REG_MM_2,
FEXCore::X86State::REG_MM_3,
FEXCore::X86State::REG_MM_4,
FEXCore::X86State::REG_MM_5,
FEXCore::X86State::REG_MM_6,
FEXCore::X86State::REG_MM_7,
FEXCore::X86State::REG_INVALID,
FEXCore::X86State::REG_INVALID,
FEXCore::X86State::REG_INVALID,
FEXCore::X86State::REG_INVALID,
FEXCore::X86State::REG_INVALID,
FEXCore::X86State::REG_INVALID,
FEXCore::X86State::REG_INVALID,
FEXCore::X86State::REG_INVALID
};
const GPRArray *GPRs = &GPRIndexes;
if (HasXMM) {
GPRs = &XMMIndexes;
}
else if (HasMM) {
GPRs = &MMIndexes;
}
else if (HighBits && !HasREX) {
GPRs = &GPR8BitHighIndexes;
}
uint8_t Offset = (REX << 3) | bits;
if (Offset == InvalidOffset) {
return FEXCore::X86State::REG_INVALID;
}
return (*GPRs)[(REX << 3) | bits];
if (HasXMM) {
return FEXCore::X86State::REG_XMM_0 + Offset;
}
else if (HasMM) {
return FEXCore::X86State::REG_MM_0 + Offset;
}
else if (!(HighBits && !HasREX)) {
return FEXCore::X86State::REG_RAX + Offset;
}
return GPR8BitHighIndexes[Offset];
}
static uint32_t MapVEXToReg(uint8_t vvvv, bool HasXMM) {
using GPRArray = std::array<uint32_t, 16>;
static constexpr GPRArray GPRIndexes = {
FEXCore::X86State::REG_RAX,
FEXCore::X86State::REG_RCX,
FEXCore::X86State::REG_RDX,
FEXCore::X86State::REG_RBX,
FEXCore::X86State::REG_RSP,
FEXCore::X86State::REG_RBP,
FEXCore::X86State::REG_RSI,
FEXCore::X86State::REG_RDI,
FEXCore::X86State::REG_R8,
FEXCore::X86State::REG_R9,
FEXCore::X86State::REG_R10,
FEXCore::X86State::REG_R11,
FEXCore::X86State::REG_R12,
FEXCore::X86State::REG_R13,
FEXCore::X86State::REG_R14,
FEXCore::X86State::REG_R15,
};
static constexpr GPRArray XMMIndexes = {
FEXCore::X86State::REG_XMM_0,
FEXCore::X86State::REG_XMM_1,
FEXCore::X86State::REG_XMM_2,
FEXCore::X86State::REG_XMM_3,
FEXCore::X86State::REG_XMM_4,
FEXCore::X86State::REG_XMM_5,
FEXCore::X86State::REG_XMM_6,
FEXCore::X86State::REG_XMM_7,
FEXCore::X86State::REG_XMM_8,
FEXCore::X86State::REG_XMM_9,
FEXCore::X86State::REG_XMM_10,
FEXCore::X86State::REG_XMM_11,
FEXCore::X86State::REG_XMM_12,
FEXCore::X86State::REG_XMM_13,
FEXCore::X86State::REG_XMM_14,
FEXCore::X86State::REG_XMM_15,
};
if (HasXMM) {
return XMMIndexes[vvvv];
return FEXCore::X86State::REG_XMM_0 + vvvv;
} else {
return GPRIndexes[vvvv];
return FEXCore::X86State::REG_RAX + vvvv;
}
}
@@ -206,7 +108,7 @@ uint64_t Decoder::ReadData(uint8_t Size) {
uint64_t Res = 0;
std::memcpy(&Res, &InstStream[InstructionSize], Size);
#ifndef NDEBUG
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
for(size_t i = 0; i < Size; ++i) {
ReadByte();
}
@@ -384,12 +286,6 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
DecodeInst->OP = Op;
DecodeInst->TableInfo = Info;
// XXX: Once we support 32bit x86 then this will be necessary to support
if (Info->Type == FEXCore::X86Tables::TYPE_LEGACY_PREFIX) {
LogMan::Msg::DFmt("Legacy Prefix");
return false;
}
if (Info->Type == FEXCore::X86Tables::TYPE_UNKNOWN) {
LogMan::Msg::DFmt("Unknown instruction: {} 0x{:04x} 0x{:x}", Info->Name ?: "UND", Op, DecodeInst->PC);
return false;
@@ -429,6 +325,14 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
// This is used for ModRM register modification
// For both modrm.reg and modrm.rm(when mod == 0b11) when value is >= 0b100
// then it changes from expected registers to the high 8bits of the lower registers
// Bit annoying to support
// In the case of no modrm (REX in byte situation) then it is unaffected
bool Is8BitSrc{};
bool Is8BitDest{};
// If we require ModRM and haven't decoded it yet, do it now
// Some instructions have to read modrm upfront, others do it later
if (HasMODRM && !DecodeInst->DecodedModRM) {
@@ -445,6 +349,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
if (DstSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_8BIT) {
DecodeInst->Flags |= DecodeFlags::GenSizeDstSize(DecodeFlags::SIZE_8BIT);
DestSize = 1;
Is8BitDest = true;
}
else if (DstSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_16BIT) {
DecodeInst->Flags |= DecodeFlags::GenSizeDstSize(DecodeFlags::SIZE_16BIT);
@@ -487,6 +392,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
// Decode sources
if (SrcSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_8BIT) {
DecodeInst->Flags |= DecodeFlags::GenSizeSrcSize(DecodeFlags::SIZE_8BIT);
Is8BitSrc = true;
}
else if (SrcSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_16BIT) {
DecodeInst->Flags |= DecodeFlags::GenSizeSrcSize(DecodeFlags::SIZE_16BIT);
@@ -520,14 +426,6 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
}
}
// This is used for ModRM register modification
// For both modrm.reg and modrm.rm(when mod == 0b11) when value is >= 0b100
// then it changes from expected registers to the high 8bits of the lower registers
// Bit annoying to support
// In the case of no modrm (REX in byte situation) then it is unaffected
const bool Is8BitSrc = (DecodeFlags::GetSizeSrcFlags(DecodeInst->Flags) == DecodeFlags::SIZE_8BIT);
const bool Is8BitDest = (DecodeFlags::GetSizeDstFlags(DecodeInst->Flags) == DecodeFlags::SIZE_8BIT);
auto *CurrentDest = &DecodeInst->Dest;
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ||
@@ -538,8 +436,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
CurrentDest->Data.GPR.GPR = HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ? FEXCore::X86State::REG_RAX : FEXCore::X86State::REG_RDX;
CurrentDest = &DecodeInst->Src[0];
}
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_REX_IN_BYTE)) {
else if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_REX_IN_BYTE)) {
LOGMAN_THROW_AA_FMT(!HasMODRM, "This instruction shouldn't have ModRM!");
// If the REX is in the byte that means the lower nibble of the OP contains the destination GPR
@@ -684,12 +581,6 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
DecodeInst->OP = Op;
DecodeInst->TableInfo = Info;
// XXX: Once we support 32bit x86 then this will be necessary to support
if (Info->Type == FEXCore::X86Tables::TYPE_LEGACY_PREFIX) {
LogMan::Msg::DFmt("Legacy Prefix");
return false;
}
if (Info->Type == FEXCore::X86Tables::TYPE_UNKNOWN) {
LogMan::Msg::DFmt("Unknown instruction: {} 0x{:04x} 0x{:x}", Info->Name ?: "UND", Op, DecodeInst->PC);
return false;
@@ -703,7 +594,11 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
LOGMAN_THROW_AA_FMT(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX,
"REX PREFIX should have been decoded before this!");
if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
// A normal instruction is the most likely.
if (Info->Type == FEXCore::X86Tables::TYPE_INST) [[likely]] {
return NormalOp(Info, Op);
}
else if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
Info->Type <= FEXCore::X86Tables::TYPE_GROUP_11) {
uint8_t ModRMByte = ReadByte();
DecodeInst->ModRM = ModRMByte;
@@ -851,7 +746,8 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
return NormalOp(&EVEXTableOps[EVEXOp], EVEXOp);
}
return NormalOp(Info, Op);
LOGMAN_MSG_A_FMT("Invalid instruction decoding type");
FEX_UNREACHABLE;
}
bool Decoder::DecodeInstruction(uint64_t PC) {
@@ -870,105 +766,106 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
case 0x0F: {// Escape Op
uint8_t EscapeOp = ReadByte();
switch (EscapeOp) {
case 0x0F: [[unlikely]] { // 3DNow!
// 3DNow! Instruction Encoding: 0F 0F [ModRM] [SIB] [Displacement] [Opcode]
// Decode ModRM
uint8_t ModRMByte = ReadByte();
DecodeInst->ModRM = ModRMByte;
DecodeInst->DecodedModRM = true;
case 0x0F: [[unlikely]] { // 3DNow!
// 3DNow! Instruction Encoding: 0F 0F [ModRM] [SIB] [Displacement] [Opcode]
// Decode ModRM
uint8_t ModRMByte = ReadByte();
DecodeInst->ModRM = ModRMByte;
DecodeInst->DecodedModRM = true;
FEXCore::X86Tables::ModRMDecoded ModRM;
ModRM.Hex = DecodeInst->ModRM;
FEXCore::X86Tables::ModRMDecoded ModRM;
ModRM.Hex = DecodeInst->ModRM;
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
// All 3DNow! instructions have the second argument as the rm handler
// We need to decode it upfront to get the displacement out of the way
if (ModRM.mod != 0b11) {
auto Disp = DecodeModRMs_Disp[Has16BitAddressing];
(this->*Disp)(&DecodeInst->Src[0], ModRM);
// All 3DNow! instructions have the second argument as the rm handler
// We need to decode it upfront to get the displacement out of the way
if (ModRM.mod != 0b11) {
auto Disp = DecodeModRMs_Disp[Has16BitAddressing];
(this->*Disp)(&DecodeInst->Src[0], ModRM);
}
// Take a peek at the op just past the displacement
uint8_t LocalOp = ReadByte();
return NormalOpHeader(&FEXCore::X86Tables::DDDNowOps[LocalOp], LocalOp);
break;
}
case 0x38: { // F38 Table!
constexpr uint16_t PF_38_NONE = 0;
constexpr uint16_t PF_38_66 = (1U << 0);
constexpr uint16_t PF_38_F2 = (1U << 1);
constexpr uint16_t PF_38_F3 = (1U << 2);
// Take a peek at the op just past the displacement
uint8_t LocalOp = ReadByte();
return NormalOpHeader(&FEXCore::X86Tables::DDDNowOps[LocalOp], LocalOp);
break;
}
case 0x38: { // F38 Table!
constexpr uint16_t PF_38_NONE = 0;
constexpr uint16_t PF_38_66 = (1U << 0);
constexpr uint16_t PF_38_F2 = (1U << 1);
constexpr uint16_t PF_38_F3 = (1U << 2);
uint16_t Prefix = PF_38_NONE;
if (DecodeInst->Flags & DecodeFlags::FLAG_OPERAND_SIZE) {
Prefix |= PF_38_66;
}
if (DecodeInst->Flags & DecodeFlags::FLAG_REPNE_PREFIX) {
Prefix |= PF_38_F2;
}
if (DecodeInst->Flags & DecodeFlags::FLAG_REP_PREFIX) {
Prefix |= PF_38_F3;
}
uint16_t Prefix = PF_38_NONE;
if (DecodeInst->Flags & DecodeFlags::FLAG_OPERAND_SIZE) {
Prefix |= PF_38_66;
}
if (DecodeInst->Flags & DecodeFlags::FLAG_REPNE_PREFIX) {
Prefix |= PF_38_F2;
}
if (DecodeInst->Flags & DecodeFlags::FLAG_REP_PREFIX) {
Prefix |= PF_38_F3;
uint16_t LocalOp = (Prefix << 8) | ReadByte();
return NormalOpHeader(&FEXCore::X86Tables::H0F38TableOps[LocalOp], LocalOp);
break;
}
case 0x3A: { // F3A Table!
constexpr uint16_t PF_3A_NONE = 0;
constexpr uint16_t PF_3A_66 = (1 << 0);
constexpr uint16_t PF_3A_REX = (1 << 1);
uint16_t LocalOp = (Prefix << 8) | ReadByte();
return NormalOpHeader(&FEXCore::X86Tables::H0F38TableOps[LocalOp], LocalOp);
break;
}
case 0x3A: { // F3A Table!
constexpr uint16_t PF_3A_NONE = 0;
constexpr uint16_t PF_3A_66 = (1 << 0);
constexpr uint16_t PF_3A_REX = (1 << 1);
uint16_t Prefix = PF_3A_NONE;
if (DecodeInst->LastEscapePrefix == 0x66) // Operand Size
Prefix = PF_3A_66;
uint16_t Prefix = PF_3A_NONE;
if (DecodeInst->LastEscapePrefix == 0x66) // Operand Size
Prefix = PF_3A_66;
if (DecodeInst->Flags & DecodeFlags::FLAG_REX_WIDENING)
Prefix |= PF_3A_REX;
if (DecodeInst->Flags & DecodeFlags::FLAG_REX_WIDENING)
Prefix |= PF_3A_REX;
uint16_t LocalOp = (Prefix << 8) | ReadByte();
return NormalOpHeader(&FEXCore::X86Tables::H0F3ATableOps[LocalOp], LocalOp);
break;
}
default: [[likely]] { // Two byte table!
// x86-64 abuses three legacy prefixes to extend the table encodings
// 0x66 - Operand Size prefix
// 0xF2 - REPNE prefix
// 0xF3 - REP prefix
// If any of these three prefixes are used then it falls down the subtable
// Additionally: If you hit repeat of differnt prefixes then only the LAST one before this one works for subtable selection
uint16_t LocalOp = (Prefix << 8) | ReadByte();
return NormalOpHeader(&FEXCore::X86Tables::H0F3ATableOps[LocalOp], LocalOp);
break;
}
default: // Two byte table!
// x86-64 abuses three legacy prefixes to extend the table encodings
// 0x66 - Operand Size prefix
// 0xF2 - REPNE prefix
// 0xF3 - REP prefix
// If any of these three prefixes are used then it falls down the subtable
// Additionally: If you hit repeat of differnt prefixes then only the LAST one before this one works for subtable selection
bool NoOverlay = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY) != 0;
bool NoOverlay66 = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY66) != 0;
bool NoOverlay = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY) != 0;
bool NoOverlay66 = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY66) != 0;
if (NoOverlay) { // This section of the table ignores prefix extention
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
if (NoOverlay) { // This section of the table ignores prefix extention
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0xF3) { // REP
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_REP_PREFIX;
return NormalOpHeader(&FEXCore::X86Tables::RepModOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0xF2) { // REPNE
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_REPNE_PREFIX;
return NormalOpHeader(&FEXCore::X86Tables::RepNEModOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0x66 && !NoOverlay66) { // Operand Size
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_OPERAND_SIZE;
DecodeFlags::PopOpAddrIf(&DecodeInst->Flags, DecodeFlags::FLAG_OPERAND_SIZE_LAST);
return NormalOpHeader(&FEXCore::X86Tables::OpSizeModOps[EscapeOp], EscapeOp);
}
else {
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
}
break;
}
else if (DecodeInst->LastEscapePrefix == 0xF3) { // REP
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_REP_PREFIX;
return NormalOpHeader(&FEXCore::X86Tables::RepModOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0xF2) { // REPNE
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_REPNE_PREFIX;
return NormalOpHeader(&FEXCore::X86Tables::RepNEModOps[EscapeOp], EscapeOp);
}
else if (DecodeInst->LastEscapePrefix == 0x66 && !NoOverlay66) { // Operand Size
// Remove prefix so it doesn't effect calculations.
// This is only an escape prefix rather tan modifier now
DecodeInst->Flags &= ~DecodeFlags::FLAG_OPERAND_SIZE;
DecodeFlags::PopOpAddrIf(&DecodeInst->Flags, DecodeFlags::FLAG_OPERAND_SIZE_LAST);
return NormalOpHeader(&FEXCore::X86Tables::OpSizeModOps[EscapeOp], EscapeOp);
}
else {
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
}
break;
}
break;
}
@@ -1021,7 +918,7 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
case 0x65: // GS prefix
DecodeInst->Flags |= DecodeFlags::FLAG_GS_PREFIX;
break;
default: { // Default base table
default: [[likely]] { // Default base table
auto Info = &FEXCore::X86Tables::BaseOps[Op];
if (Info->Type == FEXCore::X86Tables::TYPE_REX_PREFIX) {
@@ -1240,24 +1137,19 @@ void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC,
auto OpMinPage = OpMinAddress & FHU::FEX_PAGE_MASK;
auto OpMaxPage = OpMaxAddress & FHU::FEX_PAGE_MASK;
if (OpMinPage != CurrentCodePage) {
CurrentCodePage = OpMinPage;
if (CodePages.insert(CurrentCodePage).second) {
AddContainedCodePage(PC, CurrentCodePage, FHU::FEX_PAGE_SIZE);
}
CodePages.insert(CurrentCodePage);
}
if (OpMaxPage != CurrentCodePage) {
CurrentCodePage = OpMaxPage;
if (CodePages.insert(CurrentCodePage).second) {
AddContainedCodePage(PC, CurrentCodePage, FHU::FEX_PAGE_SIZE);
}
CodePages.insert(CurrentCodePage);
}
bool ErrorDuringDecoding = !DecodeInstruction(RIPToDecode + PCOffset);
if (ErrorDuringDecoding) {
if (ErrorDuringDecoding) [[unlikely]] {
LogMan::Msg::DFmt("Couldn't Decode something at 0x{:x}, Started at 0x{:x}", RIPToDecode + PCOffset, PC);
// Put an invalid instruction in the stream so the core can raise SIGILL if hit
CurrentBlockDecoding.HasInvalidInstruction = true;
@@ -1314,6 +1206,9 @@ void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC,
CurrentBlockDecoding.DecodedInstructions = &DecodedBuffer[BlockStartOffset];
}
for (auto CodePage : CodePages) {
AddContainedCodePage(PC, CodePage, FHU::FEX_PAGE_SIZE);
}
// sort for better branching
std::sort(Blocks.begin(), Blocks.end(), [](const FEXCore::Frontend::Decoder::DecodedBlocks& a, const FEXCore::Frontend::Decoder::DecodedBlocks& b) {
@@ -79,6 +79,7 @@ HostFeatures::HostFeatures() {
SupportsSHA = true;
SupportsBMI1 = true;
SupportsBMI2 = true;
SupportsCLWB = true;
if (!SupportsAtomics) {
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
@@ -128,6 +129,7 @@ HostFeatures::HostFeatures() {
SupportsSHA = Features.has(Xbyak::util::Cpu::tSHA);
SupportsBMI1 = Features.has(Xbyak::util::Cpu::tBMI1);
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tBMI2);
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tCLWB);
SupportsPMULL_128Bit = Features.has(Xbyak::util::Cpu::tPCLMULQDQ);
// xbyak doesn't know how to check for CLZero
@@ -18,8 +18,8 @@ $end_info$
namespace FEXCore::CPU {
[[noreturn]]
static void SignalReturn(FEXCore::Core::InternalThreadState *Thread) {
Thread->CTX->SignalThread(Thread, FEXCore::Core::SignalEvent::Return);
static void SignalReturn(FEXCore::Core::InternalThreadState *Thread, bool RT) {
Thread->CTX->SignalThread(Thread, RT ? FEXCore::Core::SignalEvent::ReturnRT : FEXCore::Core::SignalEvent::Return);
LOGMAN_MSG_A_FMT("unreachable");
FEX_UNREACHABLE;
@@ -28,7 +28,9 @@ static void SignalReturn(FEXCore::Core::InternalThreadState *Thread) {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(SignalReturn) {
SignalReturn(Data->State);
auto Op = IROp->C<IR::IROp_SignalReturn>();
SignalReturn(Data->State, Op->IsRT);
}
DEF_OP(CallbackReturn) {
@@ -155,6 +155,7 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
REGISTER_OP(LOADMEMTSO, LoadMem);
REGISTER_OP(STOREMEMTSO, StoreMem);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
// Misc ops
@@ -329,7 +330,6 @@ void InterpreterOps::InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::I
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();
@@ -182,6 +182,7 @@ namespace FEXCore::CPU {
DEF_OP(LoadMem);
DEF_OP(StoreMem);
DEF_OP(CacheLineClear);
DEF_OP(CacheLineClean);
DEF_OP(CacheLineZero);
///< Misc ops
@@ -23,6 +23,22 @@ static inline void CacheLineFlush(char *Addr) {
#endif
}
static inline void CacheLineClean(char *Addr) {
#ifdef _M_X86_64
__asm volatile (
"clwb (%[Addr]);"
:: [Addr] "r" (Addr)
: "memory");
#elif _M_ARM_64
__asm volatile (
"dc cvac, %[Addr]"
:: [Addr] "r" (Addr)
: "memory");
#else
LOGMAN_THROW_A_FMT("Unsupported architecture with cacheline clean");
#endif
}
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(LoadContext) {
const auto Op = IROp->C<IR::IROp_LoadContext>();
@@ -281,6 +297,15 @@ DEF_OP(CacheLineClear) {
CacheLineFlush(MemData);
}
DEF_OP(CacheLineClean) {
auto Op = IROp->C<IR::IROp_CacheLineClean>();
char *MemData = *GetSrc<char **>(Data->SSAData, Op->Addr);
// 64-byte cache line clear
CacheLineClean(MemData);
}
DEF_OP(CacheLineZero) {
auto Op = IROp->C<IR::IROp_CacheLineZero>();
@@ -1274,57 +1274,4 @@ DEF_OP(FCmp) {
#undef DEF_OP
void Arm64JITCore::RegisterALUHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
REGISTER_OP(CONSTANT, Constant);
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
REGISTER_OP(INLINECONSTANT, InlineConstant);
REGISTER_OP(INLINEENTRYPOINTOFFSET, InlineEntrypointOffset);
REGISTER_OP(CYCLECOUNTER, CycleCounter);
REGISTER_OP(ADD, Add);
REGISTER_OP(SUB, Sub);
REGISTER_OP(NEG, Neg);
REGISTER_OP(MUL, Mul);
REGISTER_OP(UMUL, UMul);
REGISTER_OP(DIV, Div);
REGISTER_OP(UDIV, UDiv);
REGISTER_OP(REM, Rem);
REGISTER_OP(UREM, URem);
REGISTER_OP(MULH, MulH);
REGISTER_OP(UMULH, UMulH);
REGISTER_OP(OR, Or);
REGISTER_OP(AND, And);
REGISTER_OP(ANDN, Andn);
REGISTER_OP(XOR, Xor);
REGISTER_OP(LSHL, Lshl);
REGISTER_OP(LSHR, Lshr);
REGISTER_OP(ASHR, Ashr);
REGISTER_OP(ROR, Ror);
REGISTER_OP(EXTR, Extr);
REGISTER_OP(PDEP, PDep);
REGISTER_OP(PEXT, PExt);
REGISTER_OP(LDIV, LDiv);
REGISTER_OP(LUDIV, LUDiv);
REGISTER_OP(LREM, LRem);
REGISTER_OP(LUREM, LURem);
REGISTER_OP(NOT, Not);
REGISTER_OP(POPCOUNT, Popcount);
REGISTER_OP(FINDLSB, FindLSB);
REGISTER_OP(FINDMSB, FindMSB);
REGISTER_OP(FINDTRAILINGZEROS, FindTrailingZeros);
REGISTER_OP(COUNTLEADINGZEROES, CountLeadingZeroes);
REGISTER_OP(REV, Rev);
REGISTER_OP(BFI, Bfi);
REGISTER_OP(BFE, Bfe);
REGISTER_OP(SBFE, Sbfe);
REGISTER_OP(SELECT, Select);
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
REGISTER_OP(FCMP, FCmp);
#undef REGISTER_OP
}
}
@@ -438,23 +438,5 @@ DEF_OP(AtomicFetchNeg) {
}
#undef DEF_OP
void Arm64JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
REGISTER_OP(ATOMICSUB, AtomicSub);
REGISTER_OP(ATOMICAND, AtomicAnd);
REGISTER_OP(ATOMICOR, AtomicOr);
REGISTER_OP(ATOMICXOR, AtomicXor);
REGISTER_OP(ATOMICSWAP, AtomicSwap);
REGISTER_OP(ATOMICFETCHADD, AtomicFetchAdd);
REGISTER_OP(ATOMICFETCHSUB, AtomicFetchSub);
REGISTER_OP(ATOMICFETCHAND, AtomicFetchAnd);
REGISTER_OP(ATOMICFETCHOR, AtomicFetchOr);
REGISTER_OP(ATOMICFETCHXOR, AtomicFetchXor);
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
#undef REGISTER_OP
}
}
@@ -21,12 +21,19 @@ namespace FEXCore::CPU {
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
DEF_OP(SignalReturn) {
auto Op = IROp->C<IR::IROp_SignalReturn>();
// First we must reset the stack
ResetStack();
// Now branch to our signal return helper
// This can't be a direct branch since the code needs to live at a constant location
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler));
if (Op->IsRT) {
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandlerRT));
}
else {
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler));
}
br(ARMEmitter::Reg::r0);
}
@@ -451,20 +458,5 @@ DEF_OP(CPUID) {
}
#undef DEF_OP
void Arm64JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(SIGNALRETURN, SignalReturn);
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
REGISTER_OP(EXITFUNCTION, ExitFunction);
REGISTER_OP(JUMP, Jump);
REGISTER_OP(CONDJUMP, CondJump);
REGISTER_OP(SYSCALL, Syscall);
REGISTER_OP(INLINESYSCALL, InlineSyscall);
REGISTER_OP(THUNK, Thunk);
REGISTER_OP(VALIDATECODE, ValidateCode);
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
REGISTER_OP(CPUID, CPUID);
#undef REGISTER_OP
}
}
@@ -365,18 +365,5 @@ DEF_OP(Vector_FToI) {
}
#undef DEF_OP
void Arm64JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
REGISTER_OP(FLOAT_FTOF, Float_FToF);
REGISTER_OP(VECTOR_STOF, Vector_SToF);
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
#undef REGISTER_OP
}
}
@@ -136,16 +136,4 @@ DEF_OP(PCLMUL) {
}
#undef DEF_OP
void Arm64JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
REGISTER_OP(VAESDEC, AESDec);
REGISTER_OP(VAESDECLAST, AESDecLast);
REGISTER_OP(VAESKEYGENASSIST, AESKeyGenAssist);
REGISTER_OP(CRC32, CRC32);
REGISTER_OP(PCLMUL, PCLMUL);
#undef REGISTER_OP
}
}
@@ -14,10 +14,5 @@ DEF_OP(GetHostFlag) {
}
#undef DEF_OP
void Arm64JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
#undef REGISTER_OP
}
}
+252 -20
View File
@@ -163,7 +163,7 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header const *IROp, IR::NodeID Node) {
const auto Src1 = GetReg(IROp->Args[0].ID());
if (Info.ABI == FABI_F80_I16) {
uxth(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, Src1);
sxth(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, Src1);
}
else {
mov(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, Src1);
@@ -310,7 +310,7 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header const *IROp, IR::NodeID Node) {
FillStaticRegs();
const auto Dst = GetReg(Node);
uxth(ARMEmitter::Size::i64Bit, Dst, ARMEmitter::Reg::r0);
sxth(ARMEmitter::Size::i64Bit, Dst, ARMEmitter::Reg::r0);
}
break;
case FABI_I32_F80:{
@@ -535,21 +535,6 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
RAPass->AddRegisterConflict(FEXCore::IR::GPRClass, i * 2 + 1, FEXCore::IR::GPRPairClass, i);
}
for (uint32_t i = 0; i < FEXCore::IR::IROps::OP_LAST + 1; ++i) {
OpHandlers[i] = &Arm64JITCore::Op_Unhandled;
}
RegisterALUHandlers();
RegisterAtomicHandlers();
RegisterBranchHandlers();
RegisterConversionHandlers();
RegisterFlagHandlers();
RegisterMemoryHandlers();
RegisterMiscHandlers();
RegisterMoveHandlers();
RegisterVectorHandlers();
RegisterEncryptionHandlers();
{
// Set up pointers that the JIT needs to load
@@ -769,10 +754,257 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
const auto ID = IR->GetID(CodeNode);
switch (IROp->Op) {
#define REGISTER_OP(op, x) case FEXCore::IR::IROps::OP_##op: Op_##x(IROp, ID); break
// ALU ops
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
REGISTER_OP(CONSTANT, Constant);
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
REGISTER_OP(INLINECONSTANT, InlineConstant);
REGISTER_OP(INLINEENTRYPOINTOFFSET, InlineEntrypointOffset);
REGISTER_OP(CYCLECOUNTER, CycleCounter);
REGISTER_OP(ADD, Add);
REGISTER_OP(SUB, Sub);
REGISTER_OP(NEG, Neg);
REGISTER_OP(MUL, Mul);
REGISTER_OP(UMUL, UMul);
REGISTER_OP(DIV, Div);
REGISTER_OP(UDIV, UDiv);
REGISTER_OP(REM, Rem);
REGISTER_OP(UREM, URem);
REGISTER_OP(MULH, MulH);
REGISTER_OP(UMULH, UMulH);
REGISTER_OP(OR, Or);
REGISTER_OP(AND, And);
REGISTER_OP(ANDN, Andn);
REGISTER_OP(XOR, Xor);
REGISTER_OP(LSHL, Lshl);
REGISTER_OP(LSHR, Lshr);
REGISTER_OP(ASHR, Ashr);
REGISTER_OP(ROR, Ror);
REGISTER_OP(EXTR, Extr);
REGISTER_OP(PDEP, PDep);
REGISTER_OP(PEXT, PExt);
REGISTER_OP(LDIV, LDiv);
REGISTER_OP(LUDIV, LUDiv);
REGISTER_OP(LREM, LRem);
REGISTER_OP(LUREM, LURem);
REGISTER_OP(NOT, Not);
REGISTER_OP(POPCOUNT, Popcount);
REGISTER_OP(FINDLSB, FindLSB);
REGISTER_OP(FINDMSB, FindMSB);
REGISTER_OP(FINDTRAILINGZEROS, FindTrailingZeros);
REGISTER_OP(COUNTLEADINGZEROES, CountLeadingZeroes);
REGISTER_OP(REV, Rev);
REGISTER_OP(BFI, Bfi);
REGISTER_OP(BFE, Bfe);
REGISTER_OP(SBFE, Sbfe);
REGISTER_OP(SELECT, Select);
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
REGISTER_OP(FCMP, FCmp);
// Execute handler
OpHandler Handler = OpHandlers[IROp->Op];
(this->*Handler)(IROp, ID);
// Atomic ops
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
REGISTER_OP(ATOMICSUB, AtomicSub);
REGISTER_OP(ATOMICAND, AtomicAnd);
REGISTER_OP(ATOMICOR, AtomicOr);
REGISTER_OP(ATOMICXOR, AtomicXor);
REGISTER_OP(ATOMICSWAP, AtomicSwap);
REGISTER_OP(ATOMICFETCHADD, AtomicFetchAdd);
REGISTER_OP(ATOMICFETCHSUB, AtomicFetchSub);
REGISTER_OP(ATOMICFETCHAND, AtomicFetchAnd);
REGISTER_OP(ATOMICFETCHOR, AtomicFetchOr);
REGISTER_OP(ATOMICFETCHXOR, AtomicFetchXor);
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
// Branch ops
REGISTER_OP(SIGNALRETURN, SignalReturn);
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
REGISTER_OP(EXITFUNCTION, ExitFunction);
REGISTER_OP(JUMP, Jump);
REGISTER_OP(CONDJUMP, CondJump);
REGISTER_OP(SYSCALL, Syscall);
REGISTER_OP(INLINESYSCALL, InlineSyscall);
REGISTER_OP(THUNK, Thunk);
REGISTER_OP(VALIDATECODE, ValidateCode);
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
REGISTER_OP(CPUID, CPUID);
// Conversion ops
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
REGISTER_OP(FLOAT_FTOF, Float_FToF);
REGISTER_OP(VECTOR_STOF, Vector_SToF);
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
// Encryption ops
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
REGISTER_OP(VAESDEC, AESDec);
REGISTER_OP(VAESDECLAST, AESDecLast);
REGISTER_OP(VAESKEYGENASSIST, AESKeyGenAssist);
REGISTER_OP(CRC32, CRC32);
REGISTER_OP(PCLMUL, PCLMUL);
// Flag ops
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
// Memory ops
REGISTER_OP(LOADCONTEXT, LoadContext);
REGISTER_OP(STORECONTEXT, StoreContext);
REGISTER_OP(LOADREGISTER, LoadRegister);
REGISTER_OP(STOREREGISTER, StoreRegister);
REGISTER_OP(LOADCONTEXTINDEXED, LoadContextIndexed);
REGISTER_OP(STORECONTEXTINDEXED, StoreContextIndexed);
REGISTER_OP(SPILLREGISTER, SpillRegister);
REGISTER_OP(FILLREGISTER, FillRegister);
REGISTER_OP(LOADFLAG, LoadFlag);
REGISTER_OP(STOREFLAG, StoreFlag);
REGISTER_OP(LOADMEM, LoadMem);
REGISTER_OP(STOREMEM, StoreMem);
case FEXCore::IR::IROps::OP_LOADMEMTSO:
if (ParanoidTSO()) {
Op_ParanoidLoadMemTSO(IROp, ID);
}
else {
Op_LoadMemTSO(IROp, ID);
}
break;
case FEXCore::IR::IROps::OP_STOREMEMTSO:
if (ParanoidTSO()) {
Op_ParanoidStoreMemTSO(IROp, ID);
}
else {
Op_StoreMemTSO(IROp, ID);
}
break;
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
// Misc ops
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
REGISTER_OP(BEGINBLOCK, NoOp);
REGISTER_OP(ENDBLOCK, NoOp);
REGISTER_OP(GUESTOPCODE, GuestOpcode);
REGISTER_OP(FENCE, Fence);
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);
REGISTER_OP(PHIVALUE, NoOp);
REGISTER_OP(PRINT, Print);
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
REGISTER_OP(INVALIDATEFLAGS, NoOp);
REGISTER_OP(PROCESSORID, ProcessorID);
REGISTER_OP(RDRAND, RDRAND);
REGISTER_OP(YIELD, Yield);
// Move ops
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
// Vector ops
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(VMOV, VMov);
REGISTER_OP(VAND, VAnd);
REGISTER_OP(VBIC, VBic);
REGISTER_OP(VOR, VOr);
REGISTER_OP(VXOR, VXor);
REGISTER_OP(VADD, VAdd);
REGISTER_OP(VSUB, VSub);
REGISTER_OP(VUQADD, VUQAdd);
REGISTER_OP(VUQSUB, VUQSub);
REGISTER_OP(VSQADD, VSQAdd);
REGISTER_OP(VSQSUB, VSQSub);
REGISTER_OP(VADDP, VAddP);
REGISTER_OP(VADDV, VAddV);
REGISTER_OP(VUMINV, VUMinV);
REGISTER_OP(VURAVG, VURAvg);
REGISTER_OP(VABS, VAbs);
REGISTER_OP(VPOPCOUNT, VPopcount);
REGISTER_OP(VFADD, VFAdd);
REGISTER_OP(VFADDP, VFAddP);
REGISTER_OP(VFSUB, VFSub);
REGISTER_OP(VFMUL, VFMul);
REGISTER_OP(VFDIV, VFDiv);
REGISTER_OP(VFMIN, VFMin);
REGISTER_OP(VFMAX, VFMax);
REGISTER_OP(VFRECP, VFRecp);
REGISTER_OP(VFSQRT, VFSqrt);
REGISTER_OP(VFRSQRT, VFRSqrt);
REGISTER_OP(VNEG, VNeg);
REGISTER_OP(VFNEG, VFNeg);
REGISTER_OP(VNOT, VNot);
REGISTER_OP(VUMIN, VUMin);
REGISTER_OP(VSMIN, VSMin);
REGISTER_OP(VUMAX, VUMax);
REGISTER_OP(VSMAX, VSMax);
REGISTER_OP(VZIP, VZip);
REGISTER_OP(VZIP2, VZip2);
REGISTER_OP(VUNZIP, VUnZip);
REGISTER_OP(VUNZIP2, VUnZip2);
REGISTER_OP(VBSL, VBSL);
REGISTER_OP(VCMPEQ, VCMPEQ);
REGISTER_OP(VCMPEQZ, VCMPEQZ);
REGISTER_OP(VCMPGT, VCMPGT);
REGISTER_OP(VCMPGTZ, VCMPGTZ);
REGISTER_OP(VCMPLTZ, VCMPLTZ);
REGISTER_OP(VFCMPEQ, VFCMPEQ);
REGISTER_OP(VFCMPNEQ, VFCMPNEQ);
REGISTER_OP(VFCMPLT, VFCMPLT);
REGISTER_OP(VFCMPGT, VFCMPGT);
REGISTER_OP(VFCMPLE, VFCMPLE);
REGISTER_OP(VFCMPORD, VFCMPORD);
REGISTER_OP(VFCMPUNO, VFCMPUNO);
REGISTER_OP(VUSHL, VUShl);
REGISTER_OP(VUSHR, VUShr);
REGISTER_OP(VSSHR, VSShr);
REGISTER_OP(VUSHLS, VUShlS);
REGISTER_OP(VUSHRS, VUShrS);
REGISTER_OP(VSSHRS, VSShrS);
REGISTER_OP(VINSELEMENT, VInsElement);
REGISTER_OP(VDUPELEMENT, VDupElement);
REGISTER_OP(VEXTR, VExtr);
REGISTER_OP(VUSHRI, VUShrI);
REGISTER_OP(VSSHRI, VSShrI);
REGISTER_OP(VSHLI, VShlI);
REGISTER_OP(VUSHRNI, VUShrNI);
REGISTER_OP(VUSHRNI2, VUShrNI2);
REGISTER_OP(VSXTL, VSXTL);
REGISTER_OP(VSXTL2, VSXTL2);
REGISTER_OP(VUXTL, VUXTL);
REGISTER_OP(VUXTL2, VUXTL2);
REGISTER_OP(VSQXTN, VSQXTN);
REGISTER_OP(VSQXTN2, VSQXTN2);
REGISTER_OP(VSQXTUN, VSQXTUN);
REGISTER_OP(VSQXTUN2, VSQXTUN2);
REGISTER_OP(VUMUL, VMul);
REGISTER_OP(VSMUL, VMul);
REGISTER_OP(VUMULL, VUMull);
REGISTER_OP(VSMULL, VSMull);
REGISTER_OP(VUMULL2, VUMull2);
REGISTER_OP(VSMULL2, VSMull2);
REGISTER_OP(VUABDL, VUABDL);
REGISTER_OP(VTBL1, VTBL1);
REGISTER_OP(VREV64, VRev64);
#undef REGISTER_OP
default:
Op_Unhandled(IROp, ID);
break;
}
}
if (DebugData) {
+1 -12
View File
@@ -235,18 +235,6 @@ private:
*/
uint8_t *GuestEntry{};
using OpHandler = void (Arm64JITCore::*)(IR::IROp_Header const *IROp, IR::NodeID Node);
std::array<OpHandler, IR::IROps::OP_LAST + 1> OpHandlers {};
void RegisterALUHandlers();
void RegisterAtomicHandlers();
void RegisterBranchHandlers();
void RegisterConversionHandlers();
void RegisterFlagHandlers();
void RegisterMemoryHandlers();
void RegisterMiscHandlers();
void RegisterMoveHandlers();
void RegisterVectorHandlers();
void RegisterEncryptionHandlers();
#define DEF_OP(x) void Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
///< Unhandled handler
@@ -368,6 +356,7 @@ private:
DEF_OP(ParanoidLoadMemTSO);
DEF_OP(ParanoidStoreMemTSO);
DEF_OP(CacheLineClear);
DEF_OP(CacheLineClean);
DEF_OP(CacheLineZero);
///< Misc ops
+117 -42
View File
@@ -703,19 +703,43 @@ DEF_OP(SpillRegister) {
const auto Src = GetReg(Op->Value.ID());
switch (OpSize) {
case 1: {
strb(Src, ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 4095) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
strb(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
strb(Src, ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 2: {
strh(Src, ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 8190) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
strh(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
strh(Src, ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 4: {
str(Src.W(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 16380) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
str(Src.W(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 8: {
str(Src.X(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 32760) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
str(Src.X(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
default:
@@ -727,15 +751,33 @@ DEF_OP(SpillRegister) {
switch (OpSize) {
case 4: {
str(Src.S(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 16380) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
str(Src.S(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 8: {
str(Src.D(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 32760) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
str(Src.D(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 16: {
str(Src.Q(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 65520) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
str(Src.Q(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 32: {
@@ -761,19 +803,43 @@ DEF_OP(FillRegister) {
const auto Dst = GetReg(Node);
switch (OpSize) {
case 1: {
ldrb(Dst, ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 4095) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldrb(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
ldrb(Dst, ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 2: {
ldrh(Dst, ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 8190) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldrh(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
ldrh(Dst, ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 4: {
ldr(Dst.W(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 16380) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
ldr(Dst.W(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 8: {
ldr(Dst.X(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 32760) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
ldr(Dst.X(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
default:
@@ -785,15 +851,33 @@ DEF_OP(FillRegister) {
switch (OpSize) {
case 4: {
ldr(Dst.S(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 16380) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
ldr(Dst.S(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 8: {
ldr(Dst.D(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 32760) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
ldr(Dst.D(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 16: {
ldr(Dst.Q(), ARMEmitter::Reg::rsp, SlotOffset);
if (SlotOffset > 65520) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
else {
ldr(Dst.Q(), ARMEmitter::Reg::rsp, SlotOffset);
}
break;
}
case 32: {
@@ -1409,10 +1493,27 @@ DEF_OP(CacheLineClear) {
// icache doesn't matter here since the guest application shouldn't be calling clflush on JIT code.
mov(TMP1, MemReg.X());
for (size_t i = 0; i < std::max(1U, CTX->HostFeatures.DCacheLineSize / 64U); ++i) {
dc(ARMEmitter::DataCacheOperation::CVAU, TMP1);
dc(ARMEmitter::DataCacheOperation::CIVAC, TMP1);
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, CTX->HostFeatures.DCacheLineSize);
}
if (Op->Serialize) {
// If requested, serialized all of the data cache operations.
dsb(FEXCore::ARMEmitter::BarrierScope::ISH);
}
}
DEF_OP(CacheLineClean) {
auto Op = IROp->C<IR::IROp_CacheLineClean>();
auto MemReg = GetReg(Op->Addr.ID());
// Clean dcache only
mov(TMP1, MemReg.X());
for (size_t i = 0; i < std::max(1U, CTX->HostFeatures.DCacheLineSize / 64U); ++i) {
dc(ARMEmitter::DataCacheOperation::CVAC, TMP1);
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, CTX->HostFeatures.DCacheLineSize);
}
dsb(FEXCore::ARMEmitter::BarrierScope::ISH);
}
DEF_OP(CacheLineZero) {
@@ -1438,31 +1539,5 @@ DEF_OP(CacheLineZero) {
}
#undef DEF_OP
void Arm64JITCore::RegisterMemoryHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(LOADCONTEXT, LoadContext);
REGISTER_OP(STORECONTEXT, StoreContext);
REGISTER_OP(LOADREGISTER, LoadRegister);
REGISTER_OP(STOREREGISTER, StoreRegister);
REGISTER_OP(LOADCONTEXTINDEXED, LoadContextIndexed);
REGISTER_OP(STORECONTEXTINDEXED, StoreContextIndexed);
REGISTER_OP(SPILLREGISTER, SpillRegister);
REGISTER_OP(FILLREGISTER, FillRegister);
REGISTER_OP(LOADFLAG, LoadFlag);
REGISTER_OP(STOREFLAG, StoreFlag);
REGISTER_OP(LOADMEM, LoadMem);
REGISTER_OP(STOREMEM, StoreMem);
if (ParanoidTSO()) {
REGISTER_OP(LOADMEMTSO, ParanoidLoadMemTSO);
REGISTER_OP(STOREMEMTSO, ParanoidStoreMemTSO);
}
else {
REGISTER_OP(LOADMEMTSO, LoadMemTSO);
REGISTER_OP(STOREMEMTSO, StoreMemTSO);
}
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
#undef REGISTER_OP
}
}
@@ -231,27 +231,5 @@ DEF_OP(Yield) {
}
#undef DEF_OP
void Arm64JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
REGISTER_OP(BEGINBLOCK, NoOp);
REGISTER_OP(ENDBLOCK, NoOp);
REGISTER_OP(GUESTOPCODE, GuestOpcode);
REGISTER_OP(FENCE, Fence);
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);
REGISTER_OP(PHIVALUE, NoOp);
REGISTER_OP(PRINT, Print);
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
REGISTER_OP(INVALIDATEFLAGS, NoOp);
REGISTER_OP(PROCESSORID, ProcessorID);
REGISTER_OP(RDRAND, RDRAND);
REGISTER_OP(YIELD, Yield);
#undef REGISTER_OP
}
}
@@ -42,11 +42,5 @@ DEF_OP(CreateElementPair) {
}
#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);
#undef REGISTER_OP
}
}
@@ -2020,7 +2020,44 @@ DEF_OP(VUShr) {
}
DEF_OP(VSShr) {
LOGMAN_MSG_A_FMT("Unimplemented");
const auto Op = IROp->C<IR::IROp_VSShr>();
const auto OpSize = IROp->Size;
const auto ElementSize = IROp->ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto MaxShift = (ElementSize * 8) - 1;
const auto Dst = GetVReg(Node);
const auto ShiftVector = GetVReg(Op->ShiftVector.ID());
const auto Vector = GetVReg(Op->Vector.ID());
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
const auto SubRegSize =
ElementSize == 1 ? ARMEmitter::SubRegSize::i8Bit :
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Merging();
dup_imm(SubRegSize, VTMP2.Z(), MaxShift);
umin(SubRegSize, VTMP2.Z(), Mask, VTMP2.Z(), ShiftVector.Z());
movprfx(VTMP1.Z(), Vector.Z());
asr(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
mov(Dst.Z(), VTMP1.Z());
} else {
LOGMAN_THROW_AA_FMT(ElementSize != 8, "Adv. SIMD UMIN doesn't handle 64-bit values");
movi(SubRegSize, VTMP1.Q(), MaxShift);
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
// Need to invert shift values to perform a right shift with SSHL
// (SSHR only has an immediate variant).
neg(SubRegSize, VTMP1.Q(), VTMP1.Q());
sshl(SubRegSize, Dst.Q(), Vector.Q(), VTMP1.Q());
}
}
DEF_OP(VUShlS) {
@@ -2935,93 +2972,5 @@ DEF_OP(VRev64) {
}
#undef DEF_OP
void Arm64JITCore::RegisterVectorHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(VMOV, VMov);
REGISTER_OP(VAND, VAnd);
REGISTER_OP(VBIC, VBic);
REGISTER_OP(VOR, VOr);
REGISTER_OP(VXOR, VXor);
REGISTER_OP(VADD, VAdd);
REGISTER_OP(VSUB, VSub);
REGISTER_OP(VUQADD, VUQAdd);
REGISTER_OP(VUQSUB, VUQSub);
REGISTER_OP(VSQADD, VSQAdd);
REGISTER_OP(VSQSUB, VSQSub);
REGISTER_OP(VADDP, VAddP);
REGISTER_OP(VADDV, VAddV);
REGISTER_OP(VUMINV, VUMinV);
REGISTER_OP(VURAVG, VURAvg);
REGISTER_OP(VABS, VAbs);
REGISTER_OP(VPOPCOUNT, VPopcount);
REGISTER_OP(VFADD, VFAdd);
REGISTER_OP(VFADDP, VFAddP);
REGISTER_OP(VFSUB, VFSub);
REGISTER_OP(VFMUL, VFMul);
REGISTER_OP(VFDIV, VFDiv);
REGISTER_OP(VFMIN, VFMin);
REGISTER_OP(VFMAX, VFMax);
REGISTER_OP(VFRECP, VFRecp);
REGISTER_OP(VFSQRT, VFSqrt);
REGISTER_OP(VFRSQRT, VFRSqrt);
REGISTER_OP(VNEG, VNeg);
REGISTER_OP(VFNEG, VFNeg);
REGISTER_OP(VNOT, VNot);
REGISTER_OP(VUMIN, VUMin);
REGISTER_OP(VSMIN, VSMin);
REGISTER_OP(VUMAX, VUMax);
REGISTER_OP(VSMAX, VSMax);
REGISTER_OP(VZIP, VZip);
REGISTER_OP(VZIP2, VZip2);
REGISTER_OP(VUNZIP, VUnZip);
REGISTER_OP(VUNZIP2, VUnZip2);
REGISTER_OP(VBSL, VBSL);
REGISTER_OP(VCMPEQ, VCMPEQ);
REGISTER_OP(VCMPEQZ, VCMPEQZ);
REGISTER_OP(VCMPGT, VCMPGT);
REGISTER_OP(VCMPGTZ, VCMPGTZ);
REGISTER_OP(VCMPLTZ, VCMPLTZ);
REGISTER_OP(VFCMPEQ, VFCMPEQ);
REGISTER_OP(VFCMPNEQ, VFCMPNEQ);
REGISTER_OP(VFCMPLT, VFCMPLT);
REGISTER_OP(VFCMPGT, VFCMPGT);
REGISTER_OP(VFCMPLE, VFCMPLE);
REGISTER_OP(VFCMPORD, VFCMPORD);
REGISTER_OP(VFCMPUNO, VFCMPUNO);
REGISTER_OP(VUSHL, VUShl);
REGISTER_OP(VUSHR, VUShr);
REGISTER_OP(VSSHR, VSShr);
REGISTER_OP(VUSHLS, VUShlS);
REGISTER_OP(VUSHRS, VUShrS);
REGISTER_OP(VSSHRS, VSShrS);
REGISTER_OP(VINSELEMENT, VInsElement);
REGISTER_OP(VDUPELEMENT, VDupElement);
REGISTER_OP(VEXTR, VExtr);
REGISTER_OP(VUSHRI, VUShrI);
REGISTER_OP(VSSHRI, VSShrI);
REGISTER_OP(VSHLI, VShlI);
REGISTER_OP(VUSHRNI, VUShrNI);
REGISTER_OP(VUSHRNI2, VUShrNI2);
REGISTER_OP(VSXTL, VSXTL);
REGISTER_OP(VSXTL2, VSXTL2);
REGISTER_OP(VUXTL, VUXTL);
REGISTER_OP(VUXTL2, VUXTL2);
REGISTER_OP(VSQXTN, VSQXTN);
REGISTER_OP(VSQXTN2, VSQXTN2);
REGISTER_OP(VSQXTUN, VSQXTUN);
REGISTER_OP(VSQXTUN2, VSQXTUN2);
REGISTER_OP(VUMUL, VMul);
REGISTER_OP(VSMUL, VMul);
REGISTER_OP(VUMULL, VUMull);
REGISTER_OP(VSMULL, VSMull);
REGISTER_OP(VUMULL2, VUMull2);
REGISTER_OP(VSMULL2, VSMull2);
REGISTER_OP(VUABDL, VUABDL);
REGISTER_OP(VTBL1, VTBL1);
REGISTER_OP(VREV64, VRev64);
#undef REGISTER_OP
}
}
@@ -31,12 +31,19 @@ namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(SignalReturn) {
auto Op = IROp->C<IR::IROp_SignalReturn>();
// Adjust the stack first for a regular return
if (SpillSlots) {
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
}
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler)]);
if (Op->IsRT) {
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandlerRT)]);
}
else {
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler)]);
}
}
DEF_OP(CallbackReturn) {
+7 -2
View File
@@ -147,7 +147,12 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
case FABI_F80_I32: {
PushRegs();
mov(edi, GetSrc<RA_32>(IROp->Args[0].ID()));
if (Info.ABI == FABI_F80_I16) {
movsx(rdi, GetSrc<RA_32>(IROp->Args[0].ID()).cvt16());
}
else {
mov(edi, GetSrc<RA_32>(IROp->Args[0].ID()));
}
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
@@ -223,7 +228,7 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
PopRegs();
movzx(GetDst<RA_64>(Node), ax);
movsx(GetDst<RA_64>(Node), ax);
}
break;
case FABI_I32_F80:{
@@ -348,6 +348,7 @@ private:
DEF_OP(LoadMem);
DEF_OP(StoreMem);
DEF_OP(CacheLineClear);
DEF_OP(CacheLineClean);
DEF_OP(CacheLineZero);
///< Misc ops
@@ -771,7 +771,19 @@ DEF_OP(CacheLineClear) {
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
clflush(ptr [MemReg]);
if (Op->Serialize) {
clflush(ptr [MemReg]);
}
else {
clflushopt(ptr [MemReg]);
}
}
DEF_OP(CacheLineClean) {
auto Op = IROp->C<IR::IROp_CacheLineClean>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
clwb(ptr [MemReg]);
}
DEF_OP(CacheLineZero) {
@@ -809,6 +821,7 @@ void X86JITCore::RegisterMemoryHandlers() {
REGISTER_OP(LOADMEMTSO, LoadMem);
REGISTER_OP(STOREMEMTSO, StoreMem);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
#undef REGISTER_OP
}
@@ -2549,7 +2549,22 @@ DEF_OP(VUShr) {
}
DEF_OP(VSShr) {
LOGMAN_MSG_A_FMT("Unimplemented");
const auto Op = IROp->C<IR::IROp_VSShr>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = IROp->ElementSize;
LOGMAN_THROW_AA_FMT(ElementSize == 4, "VSShr only supports 32-bit elements");
const auto Dst = GetDst(Node);
const auto ShiftVector = GetSrc(Op->ShiftVector.ID());
const auto Vector = GetSrc(Op->Vector.ID());
if (Is256Bit) {
vpsravd(ToYMM(Dst), ToYMM(Vector), ToYMM(ShiftVector));
} else {
vpsravd(Dst, Vector, ShiftVector);
}
}
DEF_OP(VUShlS) {
+80 -54
View File
@@ -278,9 +278,11 @@ void OpDispatchBuilder::IRETOp(OpcodeArgs) {
}
void OpDispatchBuilder::SIGRETOp(OpcodeArgs) {
uint8_t Literal = Op->Src[0].Data.Literal.Value;
const uint8_t GPRSize = CTX->GetGPRSize();
// Store the new RIP
_SignalReturn();
bool IsRT = CTX->Config.Is64BitMode() || Literal;
_SignalReturn(IsRT);
auto NewRIP = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, rip));
// This ExitFunction won't actually get hit but needs to exist
_ExitFunction(NewRIP);
@@ -1613,17 +1615,17 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
OrderedNode *Src = LoadSource_WithOpSize(GPRClass, Op, Op->Src[0], 2, Op->Flags, -1);
switch (Op->Dest.Data.GPR.GPR) {
case 0: // ES
case FEXCore::X86State::REG_RAX: // ES
case FEXCore::X86State::REG_R8: // ES
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, es_idx));
UpdatePrefixFromSegment(Src, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX);
break;
case 1: // DS
case FEXCore::X86State::REG_RBX: // DS
case FEXCore::X86State::REG_R11: // DS
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, ds_idx));
UpdatePrefixFromSegment(Src, FEXCore::X86Tables::DecodeFlags::FLAG_DS_PREFIX);
break;
case 2: // CS
case FEXCore::X86State::REG_RCX: // CS
case FEXCore::X86State::REG_R9: // CS
// CPL3 can't write to this
_Break(FEXCore::IR::BreakDefinition {
@@ -1633,12 +1635,12 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
.si_code = 0,
});
break;
case 3: // SS
case FEXCore::X86State::REG_RDX: // SS
case FEXCore::X86State::REG_R10: // SS
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, ss_idx));
UpdatePrefixFromSegment(Src, FEXCore::X86Tables::DecodeFlags::FLAG_SS_PREFIX);
break;
case 6: // GS
case FEXCore::X86State::REG_RBP: // GS
case FEXCore::X86State::REG_R13: // GS
if (!CTX->Config.Is64BitMode) {
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, gs_idx));
@@ -1648,7 +1650,7 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
DecodeFailure = true;
}
break;
case 7: // FS
case FEXCore::X86State::REG_RSP: // FS
case FEXCore::X86State::REG_R12: // FS
if (!CTX->Config.Is64BitMode) {
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, fs_idx));
@@ -1668,23 +1670,23 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
OrderedNode *Segment{};
switch (Op->Src[0].Data.GPR.GPR) {
case 0: // ES
case FEXCore::X86State::REG_RAX: // ES
case FEXCore::X86State::REG_R8: // ES
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, es_idx));
break;
case 1: // DS
case FEXCore::X86State::REG_RBX: // DS
case FEXCore::X86State::REG_R11: // DS
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, ds_idx));
break;
case 2: // CS
case FEXCore::X86State::REG_RCX: // CS
case FEXCore::X86State::REG_R9: // CS
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, cs_idx));
break;
case 3: // SS
case FEXCore::X86State::REG_RDX: // SS
case FEXCore::X86State::REG_R10: // SS
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, ss_idx));
break;
case 6: // GS
case FEXCore::X86State::REG_RBP: // GS
case FEXCore::X86State::REG_R13: // GS
if (CTX->Config.Is64BitMode) {
Segment = _Constant(0);
@@ -1693,7 +1695,7 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, gs_idx));
}
break;
case 7: // FS
case FEXCore::X86State::REG_RSP: // FS
case FEXCore::X86State::REG_R12: // FS
if (CTX->Config.Is64BitMode) {
Segment = _Constant(0);
@@ -4959,12 +4961,8 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
Core::CPUState::XMM_AVX_REG_SIZE :
Core::CPUState::XMM_SSE_REG_SIZE;
const auto VectorOffset = CTX->HostFeatures.SupportsAVX ?
offsetof(Core::CPUState, xmm.avx.data[gprIndex][0]) :
offsetof(Core::CPUState, xmm.sse.data[gprIndex][0]);
// Load the full register size if it is a XMM register source.
Src = _LoadRegister(false, VectorOffset, FPRClass, FPRFixedClass, regSize);
Src = LoadXMMRegister(gprIndex);
// If we are wanting a high-index then we need to extract an element from the upper half of the reg.
// We can only extract an element size here.
@@ -4982,11 +4980,11 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
}
}
else {
Src = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[gpr]) + (highIndex ? 1 : 0), GPRClass, GPRFixedClass, OpSize);
Src = LoadGPRRegister(gpr, OpSize, highIndex ? 8 : 0);
}
}
else if (Operand.IsGPRDirect()) {
Src = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPR.GPR]), GPRClass, GPRFixedClass, GPRSize);
Src = LoadGPRRegister(Operand.Data.GPR.GPR, GPRSize);
LoadableType = true;
if (Operand.Data.GPR.GPR == FEXCore::X86State::REG_RSP && AccessType == MemoryAccessType::ACCESS_DEFAULT) {
@@ -4994,7 +4992,7 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
}
}
else if (Operand.IsGPRIndirect()) {
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPRIndirect.GPR]), GPRClass, GPRFixedClass, GPRSize);
auto GPR = LoadGPRRegister(Operand.Data.GPRIndirect.GPR, GPRSize);
auto Constant = _Constant(GPRSize * 8, Operand.Data.GPRIndirect.Displacement);
@@ -5019,7 +5017,7 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
else if (Operand.IsSIB()) {
OrderedNode *Tmp {};
if (Operand.Data.SIB.Index != FEXCore::X86State::REG_INVALID) {
Tmp = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Index]), GPRClass, GPRFixedClass, GPRSize);
Tmp = LoadGPRRegister(Operand.Data.SIB.Index, GPRSize);
if (Operand.Data.SIB.Scale != 1) {
auto Constant = _Constant(GPRSize * 8, Operand.Data.SIB.Scale);
@@ -5031,7 +5029,7 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
}
if (Operand.Data.SIB.Base != FEXCore::X86State::REG_INVALID) {
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Base]), GPRClass, GPRFixedClass, GPRSize);
auto GPR = LoadGPRRegister(Operand.Data.SIB.Base, GPRSize);
if (Tmp != nullptr) {
Tmp = _Add(Tmp, GPR);
@@ -5096,9 +5094,12 @@ OrderedNode *OpDispatchBuilder::GetRelocatedPC(FEXCore::X86Tables::DecodedOp con
OrderedNode *OpDispatchBuilder::LoadGPRRegister(uint32_t GPR, int8_t Size, uint8_t Offset) {
const uint8_t GPRSize = CTX->GetGPRSize();
if (Size == -1) {
Size = GPRSize;
}
OrderedNode *Reg = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
if (Size != -1 || Offset != 0) {
if (Size != GPRSize || Offset != 0) {
// Extract the subregister if requested.
Reg = _Bfe(Size, Size * 8, Offset, Reg);
}
@@ -5117,15 +5118,18 @@ OrderedNode *OpDispatchBuilder::LoadXMMRegister(uint32_t XMM) {
void OpDispatchBuilder::StoreGPRRegister(uint32_t GPR, OrderedNode *const Src, int8_t Size, uint8_t Offset) {
const uint8_t GPRSize = CTX->GetGPRSize();
if (Size != -1 || Offset != 0) {
if (Size == -1) {
Size = GPRSize;
}
OrderedNode *Reg = Src;
if (Size != GPRSize || Offset != 0) {
// Need to do an insert if not automatic size or zero offset.
OrderedNode *Reg = LoadGPRRegister(GPR);
Reg = LoadGPRRegister(GPR);
Reg = _Bfi(GPRSize, Size * 8, Offset, Reg, Src);
_StoreRegister(Reg, false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
}
else {
_StoreRegister(Src, false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
}
_StoreRegister(Reg, false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
}
void OpDispatchBuilder::StoreXMMRegister(uint32_t XMM, OrderedNode *const Src) {
@@ -5172,42 +5176,36 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
const auto highIndex = Operand.Data.GPR.HighBits ? 1 : 0;
const auto VectorSize = CTX->HostFeatures.SupportsAVX ? 32 : 16;
const auto VectorOffset = CTX->HostFeatures.SupportsAVX ?
offsetof(Core::CPUState, xmm.avx.data[gprIndex][highIndex]) :
offsetof(Core::CPUState, xmm.sse.data[gprIndex][highIndex]);
auto Result = Src;
if (highIndex || OpSize != VectorSize) {
auto InsertResult = Src;
// Partial writes can come from GPR or FPR.
// TODO: Fix the instructions doing partial writes rather than dealing with it here.
auto SrcVector = _LoadRegister(false, VectorOffset, FPRClass, FPRFixedClass, OpSize);
auto SrcVector = LoadXMMRegister(gprIndex);
if (Class == IR::GPRClass) {
InsertResult = _VInsGPR(VectorSize, OpSize, highIndex, SrcVector, Src);
Result = _VInsGPR(VectorSize, OpSize, highIndex, SrcVector, Src);
}
else {
// OpSize of 16 is special in that it is expected to zero the upper bits of the 256-bit operation.
// TODO: Longer term we should enforce the difference between zero and insert.
if (VectorSize == Core::CPUState::XMM_AVX_REG_SIZE && OpSize == Core::CPUState::XMM_SSE_REG_SIZE) {
InsertResult = _VMov(OpSize, Src);
Result = _VMov(OpSize, Src);
}
else {
InsertResult = _VInsElement(VectorSize, OpSize, highIndex, 0, SrcVector, Src);
Result = _VInsElement(VectorSize, OpSize, highIndex, 0, SrcVector, Src);
}
}
}
_StoreRegister(InsertResult, false, VectorOffset, FPRClass, FPRFixedClass, VectorSize);
}
else {
_StoreRegister(Src, false, VectorOffset, FPRClass, FPRFixedClass, VectorSize);
}
StoreXMMRegister(gprIndex, Result);
}
else {
if (GPRSize == 8 && OpSize == 4) {
// If the Source IR op is 64 bits, we need to zext the upper bits
// For all other sizes, the upper bits are guaranteed to already be zero
OrderedNode *Value = GetOpSize(Src) == 8 ? _Bfe(4, 32, 0, Src) : Src;
_StoreRegister(Value, false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, GPRSize);
StoreGPRRegister(gpr, Value, GPRSize);
LOGMAN_THROW_AA_FMT(!Operand.Data.GPR.HighBits, "Can't handle 32bit store to high 8bit register");
}
@@ -5220,25 +5218,24 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
// mov al, 2 ; Move in to lower 8-bits.
// mov ah, 2 ; Move in to upper 8-bits of 16-bit reg.
// mov ax, 2 ; Move in to lower 16-bits of reg.
auto RegDest = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, GPRSize);
auto Result = _Bfi(GPRSize, OpSize * 8, Operand.Data.GPR.HighBits * 8, RegDest, Src);
_StoreRegister(Result, false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, GPRSize);
StoreGPRRegister(gpr, Src, OpSize, Operand.Data.GPR.HighBits * 8);
}
else {
_StoreRegister(Src, false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, std::min(GPRSize, OpSize));
StoreGPRRegister(gpr, Src, std::min(GPRSize, OpSize));
}
}
}
}
else if (Operand.IsGPRDirect()) {
MemStoreDst = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPR.GPR]), GPRClass, GPRFixedClass, GPRSize);
MemStoreDst = LoadGPRRegister(Operand.Data.GPR.GPR, GPRSize);
MemStore = true;
if (Operand.Data.GPR.GPR == FEXCore::X86State::REG_RSP && AccessType == MemoryAccessType::ACCESS_DEFAULT) {
AccessType = MemoryAccessType::ACCESS_NONTSO;
}
}
else if (Operand.IsGPRIndirect()) {
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPRIndirect.GPR]), GPRClass, GPRFixedClass, GPRSize);
auto GPR = LoadGPRRegister(Operand.Data.GPRIndirect.GPR, GPRSize);
auto Constant = _Constant(GPRSize * 8, Operand.Data.GPRIndirect.Displacement);
MemStoreDst = _Add(GPR, Constant);
@@ -5260,7 +5257,7 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
else if (Operand.IsSIB()) {
OrderedNode *Tmp {};
if (Operand.Data.SIB.Index != FEXCore::X86State::REG_INVALID) {
Tmp = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Index]), GPRClass, GPRFixedClass, GPRSize);
Tmp = LoadGPRRegister(Operand.Data.SIB.Index, GPRSize);
if (Operand.Data.SIB.Scale != 1) {
auto Constant = _Constant(GPRSize * 8, Operand.Data.SIB.Scale);
@@ -5269,7 +5266,7 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
}
if (Operand.Data.SIB.Base != FEXCore::X86State::REG_INVALID) {
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Base]), GPRClass, GPRFixedClass, GPRSize);
auto GPR = LoadGPRRegister(Operand.Data.SIB.Base, GPRSize);
if (Tmp != nullptr) {
Tmp = _Add(Tmp, GPR);
@@ -5623,6 +5620,29 @@ void OpDispatchBuilder::FenceOp(OpcodeArgs) {
_Fence({FenceType});
}
void OpDispatchBuilder::CLWB(OpcodeArgs) {
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
_CacheLineClean(DestMem);
}
void OpDispatchBuilder::CLFLUSHOPT(OpcodeArgs) {
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
_CacheLineClear(DestMem, false);
}
void OpDispatchBuilder::MemFenceOrXSAVEOPT(OpcodeArgs) {
if (Op->ModRM == 0xF0) {
// 0xF0 is MFENCE
_Fence(FEXCore::IR::Fence_LoadStore);
}
else {
LogMan::Msg::EFmt("Application tried using XSAVEOPT");
UnimplementedOp(Op);
}
}
void OpDispatchBuilder::StoreFenceOrCLFlush(OpcodeArgs) {
if (Op->ModRM == 0xF8) {
// 0xF8 is SFENCE
@@ -5632,7 +5652,7 @@ void OpDispatchBuilder::StoreFenceOrCLFlush(OpcodeArgs) {
// This is a CLFlush
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
_CacheLineClear(DestMem);
_CacheLineClear(DestMem, true);
}
}
@@ -6054,6 +6074,7 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(2, 0b01, 0x40), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VSMUL, 4>},
{OPD(2, 0b01, 0x41), 1, &OpDispatchBuilder::VPHMINPOSUWOp},
{OPD(2, 0b01, 0x46), 1, &OpDispatchBuilder::VPSRAVDOp},
{OPD(2, 0b01, 0x58), 1, &OpDispatchBuilder::VBROADCASTOp<4>},
{OPD(2, 0b01, 0x59), 1, &OpDispatchBuilder::VBROADCASTOp<8>},
@@ -6070,6 +6091,7 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(3, 0b01, 0x00), 1, &OpDispatchBuilder::VPERMQOp},
{OPD(3, 0b01, 0x01), 1, &OpDispatchBuilder::VPERMQOp},
{OPD(3, 0b01, 0x02), 1, &OpDispatchBuilder::VPBLENDDOp},
{OPD(3, 0b01, 0x04), 1, &OpDispatchBuilder::VPERMILImmOp<4>},
{OPD(3, 0b01, 0x05), 1, &OpDispatchBuilder::VPERMILImmOp<8>},
{OPD(3, 0b01, 0x06), 1, &OpDispatchBuilder::VPERM2Op},
@@ -6077,6 +6099,7 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(3, 0b01, 0x09), 1, &OpDispatchBuilder::AVXVectorRound<8, false>},
{OPD(3, 0b01, 0x0A), 1, &OpDispatchBuilder::AVXVectorRound<4, true>},
{OPD(3, 0b01, 0x0B), 1, &OpDispatchBuilder::AVXVectorRound<8, true>},
{OPD(3, 0b01, 0x0C), 1, &OpDispatchBuilder::VPBLENDDOp},
{OPD(3, 0b01, 0x14), 1, &OpDispatchBuilder::PExtrOp<1>},
{OPD(3, 0b01, 0x15), 1, &OpDispatchBuilder::PExtrOp<2>},
@@ -6770,12 +6793,15 @@ constexpr uint16_t PF_F2 = 3;
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 2), 1, &OpDispatchBuilder::LDMXCSR},
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 3), 1, &OpDispatchBuilder::STMXCSR},
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 5), 1, &OpDispatchBuilder::FenceOp<FEXCore::IR::Fence_Load.Val>}, //LFENCE
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 6), 1, &OpDispatchBuilder::FenceOp<FEXCore::IR::Fence_LoadStore.Val>}, //MFENCE
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 6), 1, &OpDispatchBuilder::MemFenceOrXSAVEOPT}, //MFENCE
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 7), 1, &OpDispatchBuilder::StoreFenceOrCLFlush}, //SFENCE
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 5), 1, &OpDispatchBuilder::UnimplementedOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 6), 1, &OpDispatchBuilder::UnimplementedOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_66, 6), 1, &OpDispatchBuilder::CLWB},
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_66, 7), 1, &OpDispatchBuilder::CLFLUSHOPT},
// GROUP 16
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_NONE, 0), 8, &OpDispatchBuilder::NOPOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F3, 0), 8, &OpDispatchBuilder::NOPOp},
@@ -467,6 +467,8 @@ public:
template <size_t ElementSize>
void VPACKUSOp(OpcodeArgs);
void VPBLENDDOp(OpcodeArgs);
void VPERM2Op(OpcodeArgs);
void VPERMQOp(OpcodeArgs);
@@ -498,6 +500,8 @@ public:
template <size_t ElementSize>
void VPSRAIOp(OpcodeArgs);
void VPSRAVDOp(OpcodeArgs);
template <size_t ElementSize>
void VPSRLDOp(OpcodeArgs);
void VPSRLDQOp(OpcodeArgs);
@@ -689,6 +693,9 @@ public:
template<uint8_t FenceType>
void FenceOp(OpcodeArgs);
void CLWB(OpcodeArgs);
void CLFLUSHOPT(OpcodeArgs);
void MemFenceOrXSAVEOPT(OpcodeArgs);
void StoreFenceOrCLFlush(OpcodeArgs);
void CLZeroOp(OpcodeArgs);
void RDTSCPOp(OpcodeArgs);
@@ -1705,6 +1705,20 @@ void OpDispatchBuilder::VPSRAIOp<2>(OpcodeArgs);
template
void OpDispatchBuilder::VPSRAIOp<4>(OpcodeArgs);
void OpDispatchBuilder::VPSRAVDOp(OpcodeArgs) {
const auto SrcSize = GetSrcSize(Op);
const auto Is128Bit = SrcSize == Core::CPUState::XMM_SSE_REG_SIZE;
OrderedNode *Vector = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *ShiftVector = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
OrderedNode *Result = _VSShr(SrcSize, 4, Vector, ShiftVector);
if (Is128Bit) {
Result = _VMov(16, Result);
}
StoreResult(FPRClass, Op, Result, -1);
}
void OpDispatchBuilder::MOVDDUPOp(OpcodeArgs) {
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Res = _VDupElement(16, GetSrcSize(Op), Src, 0);
@@ -3742,6 +3756,56 @@ void OpDispatchBuilder::VPERMQOp(OpcodeArgs) {
StoreResult(FPRClass, Op, Result, -1);
}
void OpDispatchBuilder::VPBLENDDOp(OpcodeArgs) {
const auto DstSize = GetDstSize(Op);
const auto Is256Bit = DstSize == Core::CPUState::XMM_AVX_REG_SIZE;
OrderedNode *Src1 = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Src2 = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
LOGMAN_THROW_A_FMT(Op->Src[2].IsLiteral(), "Src[2] needs to be literal here");
const auto Selector = Op->Src[2].Data.Literal.Value;
// Each bit in the selector chooses between Src1 and Src2.
// If a bit is set, then we select it's corresponding 32-bit element from Src2
// If a bit is not set, then we select it's corresponding 32-bit element from Src1
// Cases where we can exit out early, since the selector is indicating a copy
// of an entire input vector. Unlikely to occur, since it's slower than
// just an equivalent vector move instruction. but just in case something
// silly is happening, we have your back.
if (Selector == 0) {
OrderedNode *Result = Is256Bit ? Src1 : _VMov(16, Src1);
StoreResult(FPRClass, Op, Result, -1);
return;
}
if (Selector == 0xFF && Is256Bit) {
StoreResult(FPRClass, Op, Src2, -1);
return;
}
// The only bits we care about from the 8-bit immediate for 128-bit operations
// are the first four bits. We do a bitwise check here to catch cases where
// silliness is going on and the upper bits are being set even when they'll
// be ignored
if ((Selector & 0xF) == 0xF && !Is256Bit) {
StoreResult(FPRClass, Op, _VMov(16, Src2), -1);
return;
}
const std::array Sources{Src1, Src2};
OrderedNode *Result = _VectorZero(DstSize);
const int Num32BitElements = DstSize / 4;
for (int i = 0; i < Num32BitElements; i++) {
const auto SelectorIndex = (Selector >> i) & 1;
Result = _VInsElement(DstSize, 4, i, i, Result, Sources[SelectorIndex]);
}
StoreResult(FPRClass, Op, Result, -1);
}
void OpDispatchBuilder::VZEROOp(OpcodeArgs) {
const auto DstSize = GetDstSize(Op);
const auto IsVZEROALL = DstSize == Core::CPUState::XMM_AVX_REG_SIZE;
@@ -39,7 +39,7 @@ class OrderedNode;
//FST(register to register)
// State loading duplicated from X87.cpp, setting host rounding mode
// See issue
// See issue
void OpDispatchBuilder::FNINITF64(OpcodeArgs) {
// Init FCW to 0x037F
auto NewFCW = _Constant(16, 0x037F);
@@ -76,7 +76,7 @@ void OpDispatchBuilder::X87LDENVF64(OpcodeArgs) {
roundingMode = _And(roundingMode, roundMask);
_SetRoundingMode(roundingMode);
_F80LoadFCW(NewFCW);
_StoreContext(2, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
OrderedNode *MemLocation = _Add(Mem, _Constant(Size * 1));
@@ -184,7 +184,7 @@ void OpDispatchBuilder::FBLDF64(OpcodeArgs) {
void OpDispatchBuilder::FBSTPF64(OpcodeArgs) {
auto orig_top = GetX87Top();
auto data = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
OrderedNode *converted = _F80CVTTo(data, 8);
converted = _F80BCDStore(converted);
@@ -256,7 +256,7 @@ void OpDispatchBuilder::FSTF64(OpcodeArgs) {
//Convert to 80-bit float
auto result = _F80CVTTo(data, 8);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, result, 10, 1);
}
}
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
@@ -315,7 +315,10 @@ void OpDispatchBuilder::FADDF64(OpcodeArgs) {
// Memory arg
if constexpr (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FromGPR_S(8, 8, arg);
if(width == 16) {
arg = _Sext(16, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if constexpr (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FToF(8, 4, arg);
@@ -373,7 +376,10 @@ void OpDispatchBuilder::FMULF64(OpcodeArgs) {
// Memory arg
if constexpr (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FromGPR_S(8, 8, arg);
if(width == 16) {
arg = _Sext(16, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if constexpr (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FToF(8, 4, arg);
@@ -434,7 +440,10 @@ void OpDispatchBuilder::FDIVF64(OpcodeArgs) {
// Memory arg
if constexpr (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FromGPR_S(8, 8, arg);
if(width == 16) {
arg = _Sext(16, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if constexpr (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FToF(8, 4, arg);
@@ -517,7 +526,10 @@ void OpDispatchBuilder::FSUBF64(OpcodeArgs) {
// Memory arg
if constexpr (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FromGPR_S(8, 8, arg);
if(width == 16) {
arg = _Sext(16, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if constexpr (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FToF(8, 4, arg);
@@ -676,7 +688,10 @@ void OpDispatchBuilder::FCOMIF64(OpcodeArgs) {
// Memory arg
if constexpr (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FromGPR_S(8, 8, arg);
if(width == 16) {
arg = _Sext(16, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if constexpr (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
b = _Float_FToF(8, 4, arg);
@@ -700,7 +715,7 @@ void OpDispatchBuilder::FCOMIF64(OpcodeArgs) {
OrderedNode *HostFlag_CF = _GetHostFlag(Res, FCMP_FLAG_LT);
OrderedNode *HostFlag_ZF = _GetHostFlag(Res, FCMP_FLAG_EQ);
OrderedNode *HostFlag_Unordered = _GetHostFlag(Res, FCMP_FLAG_UNORDERED);
HostFlag_CF = _Or(HostFlag_CF, HostFlag_Unordered);
HostFlag_ZF = _Or(HostFlag_ZF, HostFlag_Unordered);
@@ -810,8 +825,8 @@ void OpDispatchBuilder::X87BinaryOpF64(OpcodeArgs) {
// Overwrite the op
result.first->Header.Op = IROp;
if constexpr (IROp == IR::OP_F80FPREM ||
IROp == IR::OP_F80FPREM1) {
if constexpr (IROp == IR::OP_F64FPREM ||
IROp == IR::OP_F64FPREM1) {
//TODO: Set C0 to Q2, C3 to Q1, C1 to Q0
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(_Constant(0));
}
+4 -2
View File
@@ -24,10 +24,12 @@ X86GeneratedCode::X86GeneratedCode() {
CodePtr = AllocateGuestCodeSpace(CODE_SIZE);
SignalReturn = reinterpret_cast<uint64_t>(CodePtr);
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr) + 2;
SignalReturnRT = reinterpret_cast<uint64_t>(CodePtr) + 3;
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr) + 6;
const std::vector<uint8_t> SignalReturnCode = {
0x0F, 0x36, // SIGRET FEX instruction
0x0F, 0x36, 0x0, // SIGRET FEX instruction (Non-RT)
0x0F, 0x36, 0x1, // SIGRET FEX instruction (RT)
0x0F, 0x37, // CALLBACKRET FEX Instruction
};
+1
View File
@@ -16,6 +16,7 @@ public:
~X86GeneratedCode();
uint64_t SignalReturn{};
uint64_t SignalReturnRT{};
uint64_t CallbackReturn{};
private:
@@ -338,7 +338,7 @@ void InitializeSecondaryGroupTables() {
{OPD(TYPE_GROUP_15, PF_NONE, 3), 1, X86InstInfo{"STMXCSR", TYPE_INST, GenFlagsSameSize(SIZE_32BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_MEM_ONLY, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_NONE, 4), 1, X86InstInfo{"XSAVE", TYPE_PRIV, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_NONE, 5), 1, X86InstInfo{"LFENCE/XRSTOR", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_NONE, 6), 1, X86InstInfo{"MFENCE/XSAVEOPT", TYPE_INST, FLAGS_MODRM, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_NONE, 6), 1, X86InstInfo{"MFENCE/XSAVEOPT", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_NONE, 7), 1, X86InstInfo{"SFENCE/CLFLUSH", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_F3, 0), 1, X86InstInfo{"RDFSBASE", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_REG_ONLY, 0, nullptr}},
@@ -356,8 +356,8 @@ void InitializeSecondaryGroupTables() {
{OPD(TYPE_GROUP_15, PF_66, 3), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_66, 4), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_66, 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_66, 6), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_66, 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_66, 6), 1, X86InstInfo{"CLWB", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_66, 7), 1, X86InstInfo{"CLFLUSHOPT", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_F2, 0), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
{OPD(TYPE_GROUP_15, PF_F2, 1), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
@@ -258,7 +258,7 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
// FEX reserved instructions
// Unused x86 encoding instruction.
// Used by FEX to know when to do a signal return
{0x36, 1, X86InstInfo{"SIGRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0, nullptr}},
{0x36, 1, X86InstInfo{"SIGRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 1, nullptr}},
{0x37, 1, X86InstInfo{"CALLBACKRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0, nullptr}},
@@ -325,7 +325,7 @@ void InitializeVEXTables() {
{OPD(2, 0b01, 0x40), 1, X86InstInfo{"VPMULLD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x41), 1, X86InstInfo{"VPHMINPOSUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x45), 1, X86InstInfo{"VPSRLV", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x46), 1, X86InstInfo{"VPSRAVD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x46), 1, X86InstInfo{"VPSRAVD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x47), 1, X86InstInfo{"VPSLLV", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x58), 1, X86InstInfo{"VPBROADCASTD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
@@ -407,7 +407,7 @@ void InitializeVEXTables() {
// VEX Map 3
{OPD(3, 0b01, 0x00), 1, X86InstInfo{"VPERMQ", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x01), 1, X86InstInfo{"VPERMPD", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x02), 1, X86InstInfo{"VPBLENDD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x02), 1, X86InstInfo{"VPBLENDD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x04), 1, X86InstInfo{"VPERMILPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x05), 1, X86InstInfo{"VPERMILPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x06), 1, X86InstInfo{"VPERM2F128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
@@ -416,7 +416,7 @@ void InitializeVEXTables() {
{OPD(3, 0b01, 0x09), 1, X86InstInfo{"VROUNDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x0A), 1, X86InstInfo{"VROUNDSS", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x0B), 1, X86InstInfo{"VROUNDSD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x0C), 1, X86InstInfo{"VBLENDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x0C), 1, X86InstInfo{"VBLENDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x0D), 1, X86InstInfo{"VBLENDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x0E), 1, X86InstInfo{"VBLENDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x0F), 1, X86InstInfo{"VPALIGNR", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
+11 -3
View File
@@ -264,7 +264,7 @@
"Break BreakDefinition:$Reason": {
"HasSideEffects": true
},
"SignalReturn": {
"SignalReturn i8:$IsRT": {
"HasSideEffects": true
},
"CallbackReturn": {
@@ -479,9 +479,17 @@
]
},
"CacheLineClear GPR:$Addr": {
"CacheLineClear GPR:$Addr, i1:$Serialize": {
"Desc": ["Does a 64 byte cacheline clear at the address specified",
"Only clears the data cachelines. Doesn't do any zeroing"
"Only clears the data cachelines. Doesn't do any zeroing",
"Can skip serialization if requested."
],
"HasSideEffects": true
},
"CacheLineClean GPR:$Addr": {
"Desc": ["Does a 64 byte cacheline cleanat the address specified",
"Only cleans the data cachelines. Doesn't do any zeroing",
"Skips the invalidation step of the CacheLineClear operation"
],
"HasSideEffects": true
},
+3 -2
View File
@@ -23,6 +23,7 @@ namespace FEXCore::IR {
#define IROP_REG_CLASSES_IMPL
#define IROP_HASSIDEEFFECTS_IMPL
#define IROP_SIZES_IMPL
#define IROP_GETHASDEST_IMPL
#include <FEXCore/IR/IRDefines.inc>
@@ -125,7 +126,7 @@ static void PrintArg(std::stringstream *out, IRListView const* IR, OrderedNodeWr
}
}
if (IROp->HasDest) {
if (GetHasDest(IROp->Op)) {
uint32_t ElementSize = IROp->ElementSize;
uint32_t NumElements = IROp->Size;
if (!IROp->ElementSize) {
@@ -231,7 +232,7 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationDa
if (!Skip) {
AddIndent();
if (IROp->HasDest) {
if (GetHasDest(IROp->Op)) {
uint32_t ElementSize = IROp->ElementSize;
uint32_t NumElements = IROp->Size;
+2 -3
View File
@@ -112,7 +112,7 @@ void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode,
while (Begin != End) {
auto [RealNode, IROp] = Begin();
uint8_t NumArgs = IR::GetArgs(IROp->Op);
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
if (IROp->Args[i].ID() == NodeId) {
Node->RemoveUse();
@@ -148,7 +148,7 @@ void IREmitter::RemoveArgUses(OrderedNode *Node) {
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
uint8_t NumArgs = IR::GetArgs(IROp->Op);
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
auto ArgNode = IROp->Args[i].GetNode(ListBegin);
ArgNode->RemoveUse();
@@ -201,7 +201,6 @@ void IREmitter::ReplaceWithConstant(OrderedNode *Node, uint64_t Value) {
// Overwrite data with the new constant op
Header->Op = OP_CONSTANT;
Header->NumArgs = 0;
auto Const = Header->CW<IROp_Constant>();
Const->Constant = Value;
} else {
+31 -18
View File
@@ -29,6 +29,7 @@ $end_info$
#include <string.h>
#include <tuple>
#include <unordered_map>
#include <tsl/robin_map.h>
#include <utility>
namespace FEXCore::IR {
@@ -198,6 +199,17 @@ private:
std::unordered_map<uint64_t, OrderedNode*> ConstPool;
std::map<OrderedNode*, uint64_t> AddressgenConsts;
// Pool inline constant generation. These are typically very small and pool efficiently.
tsl::robin_map<uint64_t, OrderedNode*> InlineConstantGen;
OrderedNode *CreateInlineConstant(IREmitter *IREmit, uint64_t Constant) {
const auto it = InlineConstantGen.find(Constant);
if (it != InlineConstantGen.end()) {
return it->second;
}
auto Result = InlineConstantGen.insert_or_assign(Constant, IREmit->_InlineConstant(Constant));
return Result.first->second;
}
bool SupportsTSOImm9{};
};
@@ -233,7 +245,7 @@ void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& Cur
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
auto BlockOp = BlockIROp->CW<FEXCore::IR::IROp_CodeBlock>();
for (auto [UnaryOpNode, UnaryOpHdr] : CurrentIR.GetCode(BlockNode)) {
if (UnaryOpHdr->NumArgs == 1 && !HasSideEffects(UnaryOpHdr->Op)) {
if (IR::GetArgs(UnaryOpHdr->Op) == 1 && !HasSideEffects(UnaryOpHdr->Op)) {
// could be moved
auto SelectOpNode = IREmit->UnwrapNode(UnaryOpHdr->Args[0]);
auto SelectOpHdr = IREmit->GetOpHeader(UnaryOpHdr->Args[0]);
@@ -255,7 +267,7 @@ void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& Cur
// Copy over the op
memcpy(NewUnaryOp1.first, UnaryOpHdr, OpSize);
for (int i = 0; i < NewUnaryOp1.first->NumArgs; i++) {
for (int i = 0; i < IR::GetArgs(NewUnaryOp1.first->Op); i++) {
NewUnaryOp1.first->Args[i] = IREmit->WrapNode(IREmit->Invalid());
}
// Set New Op to operate on the constant
@@ -269,7 +281,7 @@ void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& Cur
// Copy over the op
memcpy(NewUnaryOp2.first, UnaryOpHdr, OpSize);
for (int i = 0; i < NewUnaryOp2.first->NumArgs; i++) {
for (int i = 0; i < IR::GetArgs(NewUnaryOp2.first->Op); i++) {
NewUnaryOp2.first->Args[i] = IREmit->WrapNode(IREmit->Invalid());
}
// Set New Op to operate on the constant
@@ -366,7 +378,7 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& C
case OP_ASHR:
case OP_LSHL:
case OP_ROR: {
for (int i = 0; i < IROp->NumArgs; i++) {
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp));
if (newArg.ID() != IROp->Args[i].ID()) {
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
@@ -378,7 +390,7 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& C
case OP_AND: {
// if AND's arguments are imms, they are masking
for (int i = 0; i < IROp->NumArgs; i++) {
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
auto mask = getMask(IROp);
uint64_t imm = 0;
if (IREmit->IsValueConstant(IROp->Args[i^1], &imm))
@@ -457,7 +469,7 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& C
case OP_VFDIV:
case OP_FCMP: {
auto flopSize = IROp->Size;
for (int i = 0; i < IROp->NumArgs; i++) {
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
auto argHeader = IREmit->GetOpHeader(IROp->Args[i]);
if (argHeader->Op == OP_VMOV) {
@@ -820,6 +832,7 @@ bool ConstProp::ConstantPropagation(IREmitter *IREmit, const IRListView& Current
}
bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR) {
InlineConstantGen.clear();
bool Changed = false;
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
@@ -841,7 +854,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
else
Constant2 &= 63;
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -857,7 +870,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -873,7 +886,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsImmAddSub(Constant1)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant1));
Changed = true;
}
@@ -888,8 +901,8 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
{
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
IREmit->ReplaceNodeArgument(CodeNode, 2, CreateInlineConstant(IREmit, Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 3, CreateInlineConstant(IREmit, Constant3));
}
break;
@@ -903,7 +916,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsImmAddSub(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -919,7 +932,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant));
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, Constant));
Changed = true;
} else {
@@ -945,7 +958,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsImmLogical(Constant2, IROp->Size * 8)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -961,7 +974,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsImmMemory(Constant2, IROp->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -977,7 +990,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsImmMemory(Constant2, IROp->Size)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -994,7 +1007,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsTSOImm9(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -1012,7 +1025,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
if (IsTSOImm9(Constant2)) {
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
Changed = true;
}
@@ -174,9 +174,9 @@ bool IRCompaction::Run(IREmitter *IREmit) {
for (auto [LocalNode, LocalIROp] : LocalIR.GetCode(Block.NewNode)) {
// Now that we have the op copied over, we need to modify SSA values to point to the new correct locations
// This doesn't use IR::GetArgs(Op) because we need to remap all SSA nodes
// This doesn't use IR::GetRAArgs(Op) because we need to remap all SSA nodes
// Including ones that we don't RA
const uint8_t NumArgs = LocalIROp->NumArgs;
const uint8_t NumArgs = IR::GetArgs(LocalIROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
const auto OldArg = LocalIROp->Args[i].ID();
const auto NewArg = OldToNewRemap[OldArg.Value].NodeID;
@@ -79,7 +79,7 @@ bool IRValidation::Run(IREmitter *IREmit) {
const auto ID = CurrentIR.GetID(CodeNode);
const uint8_t OpSize = IROp->Size;
if (IROp->HasDest) {
if (GetHasDest(IROp->Op)) {
HadError |= OpSize == 0;
// Does the op have a destination of size 0?
if (OpSize == 0) {
@@ -120,23 +120,8 @@ bool IRValidation::Run(IREmitter *IREmit) {
}
}
uint8_t NumArgs = IR::GetArgs(IROp->Op);
uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
if (NumArgs != IROp->NumArgs) {
switch (IROp->Op) {
case OP_BEGINBLOCK:
case OP_ENDBLOCK:
case OP_PHI:
case OP_PHIVALUE:
case OP_CONDJUMP:
case OP_JUMP:
// These override the number of args for RA, so ignore them.
break;
default:
HadError |= true;
Errors << "%ssa" << ID << ": Has wrong number of Args" << std::endl;
}
}
for (uint32_t i = 0; i < NumArgs; ++i) {
OrderedNodeWrapper Arg = IROp->Args[i];
const auto ArgID = Arg.ID();
@@ -257,7 +257,7 @@ namespace {
void FindNodeClasses(RegisterGraph *Graph, FEXCore::IR::IRListView *IR) {
for (auto [CodeNode, IROp] : IR->GetAllCode()) {
// If the destination hasn't yet been set then set it now
if (IROp->HasDest) {
if (GetHasDest(IROp->Op)) {
const auto ID = IR->GetID(CodeNode);
Graph->AllocData->Map[ID.Value] = PhysicalRegister(GetRegClassFromNode(IR, IROp), INVALID_REG);
} else {
@@ -455,7 +455,7 @@ namespace {
auto& NodeLiveRange = LiveRanges[Node.Value];
// If the destination hasn't yet been set then set it now
if (IROp->HasDest) {
if (GetHasDest(IROp->Op)) {
LOGMAN_THROW_AA_FMT(NodeLiveRange.Begin.Value == UINT32_MAX,
"Node begin already defined?");
NodeLiveRange.Begin = Node;
@@ -475,7 +475,7 @@ namespace {
continue;
}
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
const uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
const auto& Arg = IROp->Args[i];
@@ -679,7 +679,7 @@ namespace {
auto& NodeLiveRange = LiveRanges[Node.Value];
// Check for read-after-write and demote if it happens
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
const uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
const auto& Arg = IROp->Args[i];
@@ -709,7 +709,7 @@ namespace {
}
// This op defines a span
if (IROp->HasDest) {
if (GetHasDest(IROp->Op)) {
// If this is a pre-write, update the StaticMap so we track writes
if (!NodeLiveRange.PrefferedRegister.IsInvalid()) {
SRA_DEBUG("ssa{} is a pre-write\n", Node);
@@ -1037,7 +1037,7 @@ namespace {
while(1) {
auto [RealNode, IROp] = Begin();
const uint8_t NumArgs = FEXCore::IR::GetArgs(IROp->Op);
const uint8_t NumArgs = FEXCore::IR::GetRAArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
const auto ArgNode = IROp->Args[i].ID();
if (ArgNode == SearchID) {
@@ -1069,7 +1069,7 @@ namespace {
return End;
}
const uint8_t NumArgs = FEXCore::IR::GetArgs(IROp->Op);
const uint8_t NumArgs = FEXCore::IR::GetRAArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
const auto ArgNode = IROp->Args[i].ID();
if (ArgNode == SearchID) {
@@ -1297,7 +1297,7 @@ namespace {
CurrentNodes.insert(NodeOpBegin.ID());
for (int i = 0; i < IROp->NumArgs; i++) {
for (int i = 0; i < IR::GetRAArgs(IROp->Op); i++) {
CurrentNodes.insert(IROp->Args[i].ID());
}
}
@@ -1370,7 +1370,7 @@ namespace {
auto LastCursor = IREmit->GetWriteCursor();
auto [CodeNode, IROp] = IR.at(SpillPointId)();
LOGMAN_THROW_AA_FMT(IROp->HasDest, "Can't spill with no dest");
LOGMAN_THROW_AA_FMT(GetHasDest(IROp->Op), "Can't spill with no dest");
const auto Node = IR.GetID(CodeNode);
RegisterNode *CurrentNode = &Graph->Nodes[Node.Value];
@@ -94,7 +94,7 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) {
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
const auto CodeID = CurrentIR.GetID(CodeNode);
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
const uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
for (uint32_t i = 0; i < NumArgs; ++i) {
if (IROp->Args[i].IsInvalid()) continue;
if (CurrentIR.GetOp<IROp_Header>(IROp->Args[i])->Op == OP_IRHEADER) continue;
+26 -25
View File
@@ -1,51 +1,52 @@
#include <string>
#include <vector>
#include <filesystem>
#include <fstream>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <span>
#include <unistd.h>
namespace FEXCore::FileLoading {
bool LoadFile(std::vector<char> &Data, const std::string &Filepath, size_t FixedSize) {
std::fstream ConfigFile;
ConfigFile.open(Filepath, std::ios::in);
int FD = open(Filepath.c_str(), O_RDONLY);
if (!ConfigFile.is_open()) {
if (FD == -1) {
return false;
}
size_t FileSize{};
if (FixedSize == 0) {
if (!ConfigFile.seekg(0, std::fstream::end)) {
struct stat buf;
if (fstat(FD, &buf) != 0) {
close(FD);
return false;
}
FileSize = ConfigFile.tellg();
if (ConfigFile.fail()) {
return false;
}
if (!ConfigFile.seekg(0, std::fstream::beg)) {
return false;
}
FileSize = buf.st_size;
}
else {
FileSize = FixedSize;
}
ssize_t Read = -1;
if (FileSize > 0) {
Data.resize(FileSize);
if (!ConfigFile.read(&Data.at(0), FileSize)) {
// Probably means permissions aren't set. Just early exit
return false;
}
ConfigFile.close();
Read = pread(FD, &Data.at(0), FileSize, 0);
}
else {
return false;
}
return true;
close(FD);
return Read == FileSize;
}
ssize_t LoadFileToBuffer(const std::string &Filepath, std::span<char> Buffer) {
int FD = open(Filepath.c_str(), O_RDONLY);
if (FD == -1) {
return -1;
}
ssize_t Read = pread(FD, Buffer.data(), Buffer.size(), 0);
close(FD);
return Read;
}
}
+11
View File
@@ -3,6 +3,7 @@
#include <vector>
#include <filesystem>
#include <fstream>
#include <span>
namespace FEXCore::FileLoading {
/**
@@ -14,5 +15,15 @@ namespace FEXCore::FileLoading {
* @return true on file loaded, false on failure
*/
bool LoadFile(std::vector<char> &Data, const std::string &Filepath, size_t FixedSize = 0);
/**
* @brief Loads a filepath in to a buffer of data with a fixed size
*
* @param Filepath The filepath to load
* @param Buffer The buffer to load the data in to. Attempting to read the full size of the span
*
* @return The amount of data read or -1 on error.
*/
ssize_t LoadFileToBuffer(const std::string &Filepath, std::span<char> Buffer);
}
+1
View File
@@ -159,6 +159,7 @@ namespace FEXCore::Core {
uint64_t GuestSignal_SIGTRAP{};
uint64_t GuestSignal_SIGSEGV{};
uint64_t SignalReturnHandler{};
uint64_t SignalReturnHandlerRT{};
uint64_t L1Pointer{};
uint64_t L2Pointer{};
/** @} */
+1
View File
@@ -27,6 +27,7 @@ class HostFeatures final {
bool SupportsSHA{};
bool SupportsBMI1{};
bool SupportsBMI2{};
bool SupportsCLWB{};
bool SupportsPMULL_128Bit{};
// Float exception behaviour
+28
View File
@@ -372,5 +372,33 @@ namespace FEXCore {
};
static_assert(sizeof(FEXCore::x86::ucontext_t) == 236, "This needs to be the right size");
///< Non-rt signal context.
//
// Needs to match the format expected from signal handlers without SA_SIGINFO set.
struct sigcontext {
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;
uint32_t di;
uint32_t si;
uint32_t bp;
uint32_t sp;
uint32_t bx;
uint32_t dx;
uint32_t cx;
uint32_t ax;
uint32_t trapno;
uint32_t err;
uint32_t ip;
uint32_t cs;
uint32_t flags;
uint32_t sp_at_signal;
uint32_t ss;
uint32_t fpstate;
uint32_t oldmask;
uint32_t cr2;
};
}
}
+7 -7
View File
@@ -8,13 +8,13 @@ namespace FEXCore::X86State {
* @{ */
enum X86Reg : uint32_t {
REG_RAX = 0,
REG_RBX = 1,
REG_RCX = 2,
REG_RDX = 3,
REG_RSI = 4,
REG_RDI = 5,
REG_RBP = 6,
REG_RSP = 7,
REG_RCX = 1,
REG_RDX = 2,
REG_RBX = 3,
REG_RSP = 4,
REG_RBP = 5,
REG_RSI = 6,
REG_RDI = 7,
REG_R8 = 8,
REG_R9 = 9,
REG_R10 = 10,
@@ -68,6 +68,7 @@ namespace FEXCore::Core {
Pause,
Stop,
Return,
ReturnRT,
};
struct LocalIREntry {
+12 -12
View File
@@ -144,8 +144,8 @@ struct DecodedOperand {
} GPR;
struct {
uint8_t GPR;
int32_t Displacement;
uint8_t GPR;
} GPRIndirect;
struct {
@@ -156,27 +156,33 @@ struct DecodedOperand {
} RIPLiteral;
struct {
uint8_t Size;
uint64_t Value;
uint8_t Size;
} Literal;
struct {
int32_t Offset;
uint8_t Scale;
uint8_t Index; // ~0 invalid
uint8_t Base; // ~0 invalid
uint32_t Scale : 8;
int32_t Offset;
} SIB;
};
OpType Type;
TypeUnion Data;
OpType Type;
};
struct DecodedInst {
uint64_t PC;
uint16_t OP;
DecodedOperand Dest;
DecodedOperand Src[3];
// Constains the dispatcher handler pointer
X86InstInfo const* TableInfo;
uint32_t Flags;
uint16_t OP;
uint8_t ModRM;
uint8_t SIB;
@@ -184,12 +190,6 @@ struct DecodedInst {
uint8_t LastEscapePrefix;
bool DecodedModRM;
bool DecodedSIB;
DecodedOperand Dest;
DecodedOperand Src[3];
// Constains the dispatcher handler pointer
X86InstInfo const* TableInfo;
};
union ModRMDecoded {
-2
View File
@@ -58,8 +58,6 @@ friend class FEXCore::IR::PassManager;
Op.first->Constant = (Constant & Mask);
Op.first->Header.Size = Size / 8;
Op.first->Header.ElementSize = Size / 8;
Op.first->Header.NumArgs = 0;
Op.first->Header.HasDest = true;
return Op;
}
IRPair<IROp_Bfe> _Bfe(uint8_t Width, uint8_t lsb, OrderedNode *ssa0) {
+152 -2
View File
@@ -83,6 +83,150 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
CHECK(DisassembleEncoding(0) == 0xb000001e);
}
{
// Will generate adr.
BackwardLabel Label;
Bind(&Label);
dc32(0);
LongAddressGen(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x10fffffe);
}
{
// Will generate nop + adr.
ForwardLabel Label;
LongAddressGen(Reg::r30, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
CHECK(DisassembleEncoding(1) == 0x1000003e);
}
{
// Will generate adr.
BiDirectionalLabel Label;
Bind(&Label);
dc32(0);
LongAddressGen(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x10fffffe);
}
{
// Will generate nop + adr.
BiDirectionalLabel Label;
LongAddressGen(Reg::r30, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
CHECK(DisassembleEncoding(1) == 0x1000003e);
}
{
// Will generate adrp.
BackwardLabel Label;
Bind(&Label);
dc32(0);
// Move adrp 1MB away.
for (size_t i = 0; i < (1 * 1024 * 1024 / 4); ++i) {
nop();
}
LongAddressGen(Reg::r30, &Label);
nop();
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
CHECK(DisassembleEncoding(262146) == 0xd503201f);
}
{
// Will generate nop + adrp.
ForwardLabel Label;
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, and then aligned to a page.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
nop();
}
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
CHECK(DisassembleEncoding(1) == 0x9000081e);
}
{
// Will generate adrp + add.
ForwardLabel Label;
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, plus one instruction.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
nop();
}
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb000081e);
CHECK(DisassembleEncoding(1) == 0x910013de);
}
{
// Will generate adrp.
BiDirectionalLabel Label;
Bind(&Label);
dc32(0);
// Move adrp 1MB away.
for (size_t i = 0; i < (1 * 1024 * 1024 / 4); ++i) {
nop();
}
LongAddressGen(Reg::r30, &Label);
nop();
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
CHECK(DisassembleEncoding(262146) == 0xd503201f);
}
{
// Will generate nop + adrp.
BiDirectionalLabel Label;
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, and then aligned to a page.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
nop();
}
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
CHECK(DisassembleEncoding(1) == 0x9000081e);
}
{
// Will generate adrp + add.
BiDirectionalLabel Label;
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, plus one instruction.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
nop();
}
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb000081e);
CHECK(DisassembleEncoding(1) == 0x910013de);
}
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Add/subtract immediate") {
TEST_SINGLE(add(Size::i32Bit, Reg::r29, Reg::r28, 0, false), "add w29, w28, #0x0 (0)");
@@ -1352,10 +1496,16 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: AddSub - with carry") {
TEST_SINGLE(sbcs(Size::i64Bit, Reg::r29, Reg::r28, Reg::r27), "sbcs x29, x28, x27");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Rotate right into flags") {
// TODO: Add support to emitter.
TEST_SINGLE(rmif(XReg::x30, 63, 0b0000), "rmif x30, #63, #nzcv");
TEST_SINGLE(rmif(XReg::x30, 63, 0b0001), "rmif x30, #63, #nzcV");
TEST_SINGLE(rmif(XReg::x30, 63, 0b0010), "rmif x30, #63, #nzCv");
TEST_SINGLE(rmif(XReg::x30, 63, 0b0100), "rmif x30, #63, #nZcv");
TEST_SINGLE(rmif(XReg::x30, 63, 0b1000), "rmif x30, #63, #Nzcv");
TEST_SINGLE(rmif(XReg::x30, 63, 0b1111), "rmif x30, #63, #NZCV");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Evaluate into flags") {
// TODO: Add support to emitter.
TEST_SINGLE(setf8(XReg::x30), "setf8 w30");
TEST_SINGLE(setf16(XReg::x30), "setf16 w30");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Conditional compare - register") {
TEST_SINGLE(ccmn(Size::i32Bit, Reg::r29, Reg::r28, StatusFlags::None, Condition::CC_AL), "ccmn w29, w28, #nzcv, al");
+749 -7
View File
@@ -249,13 +249,169 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD copy") {
TEST_SINGLE(ins(SubRegSize::i64Bit, VReg::v30, 1, VReg::v29, 0), "mov v30.d[1], v29.d[0]");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD three same (FP16)") {
// TODO: Implement in emitter.
TEST_SINGLE(fmaxnm<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmaxnm v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmla<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmla v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fadd<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fadd v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmulx<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmulx v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fcmeq<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fcmeq v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmax<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmax v30.8h, v29.8h, v28.8h");
TEST_SINGLE(frecps<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "frecps v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fminnm<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fminnm v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmls<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmls v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fsub<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fsub v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmin<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmin v30.8h, v29.8h, v28.8h");
TEST_SINGLE(frsqrts<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "frsqrts v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmaxnmp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmaxnmp v30.8h, v29.8h, v28.8h");
TEST_SINGLE(faddp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "faddp v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmul<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmul v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fcmge<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fcmge v30.8h, v29.8h, v28.8h");
TEST_SINGLE(facge<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "facge v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fmaxp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmaxp v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fdiv<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fdiv v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fminnmp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fminnmp v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fabd<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fabd v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fcmgt<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fcmgt v30.8h, v29.8h, v28.8h");
TEST_SINGLE(facgt<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "facgt v30.8h, v29.8h, v28.8h");
TEST_SINGLE(fminp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fminp v30.8h, v29.8h, v28.8h");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register miscellaneous (FP16)") {
// TODO: Implement in emitter.
TEST_SINGLE(frintn<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintn v30.8h, v29.8h");
TEST_SINGLE(frintm<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintm v30.8h, v29.8h");
TEST_SINGLE(fcvtns<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtns v30.8h, v29.8h");
TEST_SINGLE(fcvtms<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtms v30.8h, v29.8h");
TEST_SINGLE(fcvtas<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtas v30.8h, v29.8h");
TEST_SINGLE(scvtf<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "scvtf v30.8h, v29.8h");
TEST_SINGLE(fcmgt<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmgt v30.8h, v29.8h, #0.0");
TEST_SINGLE(fcmeq<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmeq v30.8h, v29.8h, #0.0");
TEST_SINGLE(fcmlt<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmlt v30.8h, v29.8h, #0.0");
TEST_SINGLE(fabs<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fabs v30.8h, v29.8h");
TEST_SINGLE(frintp<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintp v30.8h, v29.8h");
TEST_SINGLE(frintz<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintz v30.8h, v29.8h");
TEST_SINGLE(fcvtps<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtps v30.8h, v29.8h");
TEST_SINGLE(fcvtzs<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtzs v30.8h, v29.8h");
TEST_SINGLE(frecpe<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frecpe v30.8h, v29.8h");
TEST_SINGLE(frinta<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frinta v30.8h, v29.8h");
TEST_SINGLE(frintx<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintx v30.8h, v29.8h");
TEST_SINGLE(fcvtnu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtnu v30.8h, v29.8h");
TEST_SINGLE(fcvtmu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtmu v30.8h, v29.8h");
TEST_SINGLE(fcvtau<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtau v30.8h, v29.8h");
TEST_SINGLE(ucvtf<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "ucvtf v30.8h, v29.8h");
TEST_SINGLE(fcmge<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmge v30.8h, v29.8h, #0.0");
TEST_SINGLE(fcmle<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmle v30.8h, v29.8h, #0.0");
TEST_SINGLE(fneg<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fneg v30.8h, v29.8h");
TEST_SINGLE(frinti<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frinti v30.8h, v29.8h");
TEST_SINGLE(fcvtpu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtpu v30.8h, v29.8h");
TEST_SINGLE(fcvtzu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtzu v30.8h, v29.8h");
TEST_SINGLE(frsqrte<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frsqrte v30.8h, v29.8h");
TEST_SINGLE(fsqrt<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fsqrt v30.8h, v29.8h");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD three-register extension") {
// TODO: Implement in emitter.
TEST_SINGLE(sdot(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.16b, v29.16b, v28.16b");
TEST_SINGLE(sdot(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.8h, v29.16b, v28.16b");
TEST_SINGLE(sdot(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.4s, v29.16b, v28.16b");
TEST_SINGLE(sdot(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.2d, v29.16b, v28.16b");
TEST_SINGLE(sdot(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.8b, v29.8b, v28.8b");
TEST_SINGLE(sdot(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.4h, v29.8b, v28.8b");
TEST_SINGLE(sdot(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.2s, v29.8b, v28.8b");
//TEST_SINGLE(sdot(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.1d, v29.8b, v28.8b");
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q28), "usdot v30.4s, v29.16b, v28.16b");
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d28), "usdot v30.2s, v29.8b, v28.8b");
TEST_SINGLE(sqrdmlah(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.16b, v29.16b, v28.16b");
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.8h, v29.8h, v28.8h");
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.4s, v29.4s, v28.4s");
TEST_SINGLE(sqrdmlah(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.2d, v29.2d, v28.2d");
TEST_SINGLE(sqrdmlah(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.8b, v29.8b, v28.8b");
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.4h, v29.4h, v28.4h");
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.2s, v29.2s, v28.2s");
//TEST_SINGLE(sqrdmlah(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.1d, v29.1d, v28.1d");
TEST_SINGLE(sqrdmlsh(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.16b, v29.16b, v28.16b");
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.8h, v29.8h, v28.8h");
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.4s, v29.4s, v28.4s");
TEST_SINGLE(sqrdmlsh(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.2d, v29.2d, v28.2d");
TEST_SINGLE(sqrdmlsh(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.8b, v29.8b, v28.8b");
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.4h, v29.4h, v28.4h");
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.2s, v29.2s, v28.2s");
//TEST_SINGLE(sqrdmlsh(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.1d, v29.1d, v28.1d");
TEST_SINGLE(udot(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.16b, v29.16b, v28.16b");
TEST_SINGLE(udot(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.8h, v29.16b, v28.16b");
TEST_SINGLE(udot(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.4s, v29.16b, v28.16b");
TEST_SINGLE(udot(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.2d, v29.16b, v28.16b");
TEST_SINGLE(udot(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.8b, v29.8b, v28.8b");
TEST_SINGLE(udot(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.4h, v29.8b, v28.8b");
TEST_SINGLE(udot(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.2s, v29.8b, v28.8b");
//TEST_SINGLE(udot(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.1d, v29.8b, v28.8b");
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.16b, v29.16b, v28.16b, #0");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.8h, v29.8h, v28.8h, #0");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.4s, v29.4s, v28.4s, #0");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.2d, v29.2d, v28.2d, #0");
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.8b, v29.8b, v28.8b, #0");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.4h, v29.4h, v28.4h, #0");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.2s, v29.2s, v28.2s, #0");
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.1d, v29.1d, v28.1d, #0");
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.16b, v29.16b, v28.16b, #90");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.8h, v29.8h, v28.8h, #90");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.4s, v29.4s, v28.4s, #90");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.2d, v29.2d, v28.2d, #90");
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.8b, v29.8b, v28.8b, #90");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.4h, v29.4h, v28.4h, #90");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.2s, v29.2s, v28.2s, #90");
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.1d, v29.1d, v28.1d, #90");
// Vixl disassembler has a bug that claims 8-bit fcmla exists
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.16b, v29.16b, v28.16b, #180");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.8h, v29.8h, v28.8h, #180");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.4s, v29.4s, v28.4s, #180");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.2d, v29.2d, v28.2d, #180");
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.8b, v29.8b, v28.8b, #180");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.4h, v29.4h, v28.4h, #180");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.2s, v29.2s, v28.2s, #180");
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.1d, v29.1d, v28.1d, #180");
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.16b, v29.16b, v28.16b, #270");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.8h, v29.8h, v28.8h, #270");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.4s, v29.4s, v28.4s, #270");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.2d, v29.2d, v28.2d, #270");
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.8b, v29.8b, v28.8b, #270");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.4h, v29.4h, v28.4h, #270");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.2s, v29.2s, v28.2s, #270");
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.1d, v29.1d, v28.1d, #270");
// Vixl disassembler has a bug that claims 8-bit fcadd exists
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.16b, v29.16b, v28.16b, #90");
TEST_SINGLE(fcadd(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.8h, v29.8h, v28.8h, #90");
TEST_SINGLE(fcadd(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.4s, v29.4s, v28.4s, #90");
TEST_SINGLE(fcadd(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.2d, v29.2d, v28.2d, #90");
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.8b, v29.8b, v28.8b, #90");
TEST_SINGLE(fcadd(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.4h, v29.4h, v28.4h, #90");
TEST_SINGLE(fcadd(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.2s, v29.2s, v28.2s, #90");
//TEST_SINGLE(fcadd(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.1d, v29.1d, v28.1d, #90");
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.16b, v29.16b, v28.16b, #270");
TEST_SINGLE(fcadd(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.8h, v29.8h, v28.8h, #270");
TEST_SINGLE(fcadd(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.4s, v29.4s, v28.4s, #270");
TEST_SINGLE(fcadd(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.2d, v29.2d, v28.2d, #270");
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.8b, v29.8b, v28.8b, #270");
TEST_SINGLE(fcadd(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.4h, v29.4h, v28.4h, #270");
TEST_SINGLE(fcadd(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.2s, v29.2s, v28.2s, #270");
//TEST_SINGLE(fcadd(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.1d, v29.1d, v28.1d, #270");
// TODO: Enable once vixl disassembler supports these instructions
// TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q28), "bfdot v30.4s, v29.8h, v28.8h");
// TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d28), "bfdot v30.2s, v29.4h, v28.4h");
// TEST_SINGLE(bfmlalb(VReg::v30, VReg::v29, VReg::v28), "bfmlalb v30.4s, v29.8h, v28.8h");
// TEST_SINGLE(bfmlalt(VReg::v30, VReg::v29, VReg::v28), "bfmlalt v30.4s, v29.8h, v28.8h");
TEST_SINGLE(smmla(VReg::v30, VReg::v29, VReg::v28), "smmla v30.4s, v29.16b, v28.16b");
TEST_SINGLE(usmmla(VReg::v30, VReg::v29, VReg::v28), "usmmla v30.4s, v29.16b, v28.16b");
// TODO: Enable once vixl disassembler supports these instructions
//TEST_SINGLE(bfmmla(VReg::v30, VReg::v29, VReg::v28), "bfmmla v30.4s, v29.8h, v28.8h");
TEST_SINGLE(ummla(VReg::v30, VReg::v29, VReg::v28), "ummla v30.4s, v29.16b, v28.16b");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register miscellaneous") {
// Commented out lines showcase unallocated encodings.
@@ -2265,7 +2421,41 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
TEST_SINGLE(sxtl2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "sxtl2 v30.4s, v29.8h");
TEST_SINGLE(sxtl2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "sxtl2 v30.2d, v29.4s");
//// TODO: SCVTF, FCVTZS
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "scvtf v30.16b, v29.16b, #1");
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "scvtf v30.16b, v29.16b, #7");
TEST_SINGLE(scvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "scvtf v30.8h, v29.8h, #1");
TEST_SINGLE(scvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "scvtf v30.8h, v29.8h, #15");
TEST_SINGLE(scvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "scvtf v30.4s, v29.4s, #1");
TEST_SINGLE(scvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "scvtf v30.4s, v29.4s, #31");
TEST_SINGLE(scvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "scvtf v30.2d, v29.2d, #1");
TEST_SINGLE(scvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "scvtf v30.2d, v29.2d, #63");
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "scvtf v30.8b, v29.8b, #1");
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "scvtf v30.8b, v29.8b, #7");
TEST_SINGLE(scvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "scvtf v30.4h, v29.4h, #1");
TEST_SINGLE(scvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "scvtf v30.4h, v29.4h, #15");
TEST_SINGLE(scvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "scvtf v30.2s, v29.2s, #1");
TEST_SINGLE(scvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "scvtf v30.2s, v29.2s, #31");
//TEST_SINGLE(scvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "scvtf v30.1d, v29.1d, #1");
//TEST_SINGLE(scvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "scvtf v30.1d, v29.1d, #63");
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.16b, v29.16b, #1");
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "fcvtzs v30.16b, v29.16b, #7");
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.8h, v29.8h, #1");
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "fcvtzs v30.8h, v29.8h, #15");
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.4s, v29.4s, #1");
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "fcvtzs v30.4s, v29.4s, #31");
TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.2d, v29.2d, #1");
TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "fcvtzs v30.2d, v29.2d, #63");
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.8b, v29.8b, #1");
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "fcvtzs v30.8b, v29.8b, #7");
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.4h, v29.4h, #1");
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "fcvtzs v30.4h, v29.4h, #15");
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.2s, v29.2s, #1");
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "fcvtzs v30.2s, v29.2s, #31");
//TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.1d, v29.1d, #1");
//TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "fcvtzs v30.1d, v29.1d, #63");
TEST_SINGLE(ushr(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "ushr v30.16b, v29.16b, #1");
TEST_SINGLE(ushr(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "ushr v30.16b, v29.16b, #7");
@@ -2611,11 +2801,509 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
TEST_SINGLE(uxtl2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "uxtl2 v30.4s, v29.8h");
TEST_SINGLE(uxtl2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "uxtl2 v30.2d, v29.4s");
//// XXX: UCVTF/FCVTZU
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.16b, v29.16b, #1");
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "ucvtf v30.16b, v29.16b, #7");
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.8h, v29.8h, #1");
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "ucvtf v30.8h, v29.8h, #15");
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.4s, v29.4s, #1");
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "ucvtf v30.4s, v29.4s, #31");
TEST_SINGLE(ucvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.2d, v29.2d, #1");
TEST_SINGLE(ucvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "ucvtf v30.2d, v29.2d, #63");
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.8b, v29.8b, #1");
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "ucvtf v30.8b, v29.8b, #7");
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.4h, v29.4h, #1");
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "ucvtf v30.4h, v29.4h, #15");
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.2s, v29.2s, #1");
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "ucvtf v30.2s, v29.2s, #31");
//TEST_SINGLE(ucvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.1d, v29.1d, #1");
//TEST_SINGLE(ucvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "ucvtf v30.1d, v29.1d, #63");
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.16b, v29.16b, #1");
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "fcvtzu v30.16b, v29.16b, #7");
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.8h, v29.8h, #1");
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "fcvtzu v30.8h, v29.8h, #15");
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.4s, v29.4s, #1");
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "fcvtzu v30.4s, v29.4s, #31");
TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.2d, v29.2d, #1");
TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "fcvtzu v30.2d, v29.2d, #63");
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.8b, v29.8b, #1");
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "fcvtzu v30.8b, v29.8b, #7");
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.4h, v29.4h, #1");
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "fcvtzu v30.4h, v29.4h, #15");
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.2s, v29.2s, #1");
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "fcvtzu v30.2s, v29.2s, #31");
//TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.1d, v29.1d, #1");
//TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "fcvtzu v30.1d, v29.1d, #63");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD vector x indexed element") {
// TODO: Implement in emitter.
TEST_SINGLE(smlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(smlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlal v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlal v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlal v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlal v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(smlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(smlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlal2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlal2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlal2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlal2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(sqdmlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(sqdmlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlal v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlal v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlal v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlal v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(sqdmlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(sqdmlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlal2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlal2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlal2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlal2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(smlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(smlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlsl v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlsl v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlsl v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlsl v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(smlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(smlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlsl2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlsl2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlsl2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlsl2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(sqdmlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(sqdmlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlsl v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlsl v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlsl v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlsl v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(sqdmlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(sqdmlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlsl2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlsl2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlsl2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlsl2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(mul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mul v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(mul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "mul v30.8h, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "mul v30.4s, v29.4s, v28.s[0]");
//TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "mul v30.4s, v29.4s, v28.s[3]");
TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mul v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "mul v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(mul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mul v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(mul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "mul v30.4h, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "mul v30.2s, v29.2s, v28.s[0]");
//TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "mul v30.2s, v29.2s, v28.s[3]");
TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mul v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "mul v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(smull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(smull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smull v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smull v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smull v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smull v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(smull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(smull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smull2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smull2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smull2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smull2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(sqdmull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(sqdmull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmull v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmull v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmull v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmull v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(sqdmull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(sqdmull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmull2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmull2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmull2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmull2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqdmulh v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqdmulh v30.8h, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqdmulh v30.4s, v29.4s, v28.s[0]");
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqdmulh v30.4s, v29.4s, v28.s[3]");
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqdmulh v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqdmulh v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqdmulh v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqdmulh v30.4h, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqdmulh v30.2s, v29.2s, v28.s[0]");
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqdmulh v30.2s, v29.2s, v28.s[3]");
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqdmulh v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqdmulh v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmulh v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqrdmulh v30.8h, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqrdmulh v30.4s, v29.4s, v28.s[0]");
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqrdmulh v30.4s, v29.4s, v28.s[3]");
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmulh v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqrdmulh v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmulh v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqrdmulh v30.4h, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqrdmulh v30.2s, v29.2s, v28.s[0]");
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqrdmulh v30.2s, v29.2s, v28.s[3]");
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmulh v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqrdmulh v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q28, 0), "sdot v30.4s, v29.16b, v28.4b[0]");
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q28, 3), "sdot v30.4s, v29.16b, v28.4b[3]");
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q15, 0), "sdot v30.4s, v29.16b, v15.4b[0]");
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q15, 3), "sdot v30.4s, v29.16b, v15.4b[3]");
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d28, 0), "sdot v30.2s, v29.8b, v28.4b[0]");
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d28, 3), "sdot v30.2s, v29.8b, v28.4b[3]");
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d15, 0), "sdot v30.2s, v29.8b, v15.4b[0]");
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d15, 3), "sdot v30.2s, v29.8b, v15.4b[3]");
TEST_SINGLE(fmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmla v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(fmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "fmla v30.8h, v29.8h, v15.h[7]");
TEST_SINGLE(fmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmla v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(fmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "fmla v30.4h, v29.4h, v15.h[7]");
TEST_SINGLE(fmls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmls v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(fmls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "fmls v30.8h, v29.8h, v15.h[7]");
TEST_SINGLE(fmls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmls v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(fmls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "fmls v30.4h, v29.4h, v15.h[7]");
TEST_SINGLE(fmul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmul v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(fmul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "fmul v30.8h, v29.8h, v15.h[7]");
TEST_SINGLE(fmul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmul v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(fmul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "fmul v30.4h, v29.4h, v15.h[7]");
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q28, 0), "sudot v30.4s, v29.16b, v28.4b[0]");
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q28, 3), "sudot v30.4s, v29.16b, v28.4b[3]");
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q15, 0), "sudot v30.4s, v29.16b, v15.4b[0]");
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q15, 3), "sudot v30.4s, v29.16b, v15.4b[3]");
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d28, 0), "sudot v30.2s, v29.8b, v28.4b[0]");
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d28, 3), "sudot v30.2s, v29.8b, v28.4b[3]");
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d15, 0), "sudot v30.2s, v29.8b, v15.4b[0]");
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d15, 3), "sudot v30.2s, v29.8b, v15.4b[3]");
// Unimplemented in vixl disassembler
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q28, 0), "bfdot v30.4s, v29.8h, v28.2h[0]");
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q28, 3), "bfdot v30.4s, v29.8h, v28.2h[3]");
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q15, 0), "bfdot v30.4s, v29.8h, v15.2h[0]");
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q15, 3), "bfdot v30.4s, v29.8h, v15.2h[3]");
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d28, 0), "bfdot v30.2s, v29.4h, v28.2h[0]");
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d28, 3), "bfdot v30.2s, v29.4h, v28.2h[3]");
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d15, 0), "bfdot v30.2s, v29.4h, v15.2h[0]");
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d15, 3), "bfdot v30.2s, v29.4h, v15.2h[3]");
TEST_SINGLE(fmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmla v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(fmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "fmla v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(fmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmla v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(fmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "fmla v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(fmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmla v30.2d, v29.2d, v15.d[0]");
TEST_SINGLE(fmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 1), "fmla v30.2d, v29.2d, v15.d[1]");
//TEST_SINGLE(fmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmla v30.1d, v29.1d, v15.d[0]");
//TEST_SINGLE(fmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 1), "fmla v30.1d, v29.1d, v15.d[1]");
TEST_SINGLE(fmls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmls v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(fmls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "fmls v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(fmls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmls v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(fmls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "fmls v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(fmls(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmls v30.2d, v29.2d, v15.d[0]");
TEST_SINGLE(fmls(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 1), "fmls v30.2d, v29.2d, v15.d[1]");
//TEST_SINGLE(fmls(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmls v30.1d, v29.1d, v15.d[0]");
//TEST_SINGLE(fmls(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 1), "fmls v30.1d, v29.1d, v15.d[1]");
TEST_SINGLE(fmul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmul v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(fmul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "fmul v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(fmul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmul v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(fmul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "fmul v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(fmul(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmul v30.2d, v29.2d, v15.d[0]");
TEST_SINGLE(fmul(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 1), "fmul v30.2d, v29.2d, v15.d[1]");
//TEST_SINGLE(fmul(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmul v30.1d, v29.1d, v15.d[0]");
//TEST_SINGLE(fmul(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 1), "fmul v30.1d, v29.1d, v15.d[1]");
TEST_SINGLE(fmlal(QReg::q30, QReg::q29, QReg::q15, 0), "fmlal v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(fmlal(QReg::q30, QReg::q29, QReg::q15, 7), "fmlal v30.4s, v29.4h, v15.h[7]");
TEST_SINGLE(fmlal(DReg::d30, DReg::d29, DReg::d15, 0), "fmlal v30.2s, v29.2h, v15.h[0]");
TEST_SINGLE(fmlal(DReg::d30, DReg::d29, DReg::d15, 7), "fmlal v30.2s, v29.2h, v15.h[7]");
TEST_SINGLE(fmlal2(QReg::q30, QReg::q29, QReg::q15, 0), "fmlal2 v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(fmlal2(QReg::q30, QReg::q29, QReg::q15, 7), "fmlal2 v30.4s, v29.4h, v15.h[7]");
TEST_SINGLE(fmlal2(DReg::d30, DReg::d29, DReg::d15, 0), "fmlal2 v30.2s, v29.2h, v15.h[0]");
TEST_SINGLE(fmlal2(DReg::d30, DReg::d29, DReg::d15, 7), "fmlal2 v30.2s, v29.2h, v15.h[7]");
TEST_SINGLE(fmlsl(QReg::q30, QReg::q29, QReg::q15, 0), "fmlsl v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(fmlsl(QReg::q30, QReg::q29, QReg::q15, 7), "fmlsl v30.4s, v29.4h, v15.h[7]");
TEST_SINGLE(fmlsl(DReg::d30, DReg::d29, DReg::d15, 0), "fmlsl v30.2s, v29.2h, v15.h[0]");
TEST_SINGLE(fmlsl(DReg::d30, DReg::d29, DReg::d15, 7), "fmlsl v30.2s, v29.2h, v15.h[7]");
TEST_SINGLE(fmlsl2(QReg::q30, QReg::q29, QReg::q15, 0), "fmlsl2 v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(fmlsl2(QReg::q30, QReg::q29, QReg::q15, 7), "fmlsl2 v30.4s, v29.4h, v15.h[7]");
TEST_SINGLE(fmlsl2(DReg::d30, DReg::d29, DReg::d15, 0), "fmlsl2 v30.2s, v29.2h, v15.h[0]");
TEST_SINGLE(fmlsl2(DReg::d30, DReg::d29, DReg::d15, 7), "fmlsl2 v30.2s, v29.2h, v15.h[7]");
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q28, 0), "usdot v30.4s, v29.16b, v28.4b[0]");
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q28, 3), "usdot v30.4s, v29.16b, v28.4b[3]");
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q15, 0), "usdot v30.4s, v29.16b, v15.4b[0]");
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q15, 3), "usdot v30.4s, v29.16b, v15.4b[3]");
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d28, 0), "usdot v30.2s, v29.8b, v28.4b[0]");
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d28, 3), "usdot v30.2s, v29.8b, v28.4b[3]");
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d15, 0), "usdot v30.2s, v29.8b, v15.4b[0]");
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d15, 3), "usdot v30.2s, v29.8b, v15.4b[3]");
// Unimplemented in vixl disassembler
//TEST_SINGLE(bfmlalb(VReg::v30, VReg::v29, VReg::v15, 0), "bfmlalb v30.4s, v29.8h, v15.h[0]");
//TEST_SINGLE(bfmlalb(VReg::v30, VReg::v29, VReg::v15, 7), "bfmlalb v30.4s, v29.8h, v15.h[7]");
//TEST_SINGLE(bfmlalt(VReg::v30, VReg::v29, VReg::v15, 0), "bfmlalt v30.4s, v29.8h, v15.h[0]");
//TEST_SINGLE(bfmlalt(VReg::v30, VReg::v29, VReg::v15, 7), "bfmlalt v30.4s, v29.8h, v15.h[7]");
TEST_SINGLE(mla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mla v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(mla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "mla v30.8h, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "mla v30.4s, v29.4s, v28.s[0]");
//TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "mla v30.4s, v29.4s, v28.s[3]");
TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mla v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "mla v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(mla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mla v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(mla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "mla v30.4h, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "mla v30.2s, v29.2s, v28.s[0]");
//TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "mla v30.2s, v29.2s, v28.s[3]");
TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mla v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "mla v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(umlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(umlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlal v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlal v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlal v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlal v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(umlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(umlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlal2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlal2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlal2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlal2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(mls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mls v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(mls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "mls v30.8h, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "mls v30.4s, v29.4s, v28.s[0]");
//TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "mls v30.4s, v29.4s, v28.s[3]");
TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mls v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "mls v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(mls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mls v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(mls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "mls v30.4h, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "mls v30.2s, v29.2s, v28.s[0]");
//TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "mls v30.2s, v29.2s, v28.s[3]");
TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mls v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "mls v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(umlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(umlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlsl v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlsl v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlsl v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlsl v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(umlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(umlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlsl2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlsl2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlsl2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlsl2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(umull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull v30.4s, v29.4h, v15.h[0]");
TEST_SINGLE(umull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umull v30.4s, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umull v30.2d, v29.2s, v28.s[0]");
//TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umull v30.2d, v29.2s, v28.s[3]");
TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull v30.2d, v29.2s, v15.s[0]");
TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umull v30.2d, v29.2s, v15.s[3]");
TEST_SINGLE(umull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull2 v30.4s, v29.8h, v15.h[0]");
TEST_SINGLE(umull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umull2 v30.4s, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umull2 v30.2d, v29.4s, v28.s[0]");
//TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umull2 v30.2d, v29.4s, v28.s[3]");
TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull2 v30.2d, v29.4s, v15.s[0]");
TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umull2 v30.2d, v29.4s, v15.s[3]");
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlah v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqrdmlah v30.8h, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqrdmlah v30.4s, v29.4s, v28.s[0]");
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqrdmlah v30.4s, v29.4s, v28.s[3]");
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlah v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqrdmlah v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlah v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqrdmlah v30.4h, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqrdmlah v30.2s, v29.2s, v28.s[0]");
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqrdmlah v30.2s, v29.2s, v28.s[3]");
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlah v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqrdmlah v30.2s, v29.2s, v15.s[3]");
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q28, 0), "udot v30.4s, v29.16b, v28.4b[0]");
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q28, 3), "udot v30.4s, v29.16b, v28.4b[3]");
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q15, 0), "udot v30.4s, v29.16b, v15.4b[0]");
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q15, 3), "udot v30.4s, v29.16b, v15.4b[3]");
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d28, 0), "udot v30.2s, v29.8b, v28.4b[0]");
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d28, 3), "udot v30.2s, v29.8b, v28.4b[3]");
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d15, 0), "udot v30.2s, v29.8b, v15.4b[0]");
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d15, 3), "udot v30.2s, v29.8b, v15.4b[3]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlsh v30.8h, v29.8h, v15.h[0]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqrdmlsh v30.8h, v29.8h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqrdmlsh v30.4s, v29.4s, v28.s[0]");
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqrdmlsh v30.4s, v29.4s, v28.s[3]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlsh v30.4s, v29.4s, v15.s[0]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqrdmlsh v30.4s, v29.4s, v15.s[3]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlsh v30.4h, v29.4h, v15.h[0]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqrdmlsh v30.4h, v29.4h, v15.h[7]");
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqrdmlsh v30.2s, v29.2s, v28.s[0]");
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqrdmlsh v30.2s, v29.2s, v28.s[3]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlsh v30.2s, v29.2s, v15.s[0]");
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqrdmlsh v30.2s, v29.2s, v15.s[3]");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Cryptographic three-register, imm2") {
// TODO: Implement in emitter.
@@ -2630,7 +3318,61 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Cryptographic two-register S
// TODO: Implement in emitter.
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Conversion between floating-point and fixed-point") {
// TODO: Implement in emitter.
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "scvtf h29, w30, #1");
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "scvtf h29, w30, #32");
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "scvtf s29, w30, #1");
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "scvtf s29, w30, #32");
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "scvtf d29, w30, #1");
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "scvtf d29, w30, #32");
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "scvtf h29, x30, #1");
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "scvtf h29, x30, #64");
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "scvtf s29, x30, #1");
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "scvtf s29, x30, #64");
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "scvtf d29, x30, #1");
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "scvtf d29, x30, #64");
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "ucvtf h29, w30, #1");
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "ucvtf h29, w30, #32");
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "ucvtf s29, w30, #1");
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "ucvtf s29, w30, #32");
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "ucvtf d29, w30, #1");
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "ucvtf d29, w30, #32");
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "ucvtf h29, x30, #1");
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "ucvtf h29, x30, #64");
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "ucvtf s29, x30, #1");
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "ucvtf s29, x30, #64");
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "ucvtf d29, x30, #1");
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "ucvtf d29, x30, #64");
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzs w30, h29, #1");
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 32), "fcvtzs w30, h29, #32");
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzs w30, s29, #1");
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 32), "fcvtzs w30, s29, #32");
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzs w30, d29, #1");
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 32), "fcvtzs w30, d29, #32");
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzs x30, h29, #1");
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 64), "fcvtzs x30, h29, #64");
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzs x30, s29, #1");
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 64), "fcvtzs x30, s29, #64");
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzs x30, d29, #1");
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 64), "fcvtzs x30, d29, #64");
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzu w30, h29, #1");
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 32), "fcvtzu w30, h29, #32");
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzu w30, s29, #1");
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 32), "fcvtzu w30, s29, #32");
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzu w30, d29, #1");
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 32), "fcvtzu w30, d29, #32");
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzu x30, h29, #1");
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 64), "fcvtzu x30, h29, #64");
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzu x30, s29, #1");
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 64), "fcvtzu x30, s29, #64");
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzu x30, d29, #1");
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 64), "fcvtzu x30, d29, #64");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Conversion between floating-point and integer") {
TEST_SINGLE(fcvtns(Size::i32Bit, Reg::r29, HReg::h30), "fcvtns w29, h30");
+1 -1
View File
@@ -1,4 +1,4 @@
if (ENABLE_VIXL_DISASSEMBLER)
if (COMPILE_VIXL_DISASSEMBLER)
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS *.cpp)
set (LIBS fmt::fmt vixl Catch2::Catch2WithMain FEXCore_Base)
+109 -9
View File
@@ -39,10 +39,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: Base Encodings") {
TEST_SINGLE(mov(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29), "mov z30.s, p6/m, z29.s");
TEST_SINGLE(mov(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29), "mov z30.d, p6/m, z29.d");
//TEST_SINGLE(mov(SubRegSize::i128Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29), "mov z30.q, p6/m, z29.q");
// TODO: HISTCNT
// TODO: FCMLA
// TODO: FCADD
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer add/subtract vectors (unpredicated)") {
TEST_SINGLE(add(SubRegSize::i8Bit, ZReg::z30, ZReg::z29, ZReg::z28), "add z30.b, z29.b, z28.b");
@@ -245,7 +241,15 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer clamp") {
// TODO: Implement in emitter.
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 character match") {
// TODO: Implement in emitter.
TEST_SINGLE(match(SubRegSize::i8Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
"match p8.b, p6/z, z30.b, z29.b");
TEST_SINGLE(match(SubRegSize::i16Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
"match p8.h, p6/z, z30.h, z29.h");
TEST_SINGLE(nmatch(SubRegSize::i8Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
"nmatch p8.b, p6/z, z30.b, z29.b");
TEST_SINGLE(nmatch(SubRegSize::i16Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
"nmatch p8.h, p6/z, z30.h, z29.h");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point convert precision odd elements") {
TEST_SINGLE(fcvtxnt(ZReg::z30, PReg::p6.Merging(), ZReg::z29), "fcvtxnt z30.s, p6/m, z29.d");
@@ -297,11 +301,72 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 floating-point pairwise o
TEST_SINGLE(fmin(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28), "fmin z30.d, p6/m, z30.d, z28.d");
//TEST_SINGLE(fmin(SubRegSize::i128Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28), "fmin z30.q, p6/m, z30.q, z28.q");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point complex add") {
TEST_SINGLE(fcadd(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_90),
"fcadd z30.h, p6/m, z30.h, z28.h, #90");
TEST_SINGLE(fcadd(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_90),
"fcadd z30.s, p6/m, z30.s, z28.s, #90");
TEST_SINGLE(fcadd(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_90),
"fcadd z30.d, p6/m, z30.d, z28.d, #90");
TEST_SINGLE(fcadd(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_270),
"fcadd z30.h, p6/m, z30.h, z28.h, #270");
TEST_SINGLE(fcadd(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_270),
"fcadd z30.s, p6/m, z30.s, z28.s, #270");
TEST_SINGLE(fcadd(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_270),
"fcadd z30.d, p6/m, z30.d, z28.d, #270");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point multiply-add (vector)") {
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_0),
"fcmla z30.h, p6/m, z10.h, z28.h, #0");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_0),
"fcmla z30.s, p6/m, z10.s, z28.s, #0");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_0),
"fcmla z30.d, p6/m, z10.d, z28.d, #0");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_90),
"fcmla z30.h, p6/m, z10.h, z28.h, #90");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_90),
"fcmla z30.s, p6/m, z10.s, z28.s, #90");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_90),
"fcmla z30.d, p6/m, z10.d, z28.d, #90");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_180),
"fcmla z30.h, p6/m, z10.h, z28.h, #180");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_180),
"fcmla z30.s, p6/m, z10.s, z28.s, #180");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_180),
"fcmla z30.d, p6/m, z10.d, z28.d, #180");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_270),
"fcmla z30.h, p6/m, z10.h, z28.h, #270");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_270),
"fcmla z30.s, p6/m, z10.s, z28.s, #270");
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_270),
"fcmla z30.d, p6/m, z10.d, z28.d, #270");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point multiply-add (indexed)") {
// TODO: Implement in emitter.
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point complex multiply-add (indexed)") {
// TODO: Implement in emitter.
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 0, Rotation::ROTATE_0),
"fcmla z30.h, z10.h, z7.h[0], #0");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 0, Rotation::ROTATE_0),
"fcmla z30.s, z10.s, z15.s[0], #0");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 1, Rotation::ROTATE_90),
"fcmla z30.h, z10.h, z7.h[1], #90");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 1, Rotation::ROTATE_90),
"fcmla z30.s, z10.s, z15.s[1], #90");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 1, Rotation::ROTATE_180),
"fcmla z30.s, z10.s, z15.s[1], #180");
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 1, Rotation::ROTATE_270),
"fcmla z30.s, z10.s, z15.s[1], #270");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 2, Rotation::ROTATE_180),
"fcmla z30.h, z10.h, z7.h[2], #180");
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 3, Rotation::ROTATE_270),
"fcmla z30.h, z10.h, z7.h[3], #270");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point multiply (indexed)") {
// TODO: Implement in emitter.
@@ -368,7 +433,25 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point arithmetic
//TEST_SINGLE(frsqrts(SubRegSize::i128Bit, ZReg::z30, ZReg::z29, ZReg::z28), "frsqrts z30.q, z29.q, z28.q");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point recursive reduction") {
// TODO: Implement in emitter.
TEST_SINGLE(faddv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "faddv h30, p7, z28.h");
TEST_SINGLE(faddv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "faddv s30, p7, z28.s");
TEST_SINGLE(faddv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "faddv d30, p7, z28.d");
TEST_SINGLE(fmaxnmv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxnmv h30, p7, z28.h");
TEST_SINGLE(fmaxnmv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxnmv s30, p7, z28.s");
TEST_SINGLE(fmaxnmv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxnmv d30, p7, z28.d");
TEST_SINGLE(fminnmv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fminnmv h30, p7, z28.h");
TEST_SINGLE(fminnmv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fminnmv s30, p7, z28.s");
TEST_SINGLE(fminnmv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fminnmv d30, p7, z28.d");
TEST_SINGLE(fmaxv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxv h30, p7, z28.h");
TEST_SINGLE(fmaxv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxv s30, p7, z28.s");
TEST_SINGLE(fmaxv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxv d30, p7, z28.d");
TEST_SINGLE(fminv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fminv h30, p7, z28.h");
TEST_SINGLE(fminv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fminv s30, p7, z28.s");
TEST_SINGLE(fminv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fminv d30, p7, z28.d");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer multiply-accumulate writing addend (predicated)") {
@@ -380,7 +463,20 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer multiply-add writi
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer add/subtract vectors (predicated)") {
// TODO: Implement in emitter.
TEST_SINGLE(add(SubRegSize::i8Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.b, p7/m, z30.b, z28.b");
TEST_SINGLE(add(SubRegSize::i16Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.h, p7/m, z30.h, z28.h");
TEST_SINGLE(add(SubRegSize::i32Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.s, p7/m, z30.s, z28.s");
TEST_SINGLE(add(SubRegSize::i64Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.d, p7/m, z30.d, z28.d");
TEST_SINGLE(sub(SubRegSize::i8Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.b, p7/m, z30.b, z28.b");
TEST_SINGLE(sub(SubRegSize::i16Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.h, p7/m, z30.h, z28.h");
TEST_SINGLE(sub(SubRegSize::i32Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.s, p7/m, z30.s, z28.s");
TEST_SINGLE(sub(SubRegSize::i64Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.d, p7/m, z30.d, z28.d");
TEST_SINGLE(subr(SubRegSize::i8Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.b, p7/m, z30.b, z28.b");
TEST_SINGLE(subr(SubRegSize::i16Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.h, p7/m, z30.h, z28.h");
TEST_SINGLE(subr(SubRegSize::i32Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.s, p7/m, z30.s, z28.s");
TEST_SINGLE(subr(SubRegSize::i64Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.d, p7/m, z30.d, z28.d");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer min/max/difference (predicated)") {
@@ -1683,8 +1779,12 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 bitwise shift right narro
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 integer add/subtract narrow high part") {
// TODO: Implement in emitter.
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 Histogram Computation") {
TEST_SINGLE(histcnt(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29, ZReg::z28), "histcnt z30.s, p6/z, z29.s, z28.s");
TEST_SINGLE(histcnt(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29, ZReg::z28), "histcnt z30.d, p6/z, z29.d, z28.d");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 Histogram Computation - Segment") {
// TODO: Implement in emitter.
TEST_SINGLE(histseg(ZReg::z30, ZReg::z29, ZReg::z28), "histseg z30.b, z29.b, z28.b");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 crypto unary operations") {
// TODO: Implement in emitter.
+3 -1
View File
@@ -12,7 +12,9 @@ public:
TestDisassembler() {
fp = tmpfile();
Disasm = std::make_unique<vixl::aarch64::PrintDisassembler>(fp);
SetBuffer(reinterpret_cast<uint8_t*>(mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), 4096);
// 2MB code size.
const size_t CodeSize = 2 * 1024 * 1024;
SetBuffer(reinterpret_cast<uint8_t*>(mmap(nullptr, CodeSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), CodeSize);
BufferBegin = GetCursorAddress<const vixl::aarch64::Instruction*>();
}
~TestDisassembler() {
+1 -1
+1 -1
@@ -0,0 +1,28 @@
#pragma once
#include <fcntl.h>
#include <string>
#include <sys/stat.h>
#include <span>
#include <unistd.h>
namespace FHU::Symlinks {
// Checks to see if a filepath is a symlink.
inline bool IsSymlink(const std::string &Filename) {
struct stat Buffer{};
int Result = lstat(Filename.c_str(), &Buffer);
return Result == 0 && S_ISLNK(Buffer.st_mode);
}
// Resolves a symlink path.
// Doesn't handle recursive symlinks.
// Doesn't append null terminator character.
// Returns a string_view of the resolved path, or an empty view on error.
inline std::string_view ResolveSymlink(const std::string &Filename, std::span<char> ResultBuffer) {
ssize_t Result = readlink(Filename.c_str(), ResultBuffer.data(), ResultBuffer.size());
if (Result == -1) {
return {};
}
return std::string_view(ResultBuffer.data(), Result);
}
}
+1 -1
View File
@@ -61,7 +61,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
#if defined(HAS_SYSCALL_TGKILL) && HAS_SYSCALL_TGKILL
return ::tgkill(tgid, tid, sig);
#else
return ::syscall(SYS_tgkill, tgid, tid, sig);
+4 -4
View File
@@ -307,12 +307,12 @@ def GetRootFSPath():
def CheckRootFSInstallStatus():
# 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",
"20.04": "Ubuntu_20_04.sqsh",
"20.04": "Ubuntu_20_04.ero",
"22.04": "Ubuntu_22_04.sqsh",
"22.04": "Ubuntu_22_04.ero",
"22.10": "Ubuntu_22_10.sqsh",
"22.10": "Ubuntu_22_10.ero",
}
return os.path.exists(GetRootFSPath() + UbuntuVersionToRootFS[GetDistro()[1]])
+2
View File
@@ -72,6 +72,7 @@ class HostFeatures(Flag) :
FEATURE_CLZERO = (1 << 5)
FEATURE_BMI1 = (1 << 6)
FEATURE_BMI2 = (1 << 7)
FEATURE_CLWB = (1 << 8)
RegStringLookup = {
"NONE": Regs.REG_NONE,
@@ -143,6 +144,7 @@ HostFeaturesLookup = {
"CLZERO" : HostFeatures.FEATURE_CLZERO,
"BMI1" : HostFeatures.FEATURE_BMI1,
"BMI2" : HostFeatures.FEATURE_BMI2,
"CLWB" : HostFeatures.FEATURE_CLWB,
}
def parse_hexstring(s):
+70 -4
View File
@@ -2,11 +2,13 @@
#include "Common/Config.h"
#include <FEXCore/Config/Config.h>
#include <FEXHeaderUtils/SymlinkChecks.h>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <map>
#include <linux/limits.h>
#include <list>
#include <unordered_map>
#include <utility>
@@ -40,12 +42,75 @@ namespace FEX::Config {
}
}
std::pair<std::string, std::string> LoadConfig(
std::string RecoverGuestProgramFilename(std::string Program, bool ExecFDInterp, const std::string_view ProgramFDFromEnv) {
// If executed with a FEX FD then the Program argument might be empty.
// In this case we need to scan the FD node to recover the application binary that exists on disk.
// Only do this if the Program argument is empty, since we would prefer the application's expectation
// of application name.
if (!ProgramFDFromEnv.empty() && Program.empty()) {
// Get the `dev` node of the execveat fd string.
Program = "/dev/fd/";
Program += ProgramFDFromEnv;
}
// If we were provided a relative path then we need to canonicalize it to become absolute.
// If the program name isn't resolved to an absolute path then glibc breaks inside it's `_dl_get_origin` function.
// This is because we rewrite `/proc/self/exe` to the absolute program path calculated in here.
if (!Program.starts_with('/')) {
Program = std::filesystem::canonical(std::move(Program)).string();
}
// If FEX was invoked through an FD path (either binfmt_misc or execveat) then we need to check the
// Program to see if it is a symlink to find the real path.
//
// binfmt_misc: Arg[0] is actually the execve `pathname` argument or `/dev/fd/<FD>` path
// - `pathname` with execve (See Side Note)
// - FD path with execveat and FD doesn't have an existing file on the disk
//
// ProgramFDFromEnv: Arg[0] is Application provided data or `/dev/fd/<FD>` from above fix-up.
// - execveat was either passed no arguments (argv=NULL) or the first argument is an empty string (argv[0]="").
// - FD path with execveat and FD doesn't have an existing file on the disk
//
// Side Note:
// The `execve` syscall doesn't take an FD but binfmt_misc will give FEX an FD to execute still.
// Arg[0] will always contain the `pathname` argument provided to execve.
// It does not resolve symlinks, and it does not convert the path to absolute.
//
// Examples:
// - Regular execve. Application must exist on disk.
// execve binfmt_misc args layout: `FEXInterpreter <Path provided to execve pathname> <user provided argv[0]> <user provided argv[n]>...`
// - Regular execveat with FD. FD is backed by application on disk.
// execveat binfmt_misc args layout: `FEXInterpreter <Path provided to execve pathname> <user provided argv[0]> <user provided argv[n]>...`
// - Regular execveat with FD. FD points to file on disk that has been deleted.
// execveat binfmt_misc args layout: `FEXInterpreter /dev/fd/<FD> <user provided argv[0]> <user provided argv[n]>...`
if (ExecFDInterp || !ProgramFDFromEnv.empty()) {
// Only in the case that FEX is executing an FD will the program argument potentially be a symlink.
// This symlink will be in the style of `/dev/fd/<FD>`.
//
// If the argument /is/ a symlink then resolve its path to get the original application name.
if (FHU::Symlinks::IsSymlink(Program)) {
char Filename[PATH_MAX];
auto SymlinkPath = FHU::Symlinks::ResolveSymlink(Program, Filename);
if (SymlinkPath.starts_with('/')) {
// This file was executed through an FD.
// Remove the ` (deleted)` text if the file was deleted after the fact.
// Otherwise just get the symlink without the deleted text.
return std::string{SymlinkPath.substr(0, SymlinkPath.rfind(" (deleted)"))};
}
}
}
return Program;
}
ApplicationNames LoadConfig(
bool NoFEXArguments,
bool LoadProgramConfig,
int argc,
char **argv,
char **const envp) {
char **const envp,
bool ExecFDInterp,
const std::string_view ProgramFDFromEnv) {
FEXCore::Config::Initialize();
FEXCore::Config::AddLayer(FEXCore::Config::CreateGlobalMainLayer());
FEXCore::Config::AddLayer(FEXCore::Config::CreateMainLayer());
@@ -68,7 +133,8 @@ namespace FEX::Config {
return {};
}
std::string Program = Args[0];
Args[0] = RecoverGuestProgramFilename(std::move(Args[0]), ExecFDInterp, ProgramFDFromEnv);
std::string& Program = Args[0];
bool Wine = false;
std::filesystem::path ProgramName;
@@ -121,7 +187,7 @@ namespace FEX::Config {
FEXCore::Config::AddLayer(FEXCore::Config::CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_LOCAL_STEAM_APP));
}
return std::make_pair(Program, ProgramName);
return ApplicationNames{std::move(Program), std::move(ProgramName)};
}
return {};
}
+24 -2
View File
@@ -19,11 +19,33 @@ namespace FEX::Config {
void SaveLayerToJSON(const std::string& Filename, FEXCore::Config::Layer *const Layer);
std::pair<std::string, std::string> LoadConfig(
struct ApplicationNames {
// This is the full path to the program (if it exists).
std::string ProgramPath;
// This is the program executable name (if it exists).
std::string ProgramName;
};
/**
* @brief Loads the FEX and application configurations for the application that is getting ready to run.
*
* @param NoFEXArguments Do we want to parse FEXLoader arguments, Or is this FEXInterpreter?
* @param LoadProgramConfig Do we want to load application specific configurations?
* @param argc The `argc` passed to main(...)
* @param argv The `argv` passed to main(...)
* @param envp The `envp` passed to main(...)
* @param ExecFDInterp If FEX was executed with binfmt_misc FD argument
* @param ProgramFDFromEnv The execveat FD argument passed through FEX
*
* @return The application name and path structure
*/
ApplicationNames LoadConfig(
bool NoFEXArguments,
bool LoadProgramConfig,
int argc,
char **argv,
char **const envp
char **const envp,
bool ExecFDInterp,
const std::string_view ProgramFDFromEnv
);
}
+65 -47
View File
@@ -12,65 +12,83 @@ $end_info$
#include <algorithm>
#include <cstring>
#include <elf.h>
#include <fcntl.h>
#include <filesystem>
#include <fstream>
#include <memory>
#include <system_error>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>
namespace ELFLoader {
ELFContainer::ELFType ELFContainer::GetELFType(std::string const &Filename) {
std::fstream ELFFile(Filename, std::fstream::in | std::fstream::binary);
if (!ELFFile.is_open()) {
return ELFType::TYPE_NONE;
}
ELFFile.seekg(0, ELFFile.end);
size_t FileSize = ELFFile.tellg();
ELFFile.seekg(0, ELFFile.beg);
size_t ELFHeaderSize = std::max(sizeof(Elf32_Ehdr), sizeof(Elf64_Ehdr));
if (FileSize < ELFHeaderSize) {
return ELFType::TYPE_NONE;
}
FileSize = ELFHeaderSize;
std::vector<char> RawFile(FileSize);
ELFFile.read(RawFile.data(), FileSize);
ELFFile.close();
uint8_t *Ident = reinterpret_cast<uint8_t*>(&RawFile.at(0));
if (Ident[EI_MAG0] != ELFMAG0 ||
Ident[EI_MAG1] != ELFMAG1 ||
Ident[EI_MAG2] != ELFMAG2 ||
Ident[EI_MAG3] != ELFMAG3) {
return ELFType::TYPE_NONE;
}
union {
Elf32_Ehdr _32;
Elf64_Ehdr _64;
} Header;
if (Ident[EI_CLASS] == ELFCLASS32) {
memcpy(&Header, reinterpret_cast<Elf32_Ehdr *>(&RawFile.at(0)),
sizeof(Elf32_Ehdr));
if (Header._32.e_machine == EM_386) {
return ELFType::TYPE_X86_32;
static ELFContainer::ELFType CheckELFType(uint8_t* Data) {
if (Data[EI_MAG0] != ELFMAG0 ||
Data[EI_MAG1] != ELFMAG1 ||
Data[EI_MAG2] != ELFMAG2 ||
Data[EI_MAG3] != ELFMAG3) {
return ELFContainer::ELFType::TYPE_NONE;
}
}
else if (Ident[EI_CLASS] == ELFCLASS64) {
memcpy(&Header, reinterpret_cast<Elf64_Ehdr *>(&RawFile.at(0)),
sizeof(Elf64_Ehdr));
if (Header._64.e_machine == EM_X86_64) {
return ELFType::TYPE_X86_64;
if (Data[EI_CLASS] == ELFCLASS32) {
Elf32_Ehdr *Header = reinterpret_cast<Elf32_Ehdr *>(Data);
if (Header->e_machine == EM_386) {
return ELFContainer::ELFType::TYPE_X86_32;
}
}
else if (Data[EI_CLASS] == ELFCLASS64) {
Elf64_Ehdr *Header = reinterpret_cast<Elf64_Ehdr *>(Data);
if (Header->e_machine == EM_X86_64) {
return ELFContainer::ELFType::TYPE_X86_64;
}
}
return ELFContainer::ELFType::TYPE_OTHER_ELF;
}
return ELFType::TYPE_OTHER_ELF;
ELFContainer::ELFType ELFContainer::GetELFType(std::string const &Filename) {
// Open the Filename to determine if it is a shebang file.
int FD = open(Filename.c_str(), O_RDONLY | O_CLOEXEC);
if (FD == -1) {
return ELFType::TYPE_NONE;
}
auto ELFType = GetELFType(FD);
close(FD);
return ELFType;
}
ELFContainer::ELFType ELFContainer::GetELFType(int FD) {
// We don't know the state of the FD coming in since this might be a guest tracked FD.
// Need to be extra careful here not to adjust file offsets and status flags.
//
// We can't use dup since that makes the FD have the same underlying state backing both FDs.
// We need to first determine the file size through fstat.
struct stat buf{};
if (fstat(FD, &buf) == -1) {
// Couldn't get size.
return ELFType::TYPE_NONE;
}
constexpr size_t ELFHeaderSize = std::max(sizeof(Elf32_Ehdr), sizeof(Elf64_Ehdr));
if (buf.st_size < ELFHeaderSize) {
// Is not a valid ELF.
return ELFType::TYPE_NONE;
}
std::array<char, ELFHeaderSize> RawFile;
// Read the header so we can tell if it is a supported ELF file.
// Can't adjust file offset, so use pread.
if (pread(FD, &RawFile.at(0), RawFile.size(), 0) != RawFile.size()) {
// Couldn't read
LogMan::Msg::EFmt("Couldn't read potential ELF FD");
return ELFType::TYPE_NONE;
}
return CheckELFType(reinterpret_cast<uint8_t*>(&RawFile.at(0)));
}
ELFContainer::ELFContainer(std::string const &Filename, std::string const &RootFS, bool CustomInterpreter) {
+1
View File
@@ -117,6 +117,7 @@ public:
TYPE_OTHER_ELF,
};
static ELFType GetELFType(std::string const &Filename);
static ELFType GetELFType(int FD);
static bool IsSupportedELF(std::string const &Filename) {
ELFType Type = GetELFType(Filename);
return Type == TYPE_X86_64 || Type == TYPE_X86_32;
+32 -63
View File
@@ -7,78 +7,47 @@ if (TERMUX_BUILD)
list(APPEND LIBS android-shmem)
endif()
add_executable(FEXLoader
FEXLoader.cpp
VDSO_Emulation.cpp
AOT/AOTGenerator.cpp)
function(GenerateInterpreter NAME AsInterpreter)
add_executable(${NAME}
FEXLoader.cpp
VDSO_Emulation.cpp
AOT/AOTGenerator.cpp)
# Enable FEX APIs to be used by targets that use target_link_libraries on FEXLoader
set_target_properties(FEXLoader PROPERTIES ENABLE_EXPORTS 1)
set_target_properties(${NAME} PROPERTIES ENABLE_EXPORTS 1)
target_include_directories(FEXLoader
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
target_link_libraries(FEXLoader
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
fmt::fmt
)
if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
target_link_options(FEXLoader
target_include_directories(${NAME}
PRIVATE
"LINKER:--gc-sections"
"LINKER:--strip-all"
"LINKER:--as-needed"
${CMAKE_CURRENT_SOURCE_DIR}/Source/
${CMAKE_BINARY_DIR}/generated
)
endif()
install(TARGETS FEXLoader
RUNTIME
DESTINATION bin
COMPONENT runtime
)
if(TERMUX_BUILD)
# Termux doesn't support hard links, just copy FEXLoader
add_custom_target(FEXInterpreter ALL
COMMAND "cp" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXLoader" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXInterpreter"
DEPENDS FEXLoader
target_link_libraries(${NAME}
PRIVATE
${LIBS}
LinuxEmulation
${PTHREAD_LIB}
fmt::fmt
)
target_compile_definitions(${NAME} PRIVATE -DFEXLOADER_AS_INTERPRETER=${AsInterpreter})
install(
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
CODE "
EXECUTE_PROCESS(COMMAND cp FEXLoader FEXInterpreter
WORKING_DIRECTORY $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/
)"
)
else()
add_custom_target(FEXInterpreter ALL
COMMAND "ln" "-f" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXLoader" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXInterpreter"
DEPENDS FEXLoader
)
install(
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
CODE "
EXECUTE_PROCESS(COMMAND ln -f FEXLoader FEXInterpreter
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"
if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
target_link_options(${NAME}
PRIVATE
"LINKER:--gc-sections"
"LINKER:--strip-all"
"LINKER:--as-needed"
)
add_dependencies(uninstall uninstall_FEXInterpreter)
endif()
endif()
install(TARGETS ${NAME}
RUNTIME
DESTINATION bin
COMPONENT runtime
)
endfunction()
GenerateInterpreter(FEXLoader 0)
GenerateInterpreter(FEXInterpreter 1)
install(PROGRAMS "${PROJECT_SOURCE_DIR}/Scripts/FEXUpdateAOTIRCache.sh" DESTINATION bin RENAME FEXUpdateAOTIRCache)
+41 -12
View File
@@ -27,6 +27,7 @@
#include <FEXCore/Utils/LogManager.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <FEXHeaderUtils/TypeDefines.h>
#include <FEXHeaderUtils/SymlinkChecks.h>
#include <elf.h>
#include <fcntl.h>
@@ -34,6 +35,7 @@
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/personality.h>
#include <sys/random.h>
#define PAGE_START(x) ((x) & ~(uintptr_t)(4095))
#define PAGE_OFFSET(x) ((x) & 4095)
@@ -176,6 +178,17 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
return LoadBase;
}
static bool GetRandom(void *Data, size_t DataSize) {
ssize_t Result{};
do {
// This is guaranteed to not be interrupted by a signal,
// since fewer than 256 bytes of RNG data are requested
Result = getrandom(Data, DataSize, 0);
} while (Result != -1 && Result != DataSize);
return Result != -1;
}
public:
static std::string ResolveRootfsFile(std::string const &File, std::string RootFS) {
@@ -191,10 +204,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
// Do some special handling if the RootFS's linker is a symlink
// Ubuntu's rootFS by default provides an absolute location symlink to the linker
// Resolve this around back to the rootfs
auto SymlinkSize = FEX::HLE::GetSymlink(RootFSLink, Filename, PATH_MAX - 1);
if (SymlinkSize > 0 && Filename[0] == '/') {
auto SymlinkPath = FHU::Symlinks::ResolveSymlink(RootFSLink, Filename);
if (SymlinkPath.starts_with('/')) {
RootFSLink = RootFS;
RootFSLink += std::string_view(Filename, SymlinkSize);
RootFSLink += SymlinkPath;
}
else {
break;
@@ -215,12 +228,24 @@ 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) :
ELFCodeLoader2(std::string const &Filename, const std::string_view FEXFDString, 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} {
bool LoadedWithFD = false;
int FD = getauxval(AT_EXECFD);
if (!FEXFDString.empty()) {
// If we passed the execve FD to us then use that.
const char *StartPtr = FEXFDString.data();
char *EndPtr{};
FD = ::strtol(StartPtr, &EndPtr, 10);
if (EndPtr == StartPtr) {
LogMan::Msg::AFmt("FEXInterpreter passed invalid FD to exececute: {}", FEXFDString);
return;
}
unsetenv("FEX_EXECVEFD");
}
// If we are provided an EXECFD then attempt to execute that first
// This happens in the case of binfmt_misc usage
if (FD != 0) {
@@ -472,9 +497,12 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
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);
uint64_t ASLR_Offset{};
if (!GetRandom(&ASLR_Offset, sizeof(ASLR_Offset))) {
// getrandom failed for some reason.
ASLR_Offset = 0;
LogMan::Msg::EFmt("RNG failed. ASLR will not work.");
}
if (Is64BitMode()) {
ASLR_Offset &= (1ULL << ASLR_BITS_64) - 1;
@@ -720,11 +748,12 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
}
else {
// Nothing provided from the kernel, generate our own random values.
std::random_device rd;
std::uniform_int_distribution<uint64_t> d(0);
RandomLoc[0] = d(rd);
RandomLoc[1] = d(rd);
if (!GetRandom(&RandomLoc[0], sizeof(uint64_t) * 2)) {
// getrandom failed for some reason.
RandomLoc[0] = 0;
RandomLoc[1] = 0;
LogMan::Msg::EFmt("RNG failed. AT_RANDOM will not be random.");
}
}
// Stack setup
+27 -12
View File
@@ -184,7 +184,7 @@ void RootFSRedirect(std::string *Filename, std::string const &RootFS) {
}
bool RanAsInterpreter(const char *Program) {
return ExecutedWithFD || strstr(Program, "FEXInterpreter") != nullptr;
return ExecutedWithFD || FEXLOADER_AS_INTERPRETER;
}
bool IsInterpreterInstalled() {
@@ -200,6 +200,8 @@ int main(int argc, char **argv, char **const envp) {
const bool IsInterpreter = RanAsInterpreter(argv[0]);
ExecutedWithFD = getauxval(AT_EXECFD) != 0;
const char* FEXFD = getenv("FEX_EXECVEFD");
const std::string_view FEXFDView = FEXFD ? std::string_view{FEXFD} : std::string_view{};
LogMan::Throw::InstallHandler(AssertHandler);
LogMan::Msg::InstallHandler(MsgHandler);
@@ -207,10 +209,11 @@ int main(int argc, char **argv, char **const envp) {
auto Program = FEX::Config::LoadConfig(
IsInterpreter,
true,
argc, argv, envp
);
argc, argv, envp,
ExecutedWithFD,
FEXFDView);
if (Program.first.empty()) {
if (Program.ProgramPath.empty() && !FEXFD) {
// Early exit if we weren't passed an argument
return 0;
}
@@ -286,14 +289,14 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Profiler::Init();
FEXCore::Telemetry::Initialize();
RootFSRedirect(&Program.first, LDPath());
InterpreterHandler(&Program.first, LDPath(), &Args);
RootFSRedirect(&Program.ProgramPath, LDPath());
InterpreterHandler(&Program.ProgramPath, LDPath(), &Args);
std::error_code ec{};
if (!std::filesystem::exists(Program.first, ec)) {
if (!ExecutedWithFD && !FEXFD && !std::filesystem::exists(Program.ProgramPath, ec)) {
// Early exit if the program passed in doesn't exist
// Will prevent a crash later
fmt::print(stderr, "{}: command not found\n", Program.first);
fmt::print(stderr, "{}: command not found\n", Program.ProgramPath);
return -ENOEXEC;
}
@@ -310,7 +313,7 @@ int main(int argc, char **argv, char **const envp) {
putenv(HostEnv.data());
}
ELFCodeLoader2 Loader{Program.first, LDPath(), Args, ParsedArgs, envp, &Environment};
ELFCodeLoader2 Loader{Program.ProgramPath, FEXFDView, LDPath(), Args, ParsedArgs, envp, &Environment};
//FEX::HarnessHelper::ELFCodeLoader Loader{Program.first, LDPath(), Args, ParsedArgs, envp, &Environment};
if (!Loader.ELFWasLoaded()) {
@@ -329,8 +332,20 @@ int main(int argc, char **argv, char **const envp) {
return -ENOEXEC;
}
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);
if (ExecutedWithFD) {
// Don't need to canonicalize Program.ProgramPath, Config loader will have resolved this already.
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, Program.ProgramPath);
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.ProgramName);
}
else if (FEXFD) {
// Anonymous program.
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, "<Anonymous>");
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, "<Anonymous>");
}
else {
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.ProgramPath).string());
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.ProgramName);
}
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
@@ -506,7 +521,7 @@ int main(int argc, char **argv, char **const envp) {
FEXCore::Allocator::ClearHooks();
FEXCore::Allocator::ReclaimMemoryRegion(Base48Bit);
// Allocator is now original system allocator
FEXCore::Telemetry::Shutdown(Program.second);
FEXCore::Telemetry::Shutdown(Program.ProgramName);
FEXCore::Profiler::Shutdown();
if (ShutdownReason == FEXCore::Context::ExitReason::EXIT_SHUTDOWN) {
return ProgramStatus;
+35 -32
View File
@@ -201,22 +201,22 @@ namespace FEX::HarnessHelper {
if (BaseConfig.OptionRegDataCount > 0) {
static constexpr std::array<uint64_t, 45> OffsetArrayAVX = {{
offsetof(FEXCore::Core::CPUState, rip),
offsetof(FEXCore::Core::CPUState, gregs[0]),
offsetof(FEXCore::Core::CPUState, gregs[1]),
offsetof(FEXCore::Core::CPUState, gregs[2]),
offsetof(FEXCore::Core::CPUState, gregs[3]),
offsetof(FEXCore::Core::CPUState, gregs[4]),
offsetof(FEXCore::Core::CPUState, gregs[5]),
offsetof(FEXCore::Core::CPUState, gregs[6]),
offsetof(FEXCore::Core::CPUState, gregs[7]),
offsetof(FEXCore::Core::CPUState, gregs[8]),
offsetof(FEXCore::Core::CPUState, gregs[9]),
offsetof(FEXCore::Core::CPUState, gregs[10]),
offsetof(FEXCore::Core::CPUState, gregs[11]),
offsetof(FEXCore::Core::CPUState, gregs[12]),
offsetof(FEXCore::Core::CPUState, gregs[13]),
offsetof(FEXCore::Core::CPUState, gregs[14]),
offsetof(FEXCore::Core::CPUState, gregs[15]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RAX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RCX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSI]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDI]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBP]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSP]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R8]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R9]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R10]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R11]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R12]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R13]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R14]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R15]),
offsetof(FEXCore::Core::CPUState, xmm.avx.data[0][0]),
offsetof(FEXCore::Core::CPUState, xmm.avx.data[1][0]),
offsetof(FEXCore::Core::CPUState, xmm.avx.data[2][0]),
@@ -248,22 +248,22 @@ namespace FEX::HarnessHelper {
}};
static constexpr std::array<uint64_t, 45> OffsetArraySSE = {{
offsetof(FEXCore::Core::CPUState, rip),
offsetof(FEXCore::Core::CPUState, gregs[0]),
offsetof(FEXCore::Core::CPUState, gregs[1]),
offsetof(FEXCore::Core::CPUState, gregs[2]),
offsetof(FEXCore::Core::CPUState, gregs[3]),
offsetof(FEXCore::Core::CPUState, gregs[4]),
offsetof(FEXCore::Core::CPUState, gregs[5]),
offsetof(FEXCore::Core::CPUState, gregs[6]),
offsetof(FEXCore::Core::CPUState, gregs[7]),
offsetof(FEXCore::Core::CPUState, gregs[8]),
offsetof(FEXCore::Core::CPUState, gregs[9]),
offsetof(FEXCore::Core::CPUState, gregs[10]),
offsetof(FEXCore::Core::CPUState, gregs[11]),
offsetof(FEXCore::Core::CPUState, gregs[12]),
offsetof(FEXCore::Core::CPUState, gregs[13]),
offsetof(FEXCore::Core::CPUState, gregs[14]),
offsetof(FEXCore::Core::CPUState, gregs[15]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RAX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RCX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSI]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDI]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBP]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSP]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R8]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R9]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R10]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R11]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R12]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R13]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R14]),
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R15]),
offsetof(FEXCore::Core::CPUState, xmm.sse.data[0][0]),
offsetof(FEXCore::Core::CPUState, xmm.sse.data[1][0]),
offsetof(FEXCore::Core::CPUState, xmm.sse.data[2][0]),
@@ -385,6 +385,7 @@ namespace FEX::HarnessHelper {
FEATURE_CLZERO = (1 << 5),
FEATURE_BMI1 = (1 << 6),
FEATURE_BMI2 = (1 << 7),
FEATURE_CLWB = (1 << 8),
};
bool Requires3DNow() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_3DNOW; }
@@ -395,6 +396,7 @@ namespace FEX::HarnessHelper {
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; }
bool RequiresCLWB() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_CLWB; }
private:
FEX_CONFIG_OPT(ConfigDumpGPRs, DUMPGPRS);
@@ -534,6 +536,7 @@ namespace FEX::HarnessHelper {
bool RequiresCLZERO() const { return Config.RequiresCLZERO(); }
bool RequiresBMI1() const { return Config.RequiresBMI1(); }
bool RequiresBMI2() const { return Config.RequiresBMI2(); }
bool RequiresCLWB() const { return Config.RequiresCLWB(); }
private:
constexpr static uint64_t STACK_SIZE = FHU::FEX_PAGE_SIZE;
+146 -88
View File
@@ -42,6 +42,7 @@ $end_info$
#include <memory>
#include <regex>
#include <sched.h>
#include <span>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -166,33 +167,19 @@ 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())) {
return true;
static bool IsShebangFile(std::span<char> Data) {
// File isn't large enough to even contain a shebang.
if (Data.size() <= 2) {
return false;
}
// If it is a shebang then we also can
std::fstream File;
size_t FileSize{0};
File.open(Filename, std::fstream::in | std::fstream::binary);
if (!File.is_open())
return false;
File.seekg(0, File.end);
FileSize = File.tellg();
File.seekg(0, File.beg);
// Is the file large enough for shebang
if (FileSize <= 2)
return false;
// Handle shebang files
if (File.get() == '#' &&
File.get() == '!') {
std::string InterpreterLine;
std::getline(File, InterpreterLine);
// Handle shebang files.
if (Data[0] == '#' &&
Data[1] == '!') {
std::string InterpreterLine {
Data.begin() + 2, // strip off "#!" prefix
std::find(Data.begin(), Data.end(), '\n')
};
std::vector<std::string> ShebangArguments{};
// Shebang line can have a single argument
@@ -202,7 +189,7 @@ static bool IsSupportedByInterpreter(std::string const &Filename) {
if (Argument.empty()) {
continue;
}
ShebangArguments.emplace_back(Argument);
ShebangArguments.push_back(std::move(Argument));
}
// Executable argument
@@ -211,57 +198,107 @@ static bool IsSupportedByInterpreter(std::string const &Filename) {
// If the filename is absolute then prepend the rootfs
// If it is relative then don't append the rootfs
if (ShebangProgram[0] == '/') {
std::string RootFS = FEX::HLE::_SyscallHandler->RootFSPath();
ShebangProgram = RootFS + ShebangProgram;
ShebangProgram = FEX::HLE::_SyscallHandler->RootFSPath() + ShebangProgram;
}
std::error_code ec;
bool exists = std::filesystem::exists(ShebangProgram, ec);
if (ec || !exists) {
return false;
}
return true;
return !ec && exists;
}
return false;
}
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs *Args) {
static bool IsShebangFD(int FD) {
// We don't know the state of the FD coming in since this might be a guest tracked FD.
// Need to be extra careful here not to adjust file offsets and status flags.
//
// Can't use dup since that makes the FD have the same file description backing both FDs.
// The maximum length of the shebang line is `#!` + 255 chars
std::array<char, 257> Header;
const auto ChunkSize = 257l;
const auto ReadSize = pread(FD, &Header.at(0), ChunkSize, 0);
return IsShebangFile(std::span<char>(Header.data(), ReadSize));
}
static bool IsShebangFilename(std::string const &Filename) {
// Open the Filename to determine if it is a shebang file.
int FD = open(Filename.c_str(), O_RDONLY | O_CLOEXEC);
if (FD == -1) {
return false;
}
bool IsShebang = IsShebangFD(FD);
close(FD);
return IsShebang;
}
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs Args) {
std::string Filename{};
std::error_code ec;
std::string RootFS = FEX::HLE::_SyscallHandler->RootFSPath();
ELFLoader::ELFContainer::ELFType Type{};
// Check the rootfs if it is available first
if (pathname[0] == '/') {
auto Path = FEX::HLE::_SyscallHandler->FM.GetEmulatedPath(pathname, true);
if (!Path.empty() && std::filesystem::exists(Path, ec)) {
Filename = Path;
// AT_EMPTY_PATH is only used if the pathname is empty.
const bool IsFDExec = (Args.flags & AT_EMPTY_PATH) && strlen(pathname) == 0;
std::string FDExecEnv;
bool IsShebang{};
if (IsFDExec) {
Type = ELFLoader::ELFContainer::GetELFType(Args.dirfd);
IsShebang = IsShebangFD(Args.dirfd);
}
else
{
// For absolute paths, check the rootfs first (if available)
if (pathname[0] == '/') {
auto Path = FEX::HLE::_SyscallHandler->FM.GetEmulatedPath(pathname, true);
if (!Path.empty() && std::filesystem::exists(Path, ec)) {
Filename = Path;
}
else {
Filename = pathname;
}
}
else {
Filename = pathname;
}
}
else {
Filename = pathname;
bool exists = std::filesystem::exists(Filename, ec);
if (ec || !exists) {
return -ENOENT;
}
int pid = getpid();
char PidSelfPath[50];
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
if (strcmp(pathname, "/proc/self/exe") == 0 ||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
strcmp(pathname, PidSelfPath) == 0) {
// If the application is trying to execve `/proc/self/exe` or its variants,
// then we need to redirect this path to the true application path.
// This is because this path is a symlink to the executing application, which is always `FEXInterpreter` or `FEXLoader`.
// ex: JRE and shapez.io do this self-execution.
Filename = FEX::HLE::_SyscallHandler->Filename();
}
Type = ELFLoader::ELFContainer::GetELFType(Filename);
IsShebang = IsShebangFilename(Filename);
}
bool exists = std::filesystem::exists(Filename, ec);
if (ec || !exists) {
return -ENOENT;
}
int pid = getpid();
char PidSelfPath[50];
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
if (strcmp(pathname, "/proc/self/exe") == 0 ||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
strcmp(pathname, PidSelfPath) == 0) {
// If pointing to self then redirect to the application
// JRE and shapez.io does this
Filename = FEX::HLE::_SyscallHandler->Filename();
if (!IsShebang && Type == ELFLoader::ELFContainer::ELFType::TYPE_NONE) {
// If our interpeter doesn't support this file format AND ELF format is NONE then ENOEXEC
// binfmt_misc could end up handling this case but we can't know that without parsing binfmt_misc ourselves
// Return -ENOEXEC until proven otherwise
return -ENOEXEC;
}
// If we don't have the interpreter installed we need to be extra careful for ENOEXEC
@@ -269,7 +306,6 @@ uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* env
// Kernel does its own checks for file format support for this
// We can only call execve directly if we both have an interpreter installed AND were ran with the interpreter
// If the user ran FEX through FEXLoader then we must go down the emulated path
ELFLoader::ELFContainer::ELFType Type = ELFLoader::ELFContainer::GetELFType(Filename);
uint64_t Result{};
if (FEX::HLE::_SyscallHandler->IsInterpreterInstalled() &&
FEX::HLE::_SyscallHandler->IsInterpreter() &&
@@ -277,38 +313,24 @@ uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* env
Type == ELFLoader::ELFContainer::ELFType::TYPE_X86_64)) {
// If the FEX interpreter is installed then just execve the ELF file
// This will stay inside of our emulated environment since binfmt_misc will capture it
if (Args) {
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), argv, envp, Args->flags);
}
else {
Result = execve(Filename.c_str(), argv, envp);
}
Result = ::syscall(SYS_execveat, Args.dirfd, Filename.c_str(), argv, envp, Args.flags);
SYSCALL_ERRNO();
}
if (!IsSupportedByInterpreter(Filename) && Type == ELFLoader::ELFContainer::ELFType::TYPE_NONE) {
// If our interpeter doesn't support this file format AND ELF format is NONE then ENOEXEC
// binfmt_misc could end up handling this case but we can't know that without parsing binfmt_misc ourselves
// Return -ENOEXEC until proven otherwise
return -ENOEXEC;
}
if (Type == ELFLoader::ELFContainer::ELFType::TYPE_OTHER_ELF) {
// We are trying to execute an ELF of a different architecture
// We can't know if we can support this without architecture specific checks and binfmt_misc parsing
// Just execve it and let the kernel handle the process
if (Args) {
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), argv, envp, Args->flags);
}
else {
Result = execve(Filename.c_str(), argv, envp);
}
Result = ::syscall(SYS_execveat, Args.dirfd, Filename.c_str(), argv, envp, Args.flags);
SYSCALL_ERRNO();
}
// We don't have an interpreter installed or we are executing a non-ELF executable
// We now need to munge the arguments
std::vector<const char *> ExecveArgs{};
std::vector<const char *> EnvpArgs{};
char *const *EnvpPtr = envp;
const char NullString[] = "";
FEX::HLE::_SyscallHandler->GetCodeLoader()->GetExecveArguments(&ExecveArgs);
if (!FEX::HLE::_SyscallHandler->IsInterpreter()) {
// If we were launched from FEXLoader then we need to make sure to split arguments from FEXLoader and guest
@@ -321,26 +343,62 @@ uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* env
auto OldArgv = argv;
// Skip filename argument
++OldArgv;
while (*OldArgv) {
// Append the arguments together
ExecveArgs.emplace_back(*OldArgv);
// It is valid to provide nullptr first argument.
if (*OldArgv) {
// Skip filename argument
++OldArgv;
while (*OldArgv) {
// Append the arguments together
ExecveArgs.emplace_back(*OldArgv);
++OldArgv;
}
}
else {
// Linux kernel will stick an empty argument in to the argv list if none are provided.
ExecveArgs.emplace_back(NullString);
}
// Emplace nullptr at the end to stop
ExecveArgs.emplace_back(nullptr);
}
if (Args) {
Result = ::syscall(SYS_execveat, Args->dirfd, "/proc/self/exe",
const_cast<char *const *>(ExecveArgs.data()), envp, Args->flags);
}
else {
Result = execve("/proc/self/exe", const_cast<char *const *>(ExecveArgs.data()), envp);
if (IsFDExec) {
if (envp) {
auto OldEnvp = envp;
while (*OldEnvp) {
EnvpArgs.emplace_back(*OldEnvp);
++OldEnvp;
}
}
int Flags = fcntl(Args.dirfd, F_GETFD);
if (Flags & FD_CLOEXEC) {
// FEX needs the FD to live past execve when binfmt_misc isn't used,
// so duplicate the FD if FD_CLOEXEC is set
Args.dirfd = dup(Args.dirfd);
}
// Remove AT_EMPTY_PATH flag now.
// We need to emulate this flag with `FEX_EXECVEFD` environment variable.
// If we passed this flag through to the real `execveat` then the target FD wouldn't get emulated by FEX.
Args.flags &= ~AT_EMPTY_PATH;
// Create the environment variable to pass the FD to our FEX.
// Needs to stick around until execveat completes.
FDExecEnv = "FEX_EXECVEFD=" + std::to_string(Args.dirfd);
// Insert the FD for FEX to track.
EnvpArgs.emplace_back(FDExecEnv.data());
// Emplace nullptr at the end to stop
EnvpArgs.emplace_back(nullptr);
EnvpPtr = const_cast<char *const *>(EnvpArgs.data());
}
Result = ::syscall(SYS_execveat, Args.dirfd, "/proc/self/exe",
const_cast<char *const *>(ExecveArgs.data()), EnvpPtr, Args.flags);
SYSCALL_ERRNO();
}
+8 -1
View File
@@ -20,6 +20,7 @@ $end_info$
#include <shared_mutex>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <type_traits>
#include <vector>
@@ -80,9 +81,15 @@ uint64_t UnimplementedSyscallSafe(FEXCore::Core::CpuStateFrame *Frame, uint64_t
struct ExecveAtArgs {
int dirfd;
int flags;
static ExecveAtArgs Empty() {
return ExecveAtArgs {
.dirfd = AT_FDCWD,
.flags = 0,
};
}
};
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs *Args);
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs Args);
class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::SourcecodeResolver {
public:
+5 -2
View File
@@ -330,7 +330,10 @@ namespace FEX::HLE::x32 {
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, nullptr);
FEX::HLE::ExecveAtArgs AtArgs = FEX::HLE::ExecveAtArgs::Empty();
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
});
REGISTER_SYSCALL_IMPL_X32(execveat, ([](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, uint32_t *argv, uint32_t *envp, int flags) -> uint64_t {
@@ -359,7 +362,7 @@ namespace FEX::HLE::x32 {
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, &AtArgs);
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
}));
REGISTER_SYSCALL_IMPL_X32(wait4, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int *wstatus, int options, struct rusage_32 *rusage) -> uint64_t {
+5 -2
View File
@@ -119,7 +119,10 @@ namespace FEX::HLE::x64 {
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, nullptr);
FEX::HLE::ExecveAtArgs AtArgs = FEX::HLE::ExecveAtArgs::Empty();
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
});
REGISTER_SYSCALL_IMPL_X64_FLAGS(execveat, SyscallFlags::DEFAULT,
@@ -150,7 +153,7 @@ namespace FEX::HLE::x64 {
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, &AtArgs);
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
}));
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(wait4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
+2 -1
View File
@@ -178,7 +178,8 @@ int main(int argc, char **argv, char **const envp) {
(!HostFeatures.SupportsSHA && Loader.RequiresSHA()) ||
(!HostFeatures.SupportsCLZERO && Loader.RequiresCLZERO()) ||
(!HostFeatures.SupportsBMI1 && Loader.RequiresBMI1()) ||
(!HostFeatures.SupportsBMI2 && Loader.RequiresBMI2());
(!HostFeatures.SupportsBMI2 && Loader.RequiresBMI2()) ||
(!HostFeatures.SupportsCLWB && Loader.RequiresCLWB());
if (TestUnsupported) {
FEXCore::Context::DestroyContext(CTX);
@@ -87,7 +87,7 @@ public:
Label Gate{};
// Patch gate entry point
// mov(dword[rip + Gate], edi)
jmpf(ptr[rip + Gate]);
jmp(qword [rip + Gate], LabelType::T_FAR);
L(Gate);
dd(0x1'0000); // This is a 32-bit offset from the start of the gate. We start at 0x1'0000 + 0
+30 -5
View File
@@ -19,13 +19,20 @@
namespace ArgOptions {
bool AssumeYes = false;
enum CompressedImageOption {
enum class CompressedImageOption {
OPTION_ASK,
OPTION_EXTRACT,
OPTION_ASIS,
};
CompressedImageOption CompressedUsageOption {OPTION_ASK};
CompressedImageOption CompressedUsageOption {CompressedImageOption::OPTION_ASK};
enum class ListQueryOption {
OPTION_ASK,
OPTION_FIRST,
};
ListQueryOption DistroListOption {ListQueryOption::OPTION_ASK};
std::vector<std::string> RemainingArgs;
@@ -55,6 +62,9 @@ namespace ArgOptions {
Parser.add_option("--distro-version")
.help("Which distro version to select");
Parser.add_option("--distro-list-first")
.action("store_true")
.help("When presented the distro-list option, automatically select the first distro if there isn't an exact match.");
optparse::Values Options = Parser.parse_args(argc, argv);
@@ -63,11 +73,15 @@ namespace ArgOptions {
}
if (Options.is_set_by_user("extract")) {
CompressedUsageOption = OPTION_EXTRACT;
CompressedUsageOption = CompressedImageOption::OPTION_EXTRACT;
}
if (Options.is_set_by_user("as_is")) {
CompressedUsageOption = OPTION_ASIS;
CompressedUsageOption = CompressedImageOption::OPTION_ASIS;
}
if (Options.is_set_by_user("distro_list_first")) {
DistroListOption = ListQueryOption::OPTION_FIRST;
}
if (Options.is_set_by_user("distro_name")) {
@@ -703,6 +717,11 @@ namespace Zenity {
return DistroIndex;
}
if (ArgOptions::DistroListOption == ArgOptions::ListQueryOption::OPTION_FIRST) {
// Return the first option if not an exact match.
return 0;
}
std::vector<std::string> Args;
Args.emplace_back("--column=Index");
@@ -876,6 +895,11 @@ namespace TTY {
return DistroIndex;
}
if (ArgOptions::DistroListOption == ArgOptions::ListQueryOption::OPTION_FIRST) {
// Return the first option if not an exact match.
return 0;
}
std::vector<std::string> Args;
for (size_t i = 0; i < Targets.size(); ++i) {
const auto &Target = Targets[i];
@@ -1080,7 +1104,8 @@ int main(int argc, char **argv, char **const envp) {
FEX::Config::LoadConfig(
true,
false,
argc, argv, envp
argc, argv, envp,
false, {}
);
// Reload the meta layer
+2 -1
View File
@@ -146,7 +146,8 @@ int main(int argc, char **argv, char **const envp) {
FEX::Config::LoadConfig(
true,
false,
argc, argv, envp
argc, argv, envp,
false, {}
);
// Reload the meta layer
+1 -1
View File
@@ -474,7 +474,7 @@ namespace ProcessPipe {
// Add the new client to the temporary array
NewPollFDs.emplace_back(pollfd {
.fd = NewFD,
.events = POLLIN | POLLPRI | POLLRDHUP | POLLREMOVE,
.events = POLLIN | POLLPRI | POLLRDHUP,
.revents = 0,
});
}
+1 -1
View File
@@ -19,7 +19,7 @@ namespace SquashFS {
void ShutdownImagePID() {
if (FuseMountPID) {
tgkill(FuseMountPID, FuseMountPID, SIGINT);
FHU::Syscalls::tgkill(FuseMountPID, FuseMountPID, SIGINT);
}
}
+6 -2
View File
@@ -442,7 +442,7 @@ void GenerateThunkLibsAction::ParseInterface(clang::ASTContext& context) {
}
thunked_api.push_back(ThunkedAPIFunction { (const FunctionParams&)data, data.function_name, data.return_type,
namespace_info.host_loader.empty() ? "dlsym" : namespace_info.host_loader,
namespace_info.host_loader.empty() ? "dlsym_default" : namespace_info.host_loader,
data.is_variadic || annotations.custom_guest_entrypoint,
data.is_variadic,
std::nullopt });
@@ -761,7 +761,11 @@ void GenerateThunkLibsAction::EmitOutput() {
version_suffix = '.' + std::to_string(*lib_version);
}
const std::string library_filename = libfilename + ".so" + version_suffix;
file << " fexldr_ptr_" << libname << "_so = dlopen(\"" << library_filename << "\", RTLD_LOCAL | RTLD_LAZY);\n";
// Load the host library in the global symbol namespace.
// This follows how these libraries get loaded in a non-emulated environment,
// Either by directly linking to the library or a loader (In OpenGL or Vulkan) putting everything in the global namespace.
file << " fexldr_ptr_" << libname << "_so = dlopen(\"" << library_filename << "\", RTLD_GLOBAL | RTLD_LAZY);\n";
file << " if (!fexldr_ptr_" << libname << "_so) { return false; }\n\n";
for (auto& import : thunked_api) {
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff. Show more