Compare commits

..
283 Commits
Author SHA1 Message Date
Ryan Houdek 63ce78c41d Docs: Update for release FEX-2303 2023-03-06 08:50:41 -08:00
Mai fc38df2ff0 Merge pull request #2460 from Sonicadvance1/implement_memset
OpcodeDispatcher: Optimize REP STOS to MemSet operation
2023-03-04 11:31:58 -05:00
Mai 2fca207e14 Merge pull request #2462 from Sonicadvance1/fix_proton_2
FileManagement: Fixes Proton
2023-03-04 11:29:07 -05:00
Mai 308fa76aa3 Merge pull request #2463 from Sonicadvance1/update_rootfslinks
FEXRootFSFetcher: Update link to rootfs links file
2023-03-04 11:27:55 -05:00
Ryan Houdek b6ac26e0e9 FEXRootFSFetcher: Update link to rootfs links file
Switches to the new CDN which is significantly faster and has other
benefits.

In order to make sure we don't break old clients, switch to the new link
for a few months while leaving the old one operational.

The links file in the old CDN still points to the new rootfs links so
they get the performance improvement on old clients still.
2023-03-04 01:59:44 -08:00
Ryan Houdek ecd144de6a FileManagement: Fixes Proton
Need to ensure that dirfd is AT_FDCWD and also need to check flags
correctly.

Flags were incorrectly checking mode for O_WRONLY and also we should
check for O_APPEND. Split it out to a helper function just so it is
easier to see what is going on.

Fixes the issue of proton not finding `/lib64/ld-linux-x86-64.so.2`
2023-03-03 13:44:17 -08:00
Ryan Houdek 7e66508016 OpcodeDispatcher: Optimize REP STOS to MemSet operation
x86's REP STOS instruction is a memset (with element size!) with the
ability to choose a direction of execution.
Additionally it has a feature where if it faults part-way through the
copy, an application can catch the fault and continue afterwards to know
how many bytes got copied.

RCX is the counter which decrements for each element, and RDI is the
memory pointer. On fault these will reflect the last location that was
attempted to be written. FEX doesn't support this behaviour which makes
our lives easier.

Without supporting that feature, this turns in to a directional memset
by element size. Let's remove all the multiple blocks and just emit a
single IR operation to improve performance of the JIT.
Our generated code here was terrible, the IR was terrible, multiblock is
always slow with RA. Just a general overall improvement.

With profiling pressure-vessel this change deletes the hottest block that
appeared in the trace. This instruction is very commonly used for
memsetting a region to zero so it should be quite fast.

We can also optimize REP MOVS in the future with a Memcpy IR operation
in a similar fashion.

Additionally in the future these can be optimized to use ARM's new MOPS
instructions since the most common case is memset by byte. Which is when
we should expose the "Fast REP STOS" CPUID bit. Both setp/setm/sete and
cpyfp/cpyfm/cpyfe match `REP STOS` and `REP MOVS` respectively.
2023-03-03 09:16:28 -08:00
Ryan Houdek d6f50bf7b0 IR: Implement support for MemSet operation
This operation directly matches what the x86 STOS instruction does
without supporting its faulting behaviour.

STOS faulting behaviour is that RCX and RDI get updated to the last word
written. Which is something that FEX hasn't ever supported.
2023-03-03 09:16:28 -08:00
Ryan Houdek e7069f9f95 Merge pull request #2461 from lioncash/pair
ARMEmitter: Tidy up some assertion handling
2023-03-02 08:33:53 -08:00
Lioncache ea96ccb63d ARMEmitter: Add missing SVE floating-point compare vectors instructions
We're missing FACGE/FACGT and the aliases FACLE FACLT
2023-03-02 10:54:54 -05:00
Lioncache 4d4eac0987 ARMEmitter: Simplify SVE floating-point compare vectors
We can move the asserts into the helper function
2023-03-02 10:42:58 -05:00
Lioncache f0ee8a49b2 ARMEmitter: Simplify Emitter: SVE: SVE2 floating-point pairwise operations ops
We can centralize all of the assertion handling in the implementation
function.
2023-03-02 10:35:58 -05:00
Lioncache e0d8fc7c2b ARMEmitter: Simplify SVE2 integer halving add/subtract (predicated) ops
We can centralize all the assertion handling in the implementation
function.
2023-03-02 10:28:07 -05:00
Lioncache adf1de5562 ARMEmitter: Simplify SVE integer pairwise ops
We can centralize everything in the helper function itself, getting rid
of a few duplicated assertions.
2023-03-02 10:17:19 -05:00
Mai e310e29898 Merge pull request #2459 from Sonicadvance1/fix_pressure_vessel
FileManagement: Skip opening emulated writable files
2023-03-02 09:41:39 -05:00
Ryan Houdek 37ec68421c FileManagement: Skip opening emulated writable files
In the case that a file is getting opened to be created or writable then
skip EmuFD and rootfs searching for this file.
This fixes an edge case where if FEX was run with an unpacked rootfs
that was writable then pressure-vessel would break.

Fixes pressure-vessel with unpacked rootfs.
2023-03-02 00:26:34 -08:00
Ryan Houdek 41731e2680 Merge pull request #2458 from lioncash/pred
ARMEmitter: Remove predicate implicit conversion operators
2023-03-01 19:58:13 -08:00
Lioncache 5e6a3c6280 ARMEmitter: Remove predicate implicit conversion operators
Like with the vector registers, we can remove all implicit conversion
operators except the ones that convert down to the base PRegister class.

With this, all of the registers are now adequately constrained, so we
shouldn't have any wonky implicit conversions happening anymore.
2023-03-01 22:44:40 -05:00
Ryan Houdek e71e3ec930 Merge pull request #2457 from lioncash/sxtw
ARMEmitter: Make second sxtw parameter a WRegister
2023-03-01 19:35:31 -08:00
Lioncache 4cac100660 ARMEmitter: Make second sxtw parameter a WRegister
Matches the assembly use of it more closely.
2023-03-01 22:20:42 -05:00
Ryan Houdek 378e0692b9 Merge pull request #2456 from lioncash/reg
ARMEmitter: Remove implicit conversions from Register/XRegister/WRegister
2023-03-01 19:16:26 -08:00
Lioncache 678415c4c9 ARMEmitter: Remove implicit conversions from Register/XRegister/WRegister
Ensures that we're always explicit about the size of a register when
using APIs that enforce it.

The only implicit conversions we keep are conversions that convert down
to Register, but not anything that converts up the hierarchy or across
it.
2023-03-01 21:59:53 -05:00
Ryan Houdek e869b2fe67 Merge pull request #2455 from lioncash/comp
ARMEmitter: Remove predicate uint32_t conversion operators
2023-03-01 18:37:34 -08:00
Lioncache 2194a1027c ARMEmitter: Remove predicate uint32_t conversion operators
Now that we have dedicated comparison operators, we no longer need to
keep these implicit conversion operators around.
2023-03-01 21:16:55 -05:00
Lioncache 52b4378e49 ARMEmitter: Add comparison functions to register types
Gets rid of the need to compare indices directly in order to compare
register equality
2023-03-01 21:15:46 -05:00
Ryan Houdek 0f45318040 Merge pull request #2454 from lioncash/convert
ARMEmitter: Remove most implicit conversion operators for vector register types
2023-03-01 17:57:21 -08:00
Ryan Houdek 21fbcef0bd Merge pull request #2453 from lioncash/explicit
ARMEmitter: Make VRegister constructor explicit
2023-03-01 17:54:49 -08:00
Ryan Houdek ef02083767 Merge pull request #2452 from lioncash/consecutive
ARMEmitter: Handle sequential registers in lists nicer
2023-03-01 17:53:47 -08:00
Ryan Houdek 24904f48c4 Merge pull request #2451 from lioncash/saddl
ARMEmitter: Simplify size handling Advanced SIMD 3 different group
2023-03-01 17:45:42 -08:00
Lioncache 9461ab5094 ARMEmitter: Remove conversion operators for VRegister 2023-03-01 18:40:12 -05:00
Lioncache 66c8b14470 ARMEmitter: Remove conversion operators for QRegister 2023-03-01 18:29:11 -05:00
Lioncache 6ab78ca93b ARMEmitter: Remove conversion operators for DRegister 2023-03-01 18:20:32 -05:00
Lioncache 2fe808f5cd ARMEmitter: Remove conversion operators for SRegister 2023-03-01 18:02:31 -05:00
Lioncache 81a94b9ffe ARMEmitter: Remove conversion operators for BRegister 2023-03-01 18:00:24 -05:00
Lioncache 0d87ed46da ARMEmitter: Remove conversion operators for HRegister 2023-03-01 17:58:06 -05:00
Mai 545a216da6 Merge pull request #2448 from Sonicadvance1/optimize_openat
EmulatedFiles: Optimize openat handler
2023-03-01 17:20:14 -05:00
Lioncache 11f65df554 ARMEmitter: Make VRegister constructor explicit
All other parameter taking constructors for the other register types are
explicit, so this just makes behavior more consistent.
2023-03-01 16:48:10 -05:00
Lioncache 29ff642499 ARMEmitter: Make use of sequential register helper
Fixes assertion behavior on quite a bit of ASIMD load-store operations
as well as a few SVE ops as well
2023-03-01 15:25:46 -05:00
Lioncache d36517a9d3 ARMEmitter: Add helper for determining if vectors are sequential
A few vector instructions that take register lists often require
vector registers within the list to be sequential in the form of an
increasing list modulo the register file size.

For example:

v1,  v2, v3, v4
v31, v0, v1, v2

both fit these requirements.

This will be used to enforce this restriction within the asserts from a
single place.
2023-03-01 15:24:07 -05:00
Lioncache 83419b410d ARMEmitter: Simplify size handling Advanced SIMD 3 different group
A large amount of size handling in this category is just decrementing
the size by 1, so we can tidy up a bunch of conditionals by just doing
that instead.
2023-03-01 11:04:40 -05:00
Mai 77fad28b69 Merge pull request #2447 from Sonicadvance1/add_hypervisorbit_hide_option
CPUID: Adds an config option to hide hypervisor bit
2023-02-28 10:59:21 -05:00
Mai 70aefc9db2 Merge pull request #2450 from Sonicadvance1/fix_fexserver_zombie
FEXServerClient: Fixes instance where FEXServer can create a zombie
2023-02-28 10:58:11 -05:00
Mai d2e0adf540 Merge pull request #2449 from Sonicadvance1/fix_fexserver_daemon_systemd
FEXServer: Change systemd service environment variable key
2023-02-28 10:57:09 -05:00
Ryan Houdek 84060cd947 FEXServerClient: Fixes instance where FEXServer can create a zombie
When FEXServer is daemonizing through an instance of FEXLoader or
FEXInterpreter, it would leave a zombie process which was waiting for us
to read the process status.
Since we don't care about the child status and don't want to get blocked
by waitpid, just ignore the signal.

This tells the kernel that we don't care about the signal and will kill
the zombie process immediately.
Didn't notice this before since FEXServer started failing to daemonize.
2023-02-28 05:09:16 -08:00
Ryan Houdek aaf17b6d41 FEXServer: Change systemd service environment variable key
It looks like `SYSTEMD_EXEC_PID` can leak through to the executable
environment in regular situations. Instead let's key off of
`INVOCATATION_ID` which doesn't leak through.

Fixes an edge case behaviour where FEXServer wouldn't daemonize in some
systemd environments.
2023-02-28 05:07:02 -08:00
Ryan Houdek 8ded25ada7 EmulatedFiles: Optimize openat handler
Fixes #2443
I found out with some profiling that this we were spending a decent
amount of time with the `openat` syscall in heavily utilized situations.
While not super common in active gameplay situations, it matters
significantly in loading screens that this is fairly optimal.

The bulk of the time is spent in the emulated files handler to ensure
that whatever path we are given, we can capture file paths that we need
to emulate. The largest contributor being the std::filesystem::canonical
function call.

A couple of optimizations in place here.
1) Do a quick hashmap check right at the start to see if we exactly fit
2) Change from `std::fs::canonical` to `realpath`
3) Switch `GetEmulatedFDPath` to not use optional so it stops building
   on the stack

I'm still not super happy with the performance of `realpath` and also
not happy that we still need to use `lexically_normal` in one code path.
But short of writing a super hand-optimized `realpath` that fits our
constraints, I don't think we can do better.

Micro benchmark needs to test four different situations due to this
optimization.
1) Non-EmuFD path
2) Non-EmuFD path with dirfs
3) EmuFD path
4) EmuFD path with dirfs

And the performance improvement for each situation respectively
1) 12% performance improvement
  - 213413 openat syscalls/s -> 238999 syscalls/s
2) 17% performance improvement
  - 202085 openat syscalls/s -> 237309 syscalls/s
3) 17% performance improvement (/proc/cpuinfo)
  - 56616 openat syscalls/s -> 66231 syscalls/s
  - Includes overhead of generating temp FD and close syscall
4) 5% performance improvement (/proc/cpuinfo)
  - 51080 openat syscalls/s -> 53956 syscalls/s
  - Includes overhead of generating temp FD and close syscall

And for sake of comparison to the non-emulated system; My test system
can hit around 1-1.1 million openat syscalls per second in the same
microbench.

Nice little performance uplift.
2023-02-28 04:00:28 -08:00
Ryan Houdek 5b9fe8f26b CPUID: Adds an config option to hide hypervisor bit
This is known to cause issues in some cases. We hit the first game that
checks for this bit and early exits if it is found.

Lets the MMORPG Tibia run in non-VM situations.
Looks like they have more checks for VMs other than hypervisor bit, so
running under Parallels still won't work. Running on bare Linux is fine.
2023-02-27 23:11:23 -08:00
Ryan Houdek e65b429c83 Merge pull request #2446 from lioncash/cpy
ARMEmitter: Simplify advanced SIMD copy
2023-02-27 19:52:05 -08:00
Lioncache dd290f129f ARMEmitter: Simplify advanced SIMD copy
Same behavior, but collapses some if statements.
2023-02-27 22:32:39 -05:00
Ryan Houdek 1832cc80d6 Merge pull request #2445 from lioncash/unsigned
ARMEmitter: Centralize handling for unsigned offset load-stores
2023-02-27 18:18:30 -08:00
Ryan Houdek fe1faf9ebe Merge pull request #2444 from lioncash/scalar
ARMEmitter: Handle SVE Integer Compare - Scalars group
2023-02-27 18:16:31 -08:00
Lioncache 12d0a7fa98 ARMEmitter: Use constants for unsigned offset encoding limits
Allows us to give some names to these constants that are used in the
JIT instead of writing them by hand.
2023-02-27 17:44:17 -05:00
Lioncache c1b08079f3 ARMEmitter: Strengthen unsigned immediate load/store helper
Centralizes all the shifting behavior and whatnot into a single
function, making everything much more localized.

Also gets rid of a lot of magic constants related to the encoding limits
of immediates.
2023-02-27 17:44:13 -05:00
Mai d688026fe4 Merge pull request #2442 from Sonicadvance1/fix_misaligned_stack_signals
Dispatcher: Fixes crash with misalign stack returning from signal
2023-02-27 14:48:39 -05:00
Lioncache 1c388b455a ARMEmitter: Move missed SVE public helpers into private section 2023-02-27 14:45:59 -05:00
Lioncache 9426abc98d ARMEmitter: Handle SVE pointer conflict compare group 2023-02-27 14:27:29 -05:00
Lioncache 5ee2db34a7 ARMEmitter: Handle SVE conditionally terminate scalars group 2023-02-27 14:22:57 -05:00
Lioncache ad37c19043 ARMEmitter: Handle SVE integer compare scalar count and limit group 2023-02-27 14:09:40 -05:00
Ryan Houdek 0e6c5911b8 Dispatcher: Fixes crash with misalign stack returning from signal
When we were taking a signal that had a misaligned stack, we would store
the host stack at a weird offset.

After that point when we were trying to sigreturn we wouldn't know the
alignment of the stack coming back and we would try loading the host
stack from the wrong offset. Easy fix is to just align the host stack
location.

Fixes Ender Lilies, which was consistently crashing from a SIGCHLD due
to having a misaligned stack.

Side-change: Move the cookie check to the start of the restore. Doesn't
make sense to check the cookie after restoring state since it could be
quite wrong.
2023-02-26 19:58:22 -08:00
Ryan Houdek f2aa0026b5 Merge pull request #2439 from lioncash/log
Emitter/ALUOps: Fix typos in log messages
2023-02-23 16:44:19 -08:00
Lioncache 553efbeb29 Emitter/ALUOps: Fix typos in log messages
Fixes a few incorrect instruction names in the logs.
2023-02-23 19:14:45 -05:00
Ryan Houdek b39a882a2d Merge pull request #2438 from lioncash/restrict
OpcodeDispatcher: Restrict partial XMM stores to FPRs in StoreResult_WithOpSize
2023-02-23 16:06:53 -08:00
Lioncache 85f7f8e6c0 OpcodeDispatcher: Restrict partial XMM stores to FPRs in StoreResult_WithOpSize
As far as I know, nothing actually uses this path. Partially resolves
the TODO of dealing with partial writes.
2023-02-23 18:48:24 -05:00
Ryan Houdek 4d25de31de Merge pull request #2437 from lioncash/dup
OpcodeDispatcher: Remove now unused _VDupElement path in LoadSource_WithOpSize
2023-02-23 13:58:09 -08:00
Ryan Houdek 9e01730c6c Merge pull request #2436 from lioncash/builtin
Arm64Emitter: Use bit utils wrapper over __builtin_ffs
2023-02-23 13:51:24 -08:00
Lioncache fea3ee1298 OpcodeDispatcher: Remove now unused _VDupElement path in LoadSource_WithOpSize
XMM instances can't use high indices anymore, since we've gotten rid of
the only flag that allows this scenario to occur.
2023-02-23 16:09:03 -05:00
Lioncache e1c42315ed Arm64Emitter: Use bit utils wrapper over __builtin_ffs
Just keeps the use of builtins contained to one place.
2023-02-23 15:23:07 -05:00
Ryan Houdek 165db37c8d Merge pull request #2434 from lioncash/predmisc
ARMEmitter: Finish off SVE Predicate Misc group
2023-02-23 12:20:03 -08:00
Ryan Houdek 9b23ae9133 Merge pull request #2433 from lioncash/subsw
OpcodeDispatcher: Handle VPHSUBSW
2023-02-23 12:17:58 -08:00
Ryan Houdek f951a406e6 Merge pull request #2435 from lioncash/mov
OpcodeDispatcher: Share MOVHPD implementation with MOVHPS
2023-02-23 12:16:27 -08:00
Lioncache 497b5c0561 X86Tables: Reclaim FLAGS_SF_HIGH_XMM_REG as an unused flag
Now that we've moved MOVHPS over to sharing the implementation of
MOVHPD, the FLAGS_SF_HIGH_XMM_REG is now unused.

Since we're supporting AVX, this flag is kind of weird in terms of
behavior, since what determines the high part of a register is now
situationally different.

Also it's much more explicit to perform the insert directly in the
implementation of instructions, than relying on a flag to do it for us.

So, instead of keeping it around, we can reclaim it as unused for use
with any necessary behavior that we would require in the future.
2023-02-23 14:22:25 -05:00
Lioncache 95393b07fb OpcodeDispatcher: Share MOVHPD implementation with MOVHPS
These instructions essentially have the same behavior. This also allows
us to remove the only used instance of FLAGS_SF_HIGH_XMM_REG, which,
given that we now support AVX, has ambiguous use.

While we're at it, we can expand the tests to make use of the store to
memory variant.

Also removes an erroneous copy-pasted comment about ZEXTing. This is
from the MOVQ implementation function. MOVHPS/MOVHPD don't do any
ZEXTing, they either store to memory or insert into a register.
2023-02-23 14:07:22 -05:00
Lioncache 372da1b820 ARMEmitter: Handle PNEXT
Now, with the helper in place, we can implement PNEXT and finish off the
SVE Predicate Misc group.
2023-02-23 11:59:39 -05:00
Lioncache 61a59d0314 ARMEmitter: Unify SVE Predicate Misc group under single helper
Centralizes the implementations and also gets rid of some code in the
process.
2023-02-23 11:51:03 -05:00
Lioncache 1045e05870 OpcodeDispatcher: Handle VPHSUBSW 2023-02-23 10:55:57 -05:00
Lioncache 052872725c OpcodeDispatcher: Factor out PHSUBS implementation into helper
This will allow it to be shared in the AVX implementation.
2023-02-23 10:32:48 -05:00
Ryan Houdek 4d655218ab Merge pull request #2431 from lioncash/brk
ARMEmitter: Handle SVE partition break categories
2023-02-22 21:13:33 -08:00
Lioncache 78ba195b66 ARMEmitter: Handle SVE partition break condition category 2023-02-22 22:49:37 -05:00
Lioncache ae2b28716d ARMEmitter: Handle SVE propagate break to next partition category 2023-02-22 22:41:55 -05:00
Lioncache 326e5e8d57 ARMEmitter: Handle propagate break from previous partition category 2023-02-22 22:35:31 -05:00
Ryan Houdek 0a8fc2cbef Merge pull request #2430 from lioncash/assert
ARMEmitter: Handle SVE integer compare with wide elements category
2023-02-22 18:57:33 -08:00
Lioncache 552293b226 ARMEmitter: Handle SVE integer compare with wide elements category
We can piggy-back on top of the existing SVEIntegerCompareVector to make
these trivial to implement.
2023-02-22 21:03:35 -05:00
Lioncache 751a4c8019 ARMEmitter: Move assertion into SVEIntegerCompareVector
Same behavior, but centralizes the assertion. While we're at it, we can
also add another assert to ensure that only predicates p0-p7 are used.
2023-02-22 20:14:49 -05:00
Ryan Houdek 68b2072eab Merge pull request #2429 from lioncash/align
OpcodeDispatcher: Handle alignment for MOVAPS a little better
2023-02-22 14:40:28 -08:00
Lioncache e3cac40b1b OpcodeDispatcher: Fix SSE MOVAPS variants being treated as MOVUPS
0x10/0x11 in the two byte op table corresponds to MOVUPS
0x28/0x29 in the two byte op table corresponds to MOVAPS
2023-02-22 15:56:15 -05:00
Ryan Houdek 9b123353b3 Merge pull request #2428 from lioncash/hsub
OpcodeDispatcher: Handle VHSUBPD/VHSUBPS
2023-02-22 11:43:10 -08:00
Lioncache f2c0c55b9c OpcodeDispatcher: Handle VHSUBPS 2023-02-22 14:27:51 -05:00
Ryan Houdek a4c694ffc7 Merge pull request #2427 from lioncash/pred
ARMEmitter: Finish off SVE Permute Vector - Predicated group
2023-02-22 11:26:49 -08:00
Lioncache 1eb722dea7 OpcodeDispatcher: Handle VHSUBPD 2023-02-22 14:12:11 -05:00
Lioncache a6746988d7 x86_64/VectorOps: Fix behavior of UnZip2 with 64-bit element 256-bit vectors
The 256-bit variant of vshufpd uses extra immediate bits rather than the
same bits for the lower lane.
2023-02-22 14:12:11 -05:00
Lioncache 0a1707f1bd OpcodeDispatcher: Factor HSUBP implementation into helper
Will be used for implementing the AVX variants of the same instructions.
2023-02-22 12:12:55 -05:00
Lioncache 23b9d8e108 ARMEmitter: Add check for registers being consecutive in constructive SPLICE
Will catch cases where registers aren't consecutive in the constructive
variant. While we're at it, we can also amend EXT's similar but slightly wrong
consecutive check.

Also adds tests to ensure these corner-cases hold.
2023-02-22 11:59:58 -05:00
Lioncache 3fa44604ba ARMEmitter: Make SPLICE use SVEPermuteVectorPredicated
These are in the same instruction category, so we can use the helper to
simplify the implementation.
2023-02-22 11:48:11 -05:00
Lioncache d3bc0c084d ARMEmitter: Make CPY (SIMD&FP) and CPY (scalar) use SVEPermuteVectorPredicated
These fall under the same instruction category, so we can use the helper
to simplify the implementation.
2023-02-22 11:29:07 -05:00
Lioncache e206414919 ARMEmitter: Make COMPACT use SVEPermuteVectorPredicated
This falls under the same category of instructions, so we can use it to
simplify the implementation.
2023-02-22 11:23:37 -05:00
Lioncache e635cc5404 ARMEmitter: Use predicated helper with revb/revh/revw/rbit
Since these are under the same category, we can merge these and get rid
of a now unnecessary helper.
2023-02-22 11:17:12 -05:00
Lioncache 822d67467b ARMEmitter: Handle SVE conditionally extract element to GPR/scalar categories 2023-02-22 11:10:13 -05:00
Lioncache e043d2c0f5 ARMEmitter: Handle SVE conditionally broadcast element to vector category 2023-02-22 10:55:34 -05:00
Lioncache d58c4405f7 ARMEmitter: Handle extract element to general register/scalar categories 2023-02-22 10:47:49 -05:00
Mai 66d879f387 Merge pull request #2400 from Sonicadvance1/rip_reconstruct
Dispatcher: Support reconstructing RIP from block entry
2023-02-22 09:48:02 -05:00
Mai 55d3edb8e6 Merge pull request #2426 from Sonicadvance1/optimize_getemulatedpath
FileManagement: Optimize GetEmulatedFDPath with an FD!
2023-02-22 09:46:44 -05:00
Ryan Houdek 98f0f22f41 FileManagement: Optimize GetEmulatedFDPath with an FD!
Performance stats up front:
This improves pressure-vessel startup time on my test device by 10.1%
Improving the startup time from 9.71425 seconds to 8.7421 seconds.

Most filesystem based syscalls support a file descriptor version with an
*at suffix. This allows us to do these syscalls with pathnames that are
relative to the directory FD that is passed to the syscall.

This is pretty much exactly what we want when we are searching for files
inside of our rootfs. The only quirk ends up being that we are getting
passed absolute paths. This ends up being very simple to workaround by
stripping off the front '/' character. Doing this is just offsetting the
pointer passed to the syscall by one byte.

This does require having two temporary buffers of size PATH_MAX passed
to the handler since just like in the other implementation, we need to
keep the previous result around. The difference being now that we aren't
doing a bunch of std::string temporary manipulation and now we are
returning one of the passed in buffers back depending on the result.
2023-02-22 01:25:40 -08:00
Ryan Houdek 5f574fb935 Merge pull request #2425 from lioncash/xop
VEXTables: Remove VPERMIL2PD and VPERMIL2PS entries
2023-02-20 18:27:32 -08:00
Ryan Houdek 618f5bb869 Merge pull request #2424 from lioncash/permil
OpcodeDispatcher: Handle register variants of VPERMILPD/VPERMILPS
2023-02-20 18:02:14 -08:00
Lioncache b5ca5f173e VEXTables: Remove VPERMIL2PD and VPERMIL2PS entries
These are actually XOP instructions. That, despite being so, are encoded
using a VEX prefix.
2023-02-20 20:58:55 -05:00
Lioncache 5cf6a680bb OpcodeDispatcher: Handle register variants of VPERMILPD/VPERMILPS 2023-02-20 20:32:31 -05:00
Ryan Houdek 645f40bb96 Merge pull request #2423 from lioncash/permd
OpcodeDispatcher: Handle VPERMD/VPERMPS
2023-02-20 15:20:59 -08:00
Ryan Houdek 268deddd09 Merge pull request #2422 from lioncash/phadds
OpcodeDispatcher: Handle VPHADDSW
2023-02-20 15:20:20 -08:00
Ryan Houdek e4488b0cfc Merge pull request #2421 from lioncash/index
ARMEmitter: Handle SVE index generation category
2023-02-20 15:16:33 -08:00
Lioncache 65b9dcd20b OpcodeDispatcher: Handle VPERMPS
With the VPERMD work in place, this is trivial to support.
2023-02-20 17:00:39 -05:00
Lioncache b2c333c383 OpcodeDispatcher: Handle VPERMD 2023-02-20 17:00:35 -05:00
Lioncache 1ea53c65ab x86_64/VectorOps: Handle 8-bit VShlI IR op
Useful for handling VPERMD.
2023-02-20 16:45:13 -05:00
Lioncache 8beae0fce4 OpcodeDispatcher: Add VTrn/VTrn2 IR opcodes
Provides a convenient way to propogate indices at given intervals in
vectors. This makes permutation instructions a little less annoying to
implement.
2023-02-20 16:44:14 -05:00
Lioncache add775c5cd OpcodeDispatcher: Handle VPHADDSW 2023-02-20 12:25:28 -05:00
Lioncache f3e6f62356 OpcodeDispatcher: Factor PHADDS implementation into helper
This will be used to also handle the VEX variant of PHADDSW
2023-02-20 12:00:38 -05:00
Lioncache 59ab10f155 ARMEmitter: Move SVE instruction helpers into privare section
Moves some instruction helpers that existed outside of the private
section of the class back into them, so that we're not exposing
unnecessary things in the interface.
2023-02-20 11:39:21 -05:00
Lioncache 2e1bd4b32b ARMEmitter: Handle SVE index generation category 2023-02-20 11:30:09 -05:00
Mai f71f2445db Merge pull request #2389 from Sonicadvance1/remove_context_c_interface
FEXCore: Removes C wrapper interface
2023-02-20 10:08:07 -05:00
Mai f6e2fe1515 Merge pull request #2420 from Sonicadvance1/fix_syscall_race
Arm64: Fixes a race condition on syscall spilling SRA
2023-02-20 10:06:53 -05:00
Mai 11c8db5a14 Merge pull request #2419 from Sonicadvance1/cortex_c_classify
Scripts: Update fit_native script for X1C/A78C
2023-02-20 10:06:07 -05:00
Mai 65b2da20d6 Merge pull request #2418 from Sonicadvance1/optimize_aluop_dispatcher
OpcodeDispatcher: Optimize ALUOp handler
2023-02-20 10:05:47 -05:00
Ryan Houdek 273f5e1f26 Arm64: Fixes a race condition on syscall spilling SRA
When executing a non-inlined syscall, we spill all static registers.
We weren't storing in to the thread context that we have done this.
If a signal occured between FEX returning from the syscall (after the
blr) and before the `FillStaticRegs` then the signal handler would get
the incorrect register state.

This typically manifested as Steam getting a SIGCHLD, trying to recover
the guest stack pointer, and it that pointer would be zero or some other
corrupt value. Thus crashing inside of the signal handler.

Surprising that we hadn't hit this way more before this point, must have
needed hardware that tickled the race condition *just* right.
2023-02-19 16:12:06 -08:00
Ryan Houdek 35af4bd42a FEXCore: Removes C wrapper interface
This has been a long time coming. The C interface has been a thorn in
our side for no reason for a long time.

The purpose of this step is to remove the C interface without changing
behaviour as much as possible. This means that with this commit there
are still some bad practices but the remaining issues will be solved
with followup PRs.

Primarily, we still have a `DestroyContext(CTX)` static function which calls
the Context implementation's `DestroyContext` and does a raw C++ delete.

Follow up PR will remove that, but I didn't want to touch it yet since
it'll require checking to ensure the unique_ptr changes play nice with
our allocator hooking. Which this is already a huge PR without trying to
change behaviour.
2023-02-19 11:59:11 -08:00
Ryan Houdek 7f1464b135 Scripts: Update fit_native script for X1C/A78C
Cortex-X1C and A78C are relatively minor changes to their non-C
counterparts. Support classifying them in case clang understands them.

Fixes a minor perf regression noticed on the Lenovo X13s while testing.
2023-02-18 23:18:48 -08:00
Ryan Houdek e594b2c4c7 OpcodeDispatcher: Optimize ALUOp handler
Take a leaf from the Vector ops and have the jump entry choose the IR
op.
Also generate one atomic op and modify the IR type in the locked memory
type just like the non locked memory path.

This class of instructions in the number one instruction type percentage
wise, so making this more optimal will be a win.

It's a fairly minor optimization so it should be a small impact.
2023-02-18 03:32:43 -08:00
Ryan Houdek 2aead5aec2 Config: Removes the x86dec_SynchronizeRIPOnAllBlocks option
This is no longer necessary since we reconstruct up to block entry from
the previous commit.
2023-02-18 02:48:55 -08:00
Ryan Houdek c3f1f602fe Dispatcher: Support reconstructing RIP from block entry
This allows us to not update RIP on block entry, but still allow
reconstructing the RIP up until that point.

While still not full RIP reconstruction, this lets us update the signal
context's RIP just like the `x86dec_SynchronizeRIPOnAllBlocks` without
eating the cost of writing to RIP on block entry.
2023-02-18 02:48:55 -08:00
Ryan Houdek 9c256bfe96 Merge pull request #2413 from lioncash/unpred
ARMEmitter: Handle a few more vector permutation categories
2023-02-15 14:51:59 -08:00
Ryan Houdek b5bc8cd294 Merge pull request #2416 from lioncash/mov
VectorOps: Remove unnecessary mov in VUShrNI2/VSQXTN2/VSQXTUN2
2023-02-15 14:46:02 -08:00
Lioncache e78b573610 VectorOps: Remove unnecessary mov in VUShrNI2/VSQXTN2/VSQXTUN2
We can move the initial move down by SPLICE, which not only lets us turn
it into a MOVPRFX, but also we can safely move into the final
destination register directly, since we can be sure there's no
potential dependencies at this point
2023-02-15 17:14:26 -05:00
Mai 81a89ab747 Merge pull request #2415 from Sonicadvance1/spillsra_fix
Dispatcher: Fixes guest stack register usage
2023-02-15 15:51:49 -05:00
Ryan Houdek fd17a3de50 Dispatcher: Fixes guest stack register usage
Fixes #2410

We were pulling the guest RSP before spilling static registers back to
the state.
Move this to after we spill SRA state to fix this bug.

Thanks to @ifquant for diving in, identifying, and finding the exact bug.
2023-02-15 12:21:11 -08:00
Ryan Houdek 2f260ae6ad Merge pull request #2414 from lioncash/sve-ex
Arm64/VectorOps: Use SVE only with 256-bit op sizes
2023-02-15 12:11:05 -08:00
Lioncache bf7118fc85 Arm64/VectorOps: Use SVE only with 256-bit op sizes
Keeps all of the IR ops consistent with each other. Also removes some
redundant scalar checks that weren't really necessary.
2023-02-15 14:46:05 -05:00
Ryan Houdek a90f5363dd Merge pull request #2412 from lioncash/ptest
OpcodeDispatcher: Handle VPTEST
2023-02-15 10:31:12 -08:00
Ryan Houdek ab03e59500 Merge pull request #2411 from lioncash/zero
OpcodeDispatcher: Use VectorZero over VectorImm in InsertPSOpImpl
2023-02-15 10:30:23 -08:00
Lioncache a59d700bbe ARMEmitter: Handle SVE Permute Predicate category 2023-02-15 12:56:39 -05:00
Lioncache 7cf27a7c26 ARMEmitter: Handle SVE Permute Vector - Unpredicated category 2023-02-15 12:18:42 -05:00
Lioncache 14e1d16710 OpcodeDispatcher: Handle VPTEST 2023-02-15 11:24:37 -05:00
Lioncache 203f29a91f OpcodeDispatcher: Use VectorZero over VectorImm in InsertPSOpImpl
A little more straightforward than using VectorImm for the same purpose.
2023-02-15 09:37:54 -05:00
Ryan Houdek 25f0a03ceb Merge pull request #2407 from lioncash/mov
OpcodeDispatcher: Handle VMOVSD/VMOVSS
2023-02-14 22:36:13 -08:00
Lioncache 3ced41414e OpcodeDispatcher: Handle VMOVSD 2023-02-15 01:18:54 -05:00
Lioncache 1a64b26d03 OpcodeDispatcher: Handle VMOVSS 2023-02-15 01:18:15 -05:00
Ryan Houdek efafe0e6e9 Merge pull request #2408 from lioncash/pmaddwd
OpcodeDispatcher: Handle VPMADDWD
2023-02-14 17:52:57 -08:00
Ryan Houdek 35746c7669 Merge pull request #2406 from lioncash/shuffle
OpcodeDispatcher: Handle VSHUFPD/VSHUFPS
2023-02-14 17:47:39 -08:00
Lioncache 4a69b87cb9 OpcodeDispatcher: Handle VPMADDWD 2023-02-14 18:50:37 -05:00
Lioncache fb2de47e73 OpcodeDispatcher: Factor out PMADDWD implementation to helper
This will be used to centralize code to also implement the AVX variant.
2023-02-14 18:38:12 -05:00
Lioncache bcee3e9374 OpcodeDispatcher: Handle VSHUFPS 2023-02-14 16:46:03 -05:00
Lioncache 6d87154ac8 OpcodeDispatcher: Handle VSHUFPD 2023-02-14 16:46:03 -05:00
Lioncache c5d799df8c OpcodeDispatcher: Make SHUFOpImpl suitable for AVX
Drops in the AVX-specific bits into the helper in preparation for
implementing VSHUFPD and VSHUFPS
2023-02-14 16:45:32 -05:00
Lioncache 449645669a OpcodeDispatcher: Move SHUFOp implementation to helper function
Will be useful for handling both the SSE and AVX variants in the same
place.
2023-02-14 16:43:33 -05:00
Ryan Houdek 3ac7b2cddf Merge pull request #2405 from lioncash/shufw
OpcodeDispatcher: Handle VPSHUFD/VPSHUFHW/VPSHUFLW
2023-02-14 10:36:48 -08:00
Lioncache 504d409cf6 OpcodeDispatcher: Handle VPSHUFD 2023-02-14 13:13:09 -05:00
Lioncache 29a6d584a9 OpcodeDispatcher: Handle VPSHUFHW 2023-02-14 12:48:42 -05:00
Lioncache 310fcf969c OpcodeDispatcher: Handle VPSHUFLW 2023-02-14 12:32:47 -05:00
Ryan Houdek b329442c09 Merge pull request #2404 from lioncash/dup
IR: Add VDupFromGPR
2023-02-13 14:37:59 -08:00
Lioncache f4d799abdd OpcodeDispatcher: Make use of VDupFromGPR where applicable
Simplifies some of the IR usage.
2023-02-13 16:52:51 -05:00
Lioncache 4bb7f49c2a IR: Add VDupFromGPR
Allows broadcasting constants into vectors from GPRs. Resolves the only
remaining TODOs within our vector ops.
2023-02-13 16:52:47 -05:00
Ryan Houdek f7f2dc2210 Merge pull request #2403 from lioncash/err
ARMEmitter/ASIMDOps: Amend a few error logs
2023-02-13 12:31:52 -08:00
Ryan Houdek d40812929f Merge pull request #2402 from lioncash/shufb
OpcodeDispatcher: Handle VPSHUFB
2023-02-13 12:18:16 -08:00
Lioncache c381185a7d ARMEmitter/ASIMDOps: Amend a few error logs
A few were logging out the wrong instruction name on a precondition
failure.
2023-02-13 15:17:09 -05:00
Lioncache ac5d09885e OpcodeDispatcher: Handle VPSHUFB 2023-02-13 14:47:37 -05:00
Lioncache d9a505e22e OpcodeDispatcher: Factor PSHUFB implementation into helper
Will let us centralize the implementation for PSHUFB and VPSHUFB
2023-02-13 12:39:27 -05:00
Ryan Houdek a96ad0fc9d Merge pull request #2401 from lioncash/palign
OpcodeDispatcher: Handle VPALIGNR
2023-02-13 09:35:19 -08:00
Lioncache 9268a356f6 OpcodeDispatcher: Handle VPALIGNR 2023-02-13 10:53:02 -05:00
Lioncache 92141d3edc OpcodeDispatcher: Factor PALIGNR code into helper
Will allow us to centralize the implementation of PALIGNR and VPALIGNR.
2023-02-13 09:49:26 -05:00
Ryan Houdek 8c8b680640 Merge pull request #2398 from lioncash/sve2acc
ARMEmitter: Handle SVE2 Accumulate category
2023-02-10 23:42:43 -08:00
Lioncache 504be62a92 ARMEmitter: Handle SVE2 integer absolute difference and accumulate 2023-02-11 00:27:07 -05:00
Lioncache 62e2f1b45d ARMEmitter: Handle SVE2 bitwise shift and insert category 2023-02-11 00:27:04 -05:00
Lioncache 880cc72842 ARMEmitter: Handle SVE2 bitwise shift right and accumulate 2023-02-11 00:24:18 -05:00
Lioncache feacd897fc ARMEmitter: Handle SVE2 integer add/sub long with carry category 2023-02-11 00:24:18 -05:00
Lioncache fdd950e1d1 ARMEmitter: Handle SVE2 integer absolute difference and accumulate long category 2023-02-11 00:24:17 -05:00
Lioncache b02af95629 ARMEmitter: Handle SVE2 complex add category 2023-02-10 22:31:45 -05:00
Ryan Houdek 2bd64ad24e Merge pull request #2396 from lioncash/narrow
ARMEmitter: Finish off SVE Misc category
2023-02-09 10:28:52 -08:00
Lioncache fa95a823c9 ARMEmitter: Handle SVE2 bitwise shift left long category 2023-02-09 06:30:43 -05:00
Lioncache 399ed61380 ARMEmitter: Handle SVE2 integer add/sub interleaved long 2023-02-09 05:44:44 -05:00
Lioncache 71550e29eb ARMEmitter: Handle SVE integer matrix multiply accumulate 2023-02-09 05:34:11 -05:00
Lioncache 1b8d8f8280 ARMEmitter: Handle SVE2 interleaved XOR category 2023-02-09 05:16:59 -05:00
Lioncache ff6c70f5e1 ARMEmitter: Handle SVE2 bitwise permute category 2023-02-09 05:11:48 -05:00
Lioncache 13ee2b5ec4 ARMEmitter: Handle SVE2 add/sub narrow high part 2023-02-09 05:01:14 -05:00
Ryan Houdek 3c1ba846f7 Merge pull request #2394 from lioncash/cpy
ARMEmitter: Handle CPY (scalar) and CPY (SIMD&FP, scalar)
2023-02-09 01:18:17 -08:00
Lioncache 1b4488e7a3 ARMEmitter: Remove outdated histogram TODO
This was implemented along with histcnt
2023-02-09 04:04:39 -05:00
Lioncache 6b4df4c998 ARMEmitter: Handle CPY (SIMD&FP, scalar) 2023-02-09 03:53:31 -05:00
Lioncache 2a1ef0ba56 ARMEmitter: Handle CPY (scalar) 2023-02-09 03:46:07 -05:00
Ryan Houdek dd2e70e4aa Merge pull request #2393 from lioncash/wide2
ARMEmitter: Handle predicated wide shifts
2023-02-08 23:23:38 -08:00
Lioncache 915a8b23ae ARMEmitter: suffix unpredicated wide shifts
Keeps the naming convention consistent while avoiding clashing
overloads.
2023-02-09 02:00:28 -05:00
Lioncache 6eeafd0724 ARMEmitter: Handle predicated wide shifts 2023-02-09 01:58:23 -05:00
Ryan Houdek e6fc159d88 Merge pull request #2390 from lioncash/ext
OpcodeDispatcher: Handle VEXTRACTF128/VEXTRACTI128
2023-02-08 22:04:22 -08:00
Ryan Houdek 5fd68b6f07 Merge pull request #2392 from lioncash/wide
ARMEmitter: Handle unpredicated wide shifts and unpredicated shifts by immediates
2023-02-08 21:52:23 -08:00
Lioncache 5f80702cf1 ARMEmitter: Handle unpredicated bitwise shift by immediate 2023-02-09 00:15:35 -05:00
Lioncache b45b980b3a ARMEmitter: Handle unpredicated shifts by wide elements 2023-02-09 00:00:17 -05:00
Lioncache f341755e3b Externals: Update fex-gcc-target-test-bins
Allows filtering out the AVX-enabled tests on non-AVX capable systems.
2023-02-08 21:54:35 -05:00
Lioncache c53e7d759b guest_test_runner: Handle AVX-only binary tests
Because the binaries have no metadata, we allow a .json file to be
placed alongside a test indicating required features in a requirements
directory

We also check if the system itself supports those features and run tests
based off of that.
2023-02-08 21:42:04 -05:00
Mai 143ef57141 Merge pull request #2345 from Sonicadvance1/user_sigreturn
Support user supplied signal restorer.
2023-02-08 20:11:47 -05:00
Ryan Houdek 8689038533 Merge pull request #2391 from lioncash/aes
IR: Allow specifying register size for AES enc/dec ops and PCLMUL
2023-02-08 17:11:03 -08:00
Lioncache ade34eeda6 gcc tests: Handle pr57275 test
We now handle all instructions that this uses.
2023-02-08 17:49:29 -05:00
Lioncache 0218c966bd IR: Allow specifying register size for PCLMUL
This will allow us to support 256-bit vector operation in the future.
2023-02-08 16:35:20 -05:00
Lioncache ec5bc9cf3e IR: Allow specifying register sizes for AES enc/dec ops
This will allow us to support operating on 256-bit vectors.

Currently only sets up the bits and pieces on the x86-64 side, since
facilities for testing the 256-bit operations on ARM isn't set up yet.
2023-02-08 16:25:19 -05:00
Lioncache 63bf0d5826 OpcodeDispatcher: Handle VEXTRACTI128 2023-02-08 15:46:27 -05:00
Lioncache 2526fa8b6f OpcodeDispatcher: Handle VEXTRACTF128 2023-02-08 15:40:36 -05:00
Mai ef6f5d2003 Merge pull request #2388 from Sonicadvance1/move_fexbash
FEXBash: Move to Tools folder
2023-02-07 16:18:55 -05:00
Ryan Houdek be02cafb05 FEXBash: Move to Tools folder
Just a cleanup, no functional change.
2023-02-07 07:40:46 -08:00
Ryan Houdek e8fd8ef3b7 Merge pull request #2387 from lioncash/prfx
Arm64/VectorOps: Use movprfx with VBSL
2023-02-06 22:53:41 -08:00
Lioncache d7c6ed842d Arm64/VectorOps: Use movprfx with VBSL
We can use movprfx here to allow compressing the move and bsl operation
together on cpus that can handle it.
2023-02-07 00:44:08 -05:00
Ryan Houdek 86a6118b62 Merge pull request #2386 from lioncash/bsl
VectorOps: Only use VBSL 256-bit path if SVE is present
2023-02-06 21:40:15 -08:00
Lioncache 7a75e43125 VectorOps: Only use VBSL 256-bit path if SVE is present
With this in place, a _VMov isn't necessary for variable blends anymore,
since the vector upper lanes are guaranteed to be zeroed out in the 128-bit case.
2023-02-07 00:13:04 -05:00
Ryan Houdek 4ef3066b69 Merge pull request #2385 from lioncash/vblend
OpcodeDispatcher: Handle VPBLENDVB/VBLENDVPD/VBLENDVPS
2023-02-06 20:25:06 -08:00
Lioncache 88fee019a1 OpcodeDispatcher: Handle VPBLENDVB 2023-02-06 23:04:26 -05:00
Lioncache 5d3141dffc OpcodeDispatcher: Handle VBLENDVPD 2023-02-06 23:04:26 -05:00
Lioncache 94e91565b1 OpcodeDispatcher: Handle VBLENDVPS 2023-02-06 23:04:26 -05:00
Lioncache acbfee55b4 IR: Allow provising register size for VBSL
Necessary, since this will now be used with both 256-bit and 128-bit
registers, rather than just 128-bit.
2023-02-06 23:04:26 -05:00
Lioncache a2481d6892 OpcodeDispatcher: Add helper for AVX variable blends
These will be used by following instruction implementations.
2023-02-06 23:04:26 -05:00
Ryan Houdek e255f1cdef Merge pull request #2383 from lioncash/blend
OpcodeDispatcher: Handle VBLENDPD/VPBLENDW
2023-02-06 18:55:31 -08:00
Ryan Houdek cb3cfed9c2 Merge pull request #2384 from lioncash/sqadd
ARMEmitter: Handle SVE2 saturating add/subtract category
2023-02-06 18:55:23 -08:00
Lioncache 2b12a46d2d ARMEmitter: Handle UQSUBR 2023-02-06 21:29:10 -05:00
Lioncache 8f50109501 ARMEmitter: Handle SQSUBR 2023-02-06 21:29:10 -05:00
Lioncache 1d451b8df1 ARMEmitter: Handle USQADD 2023-02-06 21:29:10 -05:00
Lioncache 49772c6826 ARMEmitter: Handle SUQADD 2023-02-06 21:29:10 -05:00
Lioncache 535a2ab2ba ARMEmitter: Handle UQSUB (vectors, predicated) 2023-02-06 21:29:10 -05:00
Lioncache b8b212719f ARMEmitter: Handle SQSUB (vectors, predicated) 2023-02-06 21:29:10 -05:00
Lioncache be593d43ce ARMEmitter: Handle UQADD (vectors, predicated) 2023-02-06 21:29:10 -05:00
Lioncache a89b7c5dbb ARMEmitter: Handle SQADD (vectors, predicated) 2023-02-06 21:29:07 -05:00
Lioncache c682f51811 OpcodeDispatcher: Handle VPBLENDW 2023-02-06 21:23:30 -05:00
Lioncache 2c7562c54c OpcodeDispatcher: Handle VBLENDPD 2023-02-06 21:03:54 -05:00
Lioncache 8f5ec20cb7 OpcodeDispatcher: Add helper for AVX vector blends 2023-02-06 20:38:35 -05:00
Ryan Houdek 582108a68a Merge pull request #2382 from lioncash/dedup
ARMEmitter: Centralize instruction handling for a few categories
2023-02-06 17:30:09 -08:00
Lioncache 79abe2aa64 ARMEmitter: Simplify bitwise shift by immediate (predicated) category
Centralizes the immediate handling in the encoding helper function.

Lets us move all the asserts there as well.
2023-02-06 20:03:21 -05:00
Lioncache 9f3857b3b0 ARMEmitter: Simplify saturating extract narrow category
Centralizes the immediate handling in the encoding function.
2023-02-06 19:21:42 -05:00
Lioncache 9521638910 ARMEmitter: Simplify bitwise shift right narrow category
Centralizes the immediate handling in one place, making everything much
shorter.
2023-02-06 19:21:39 -05:00
Mai 60b76f53cf Merge pull request #2381 from Sonicadvance1/code_data_header
JIT: Adds a JIT data header and tail.
2023-02-06 18:07:20 -05:00
Mai 5da90aac46 Merge pull request #2378 from Sonicadvance1/fix_emitter_warnings
ARMEmitter: Fixes some warnings that cropped up.
2023-02-06 18:05:56 -05:00
Ryan Houdek ba5ad72ca2 JIT: Adds a JIT data header and tail.
This will be used to store various bits of data about the code going
forward.

Currently unused but that will change as we move forward.
2023-02-06 14:06:37 -08:00
Mai c7c47a827a Merge pull request #2377 from Sonicadvance1/code_data_support
Core: Support Data in JIT buffer header
2023-02-06 16:54:01 -05:00
Mai c4b66b41cd Merge pull request #2379 from Sonicadvance1/rename_fstatat64
Syscalls: Renamed fstatat64 to fstatat_64
2023-02-06 16:47:02 -05:00
Mai 6047ca9fe2 Merge pull request #2376 from Sonicadvance1/minor_flag_opt
Dispatcher: Minor flags optimization
2023-02-06 16:45:45 -05:00
Mai a5762b6faa Merge pull request #2375 from Sonicadvance1/inject_libsegfault
ELFCodeLoader: Adds an option to inject libSegFault
2023-02-06 16:44:52 -05:00
Ryan Houdek 3bc722ca69 Merge pull request #2380 from Joshua-Ashton/directfb_fix
Fix SDL2 directfb includes under Alpine Linux
2023-02-05 18:37:06 -08:00
Joshua Ashton d7d8a4e28a Fix SDL2 directfb includes under Alpine Linux 2023-02-06 02:14:36 +00:00
Ryan Houdek c54c568fef Syscalls: Renamed fstatat64 to fstatat_64
Similar to our other syscall conflicts, musl/Alpine Linux has a global
define that is conflicting with our name here
2023-02-05 18:13:45 -08:00
Ryan Houdek 37421d36e6 ARMEmitter: Fixes some warnings that cropped up. 2023-02-05 18:06:29 -08:00
Ryan Houdek bd86deb9ba Core: Support Data in JIT buffer header
Currently unused (The full data gets thrown away after CompileCode is
called), but allows us to separate code and data in what `CompileCode`
returns.

This will allow us put a header on JIT blocks which will fix a long
outstanding bug where RIP isn't always synchronized on block entry, but
since it only needs to synchronize on signal we can rebuild in the
handler. This future task will remove the `86dec_SynchronizeRIPOnAllBlocks`
config option, but the data will also end up being used for more things
in the future.
2023-02-05 17:55:31 -08:00
Ryan Houdek 2e701fc9e6 Dispatcher: Minor flags optimization
SelectCC shift wasn't necessary since we just need to ensure the final
result is zero when or'd together.

Also operations calculating SF can just use a BFE instead of a shifts
with a constant. BFE by immediate is more efficiently encoded in our IR.
2023-02-04 17:50:36 -08:00
Ryan Houdek 5b97e7f1a0 ELFCodeLoader: Adds an option to inject libSegFault
When used in conjuction with #2345 this is a useful way to enable
libSegFault in applications using application profiles.

Very useful for applications and games that use launcher scripts that
set LD_PRELOAD to nothing prior to launch.

A user was wanting this.
2023-02-04 11:17:26 -08:00
Ryan Houdek 844e27e9ad X86HelperGen: Support fallback sigreturn helpers
For the case that the 32-bit VDSO thunk library isn't available, have a
fallback that can work as well.
Otherwise 32-bit applications will just straight up crash on signal
return.
2023-02-04 10:54:30 -08:00
Ryan Houdek cf147e8ab2 github: Move install step to after the build
Also enable on all builders.
Some tests now rely on 32-bit thunks existing because we need VDSO.
2023-02-04 10:35:07 -08:00
Ryan Houdek e61132b481 VDSOEmu: Handle errors in VDSO
VDSO behaves like a raw syscall which doesn't set errno.
posix tests are testing that errno is set correctly.

Our VDSO handlers weren't wired up to return errors from VDSO correctly.
To handle this we need to have different handlers depending on if the
syscall being used comes from glibc or true VDSO.

This wasn't being uncovered previously since CI wasn't running with VDSO
thunks enabled, but now that it is this needs to be handled or CI will
fail.
2023-02-04 10:35:07 -08:00
Ryan Houdek c58e7a732e X86HelperGen: Remove now unused sigret codegen
This is no longer used so doesn't need to exist.
2023-02-04 10:35:07 -08:00
Ryan Houdek 0538574dd0 Dispatcher: Supports user provided signal restorer
This is required for backtrace to work correctly.
If we are using our custom instruction for returning from a signal, then
backtrace tries to read PC for the sigreturn code and finds our code,
breaking it.

Instead we now /correctly/ support using rt_sigreturn/sigreturn and the
restorer provided from the user.
To facilitate this, we now store a single 64-bit value on the stack to
return our host stack pointer to the correct location from before the
signal.
With cookie checking in place, we can know if an application betrays our
expectations and tries to pass its own signal frames.
If an application in the future /does/ try to pass its own signal
frames, that's unsafe and we cna deal with it then.
2023-02-04 10:35:07 -08:00
Ryan Houdek 1ed546d48f SignalDelegator: Reemit the default signal if it was caught
This fixes a bug where we are falling back down the default signal
delegator after a fatal error.
We need to reraise the event in the case that it didn't come from the
kernel.

Fixes backtrace crashing with incorrect signal when it tries to reraise
the signal that it handled using tgkill.
2023-02-04 10:35:07 -08:00
Ryan Houdek 5ba0053edc VDSOEmulation: Support parsing the 32-bit VDSO symbols
We need to extract the sigreturn handlers and pass them to the FEXCore
signal dispatcher.
2023-02-04 10:35:07 -08:00
Ryan Houdek abb8de0966 VDSO: Add sigreturn functions to VDSO
These need to be bit-exact following exactly what is shown in the
assembly.

libunwind parses where EIP is to see if it is in a stack frame.
Also needsto live in VDSO otherwise backtrace doesn't work.
2023-02-04 10:35:06 -08:00
Ryan Houdek abc596c634 IR: Removes SignalReturn op
This will no longer be used as we are swithing over to using the Linux
system call directly.
2023-02-04 10:35:06 -08:00
Ryan Houdek d107bc9a24 FEXCore: Adds handlers for signal handler returns
Lets the frontend syscall handlers for signal return call the JIT return
handlers directly.
2023-02-04 10:35:06 -08:00
Ryan Houdek a45047bc1e OpDispatcher: Removes SIGRET x86 instruction
We are switching over to syscalls.
2023-02-04 10:35:06 -08:00
Ryan Houdek 1089987a29 Merge pull request #2374 from lioncash/mul
ARMEmitter: Handle SVE SQDMULH/SQRDMULH (vector)
2023-02-04 02:23:59 -08:00
Lioncache 6522d3d6e4 ARMEmitter: Move 128-bit check into SVE2IntegerMultiplyVectors
Simplifies the amount of code needed. Also we can remove some
unnecessary namespacing to make these a little faster to grok when
looking at them.
2023-02-04 05:08:43 -05:00
Lioncache d65fcf7bb8 ARMEmitter: Handle SVE SQRDMULH (vectors) 2023-02-04 05:06:56 -05:00
Lioncache 16e0f628cd ARMEmitter: Handle SVE SQDMULH (vectors) 2023-02-04 05:05:27 -05:00
Ryan Houdek d81097482d Merge pull request #2373 from lioncash/vl
ARMEmitter: Handle ADDVL/ADDPL and RDVL
2023-02-04 01:34:14 -08:00
Lioncache 75bc997ab7 ARMEmitter: Handle RDVL 2023-02-04 01:11:57 -05:00
Lioncache 600e8749d7 ARMEmitter: Handle ADDPL 2023-02-04 01:05:25 -05:00
Lioncache fc3863f444 ARMEmitter: Handle ADDVL 2023-02-04 01:03:24 -05:00
Ryan Houdek 347abf09ef Merge pull request #2372 from lioncash/mla
ARMEmitter: Handle MLA/MLS (vector) and MAD/MSB
2023-02-03 21:09:26 -08:00
Lioncache 7442ef3a83 ARMEmitter: Handle SVE MSB 2023-02-03 23:14:10 -05:00
Lioncache 3783ad8dd1 ARMEmitter: Handle SVE MAD 2023-02-03 23:13:16 -05:00
Lioncache 65ad916984 ARMEmitter: Handle SVE MLS (vectors) 2023-02-03 23:06:42 -05:00
Lioncache d491ce7125 ARMEmitter: Handle SVE MLA (vectors) 2023-02-03 23:05:21 -05:00
Ryan Houdek c0bc5d9748 Merge pull request #2371 from lioncash/mul
ARMEmitter: Handle SVE predicated mul/div and finish off integer reduction category
2023-02-03 19:36:57 -08:00
Lioncache 3f6edf7b5a ARMEmitter: Clarify SVEReductionOperation as working on integer ops 2023-02-03 22:01:34 -05:00
Lioncache 5563a51b84 ARMEmitter: Allow 64-bit variants of min/max reduction
The instructions allow specifying 64-bit element sizes.

With this, we can also completely remove the size checking from the
functions, since the general SVE integer reduction operation already
checks for invalid sizes for us.
2023-02-03 22:00:56 -05:00
Lioncache dac075b871 ARMEmitter: Move min/max reduction over to generic reduction helper
Also enforces the use of a VRegister for the destination argument like
the manual.
2023-02-03 21:45:58 -05:00
Lioncache db8317caf8 ARMEmitter: Handle SVE ANDV (predicated) 2023-02-03 21:31:41 -05:00
Lioncache 3508f7a667 ARMEmitter: Handle SVE EORV (predicated) 2023-02-03 21:30:48 -05:00
Lioncache 02861f41eb ARMEmitter: Handle SVE ORV (predicated) 2023-02-03 21:26:06 -05:00
Lioncache 51c9f70904 ARMEmitter: Handle SVE UADDV (predicated) 2023-02-03 21:05:00 -05:00
Lioncache 4f9530cec3 ARMEmitter: Handle SVE SADDV (predicated) 2023-02-03 21:02:45 -05:00
Lioncache ecd711e691 ARMEmitter: Handle SVE UDIVR (predicated) 2023-02-03 20:49:57 -05:00
Lioncache 870115dd5d ARMEmitter: Handle SVE SDIVR (predicated) 2023-02-03 20:49:57 -05:00
Lioncache 848e5561ce ARMEmitter: Handle SVE UDIV (predicated) 2023-02-03 20:49:57 -05:00
Lioncache db8a9bb5cf ARMEmitter: Handle SVE SDIV (predicated) 2023-02-03 20:49:54 -05:00
Lioncache f8a1c43c06 ARMEmitter: Handle SVE UMULH (predicated) 2023-02-03 20:27:48 -05:00
Lioncache 3a98190119 ARMEmitter: Handle SVE SMULH (predicated) 2023-02-03 20:25:44 -05:00
Lioncache 19ad19193e ARMEmitter: Handle SVE MUL (predicated) 2023-02-03 20:16:24 -05:00
177 changed files with 9354 additions and 5535 deletions

No files matched your search

+5 -6
View File
@@ -72,6 +72,11 @@ jobs:
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE
- name: Install
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target install
- name: ASM Tests
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -199,12 +204,6 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkgenTests.log || true
- name: Install
if: matrix.arch[1] == 'x64'
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target install
- name: Test GL No-Thunks
if: matrix.arch[1] == 'x64'
working-directory: ${{runner.workspace}}/build
-5
View File
@@ -1,5 +0,0 @@
{
"Config": {
"x86dec_SynchronizeRIPOnAllBlocks": "1"
}
}
-5
View File
@@ -1,5 +0,0 @@
{
"Config": {
"x86dec_SynchronizeRIPOnAllBlocks": "1"
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"Config": {
"HideHypervisorBit": "1"
}
}
-5
View File
@@ -1,5 +0,0 @@
{
"Config": {
"x86dec_SynchronizeRIPOnAllBlocks": "1"
}
}
-5
View File
@@ -1,5 +0,0 @@
{
"Config": {
"x86dec_SynchronizeRIPOnAllBlocks": "1"
}
}
+1 -1
View File
@@ -30,7 +30,7 @@
#include <tiny-json.h>
namespace FEXCore::Context {
struct Context;
class Context;
}
namespace FEXCore::Config {
+14 -5
View File
@@ -240,6 +240,17 @@
"Also needs x86_64-linux-gnu-objdump in PATH.",
"Can be very slow."
]
},
"InjectLibSegFault": {
"Type": "bool",
"Default": "false",
"Desc": [
"Sets the environment variable LD_PRELOAD=libSegFault.so",
"This allows the user to very easily enable libSegFault without dealing with environment variables",
"Very useful for applications that have launch scripts that set the variable to nothing at launch",
"Set this in an application configuration for injecting in to only specific applications.",
"\tNote: If x86/x86_64 libSegFault.so isn't installed then this option won't work."
]
}
},
"Logging": {
@@ -332,14 +343,12 @@
"Useful for a process that keeps restarting and doesn't work"
]
},
"x86dec_SynchronizeRIPOnAllBlocks": {
"HideHypervisorBit": {
"Type": "bool",
"Default": "false",
"Desc": [
"An application that uses try-catch or longjump extensively needs the ability to do context aware state flushing",
"In the case of FEX's block-linking, it won't always ensure that RIP is synchronized.",
"If an exception occurs and RIP isn't synchronized, then FEX's exception stack restore may not long jump as expected",
"Can be useful for Wine applications that rely on stack unwinding"
"Hides the hypervisor CPUID bit when set.",
"Should only be used for applications that have issues with this set."
]
}
},
+57 -152
View File
@@ -28,203 +28,108 @@ namespace FEXCore::Context {
FEXCore::Paths::ShutdownPaths();
}
FEXCore::Context::Context *CreateNewContext() {
return new FEXCore::Context::Context{};
FEXCore::Context::Context *FEXCore::Context::Context::CreateNewContext() {
return new FEXCore::Context::ContextImpl{};
}
bool InitializeContext(FEXCore::Context::Context *CTX) {
return FEXCore::CPU::CreateCPUCore(CTX);
}
void DestroyContext(FEXCore::Context::Context *CTX) {
if (CTX->ParentThread) {
CTX->DestroyThread(CTX->ParentThread);
}
void FEXCore::Context::Context::DestroyContext(FEXCore::Context::Context *CTX) {
CTX->DestroyContext();
delete CTX;
}
FEXCore::Core::InternalThreadState* InitCore(FEXCore::Context::Context *CTX, uint64_t InitialRIP, uint64_t StackPointer) {
return CTX->InitCore(InitialRIP, StackPointer);
bool FEXCore::Context::ContextImpl::InitializeContext() {
return FEXCore::CPU::CreateCPUCore(this);
}
void SetExitHandler(FEXCore::Context::Context *CTX, ExitHandler handler) {
CTX->CustomExitHandler = std::move(handler);
void FEXCore::Context::ContextImpl::DestroyContext() {
if (ParentThread) {
DestroyThread(ParentThread);
}
}
ExitHandler GetExitHandler(const FEXCore::Context::Context *CTX) {
return CTX->CustomExitHandler;
void FEXCore::Context::ContextImpl::SetExitHandler(ExitHandler handler) {
CustomExitHandler = std::move(handler);
}
void Run(FEXCore::Context::Context *CTX) {
CTX->Run();
ExitHandler FEXCore::Context::ContextImpl::GetExitHandler() const {
return CustomExitHandler;
}
void Step(FEXCore::Context::Context *CTX) {
CTX->Step();
void FEXCore::Context::ContextImpl::Stop() {
Stop(false);
}
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
Thread->CTX->CompileBlock(Thread->CurrentFrame, GuestRIP);
void FEXCore::Context::ContextImpl::CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
CompileBlock(Thread->CurrentFrame, GuestRIP);
}
FEXCore::Context::ExitReason RunUntilExit(FEXCore::Context::Context *CTX) {
return CTX->RunUntilExit();
FEXCore::Context::ExitReason FEXCore::Context::ContextImpl::GetExitReason() {
return ParentThread->ExitReason;
}
int GetProgramStatus(const FEXCore::Context::Context *CTX) {
return CTX->GetProgramStatus();
bool FEXCore::Context::ContextImpl::IsDone() const {
return IsPaused();
}
FEXCore::Context::ExitReason GetExitReason(const FEXCore::Context::Context *CTX) {
return CTX->ParentThread->ExitReason;
void FEXCore::Context::ContextImpl::GetCPUState(FEXCore::Core::CPUState *State) const {
memcpy(State, ParentThread->CurrentFrame, sizeof(FEXCore::Core::CPUState));
}
bool IsDone(const FEXCore::Context::Context *CTX) {
return CTX->IsPaused();
void FEXCore::Context::ContextImpl::SetCPUState(const FEXCore::Core::CPUState *State) {
memcpy(ParentThread->CurrentFrame, State, sizeof(FEXCore::Core::CPUState));
}
void GetCPUState(const FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
memcpy(State, CTX->ParentThread->CurrentFrame, sizeof(FEXCore::Core::CPUState));
void FEXCore::Context::ContextImpl::SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) {
CustomCPUFactory = std::move(Factory);
}
void SetCPUState(FEXCore::Context::Context *CTX, const FEXCore::Core::CPUState *State) {
memcpy(CTX->ParentThread->CurrentFrame, State, sizeof(FEXCore::Core::CPUState));
}
void Pause(FEXCore::Context::Context *CTX) {
CTX->Pause();
}
void Stop(FEXCore::Context::Context *CTX) {
CTX->Stop(false);
}
void SetCustomCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory) {
CTX->CustomCPUFactory = std::move(Factory);
}
bool AddVirtualMemoryMapping([[maybe_unused]] FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t VirtualAddress, [[maybe_unused]] uint64_t PhysicalAddress, [[maybe_unused]] uint64_t Size) {
bool FEXCore::Context::ContextImpl::AddVirtualMemoryMapping([[maybe_unused]] uint64_t VirtualAddress, [[maybe_unused]] uint64_t PhysicalAddress, [[maybe_unused]] uint64_t Size) {
return false;
}
void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t Syscall, [[maybe_unused]] FEXCore::HLE::SyscallVisitor *Visitor) {
HostFeatures FEXCore::Context::ContextImpl::GetHostFeatures() const {
return HostFeatures;
}
HostFeatures GetHostFeatures(const FEXCore::Context::Context *CTX) {
return CTX->HostFeatures;
void FEXCore::Context::ContextImpl::SetSignalDelegator(FEXCore::SignalDelegator *_SignalDelegation) {
SignalDelegation = _SignalDelegation;
}
void HandleCallback(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) {
CTX->HandleCallback(Thread, RIP);
void FEXCore::Context::ContextImpl::SetSyscallHandler(FEXCore::HLE::SyscallHandler *Handler) {
SyscallHandler = Handler;
SourcecodeResolver = Handler->GetSourcecodeResolver();
}
void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required) {
CTX->RegisterHostSignalHandler(Signal, std::move(Func), Required);
FEXCore::CPUID::FunctionResults FEXCore::Context::ContextImpl::RunCPUIDFunction(uint32_t Function, uint32_t Leaf) {
return CPUID.RunFunction(Function, Leaf);
}
void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required) {
CTX->RegisterFrontendHostSignalHandler(Signal, std::move(Func), Required);
FEXCore::CPUID::FunctionResults FEXCore::Context::ContextImpl::RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) {
return CPUID.RunFunctionName(Function, Leaf, CPU);
}
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
return CTX->CreateThread(NewThreadState, ParentTID);
}
void ExecutionThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
return CTX->ExecutionThread(Thread);
}
void InitializeThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
return CTX->InitializeThread(Thread);
}
void RunThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->RunThread(Thread);
}
void StopThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->StopThread(Thread);
}
void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->DestroyThread(Thread);
}
void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
CTX->CleanupAfterFork(Thread);
}
void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation) {
CTX->SignalDelegation = SignalDelegation;
}
void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler) {
CTX->SyscallHandler = Handler;
CTX->SourcecodeResolver = Handler->GetSourcecodeResolver();
}
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf) {
return CTX->CPUID.RunFunction(Function, Leaf);
}
FEX_DEFAULT_VISIBILITY FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf, uint32_t CPU) {
return CTX->CPUID.RunFunctionName(Function, Leaf, CPU);
}
void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader) {
CTX->SetAOTIRLoader(CacheReader);
}
void SetAOTIRWriter(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) {
CTX->SetAOTIRWriter(CacheWriter);
}
void SetAOTIRRenamer(FEXCore::Context::Context *CTX, std::function<void(const std::string&)> CacheRenamer) {
CTX->SetAOTIRRenamer(CacheRenamer);
}
void FinalizeAOTIRCache(FEXCore::Context::Context *CTX) {
CTX->FinalizeAOTIRCache();
}
void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
CTX->WriteFilesWithCode(Writer);
}
IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(FEXCore::Context::Context *CTX, const std::string &Name) {
return CTX->LoadAOTIRCacheEntry(Name);
}
void UnloadAOTIRCacheEntry(FEXCore::Context::Context *CTX, IR::AOTIRCacheEntry *Entry) {
return CTX->UnloadAOTIRCacheEntry(Entry);
}
CustomIRResult AddCustomIREntrypoint(FEXCore::Context::Context *CTX, uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data) {
return CTX->AddCustomIREntrypoint(Entrypoint, Handler, Creator, Data);
}
void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
CTX->AppendThunkDefinitions(Definitions);
void SetVDSOSigReturn(FEXCore::Context::Context *CTX, const VDSOSigReturn &Pointers) {
CTX->SetVDSOSigReturn(Pointers);
}
namespace Debug {
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
CTX->CompileRIP(CTX->ParentThread, RIP);
}
uint64_t GetThreadCount(FEXCore::Context::Context *CTX) {
return CTX->GetThreadCount();
}
//void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
// CTX->CompileRIP(CTX->ParentThread, RIP);
//}
//uint64_t GetThreadCount(FEXCore::Context::Context *CTX) {
// return CTX->GetThreadCount();
//}
FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(FEXCore::Context::Context *CTX, uint64_t Thread) {
return CTX->GetRuntimeStatsForThread(Thread);
}
//FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(FEXCore::Context::Context *CTX, uint64_t Thread) {
// return CTX->GetRuntimeStatsForThread(Thread);
//}
bool GetDebugDataForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::Core::DebugData *Data) {
return CTX->GetDebugDataForRIP(RIP, Data);
}
//bool GetDebugDataForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::Core::DebugData *Data) {
// return CTX->GetDebugDataForRIP(RIP, Data);
//}
bool FindHostCodeForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, uint8_t **Code) {
return CTX->FindHostCodeForRIP(RIP, Code);
}
//bool FindHostCodeForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, uint8_t **Code) {
// return CTX->FindHostCodeForRIP(RIP, Code);
//}
// XXX:
// bool FindIRForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::IR::IntrusiveIRList **ir) {
+138 -95
View File
@@ -70,7 +70,130 @@ namespace FEXCore::Context {
MODE_SINGLESTEP = 1,
};
struct Context {
class ContextImpl final : public FEXCore::Context::Context {
public:
// Context base class implementation.
bool InitializeContext() override;
void DestroyContext() override;
FEXCore::Core::InternalThreadState* InitCore(uint64_t InitialRIP, uint64_t StackPointer) override;
void SetExitHandler(ExitHandler handler) override;
ExitHandler GetExitHandler() const override;
void Pause() override;
void Run() override;
void Stop() override;
void Step() override;
ExitReason RunUntilExit() override;
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) override;
int GetProgramStatus() const override;
ExitReason GetExitReason() override;
bool IsDone() const override;
void GetCPUState(FEXCore::Core::CPUState *State) const override;
void SetCPUState(const FEXCore::Core::CPUState *State) override;
void SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) override;
bool AddVirtualMemoryMapping(uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size) override;
HostFeatures GetHostFeatures() const override;
void HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) override;
void RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) override;
[[noreturn]] void HandleSignalHandlerReturn(bool RT) override ;
void RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) override;
/**
* @brief Used to create FEX thread objects in preparation for creating a true OS thread. Does set a TID or PID.
*
* @param NewThreadState The initial thread state to setup for our state
* @param ParentTID The PID that was the parent thread that created this
*
* @return The InternalThreadState object that tracks all of the emulated thread's state
*
* Usecases:
* OS thread Creation:
* - Thread = CreateThread(NewState, PPID);
* - InitializeThread(Thread);
* OS fork (New thread created with a clone of thread state):
* - clone{2, 3}
* - Thread = CreateThread(CopyOfThreadState, PPID);
* - ExecutionThread(Thread); // Starts executing without creating another host thread
* Thunk callback executing guest code from native host thread
* - Thread = CreateThread(NewState, PPID);
* - InitializeThreadTLSData(Thread);
* - HandleCallback(Thread, RIP);
*/
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) override;
// Public for threading
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread) override;
/**
* @brief Initializes the OS thread object and prepares to start executing on that new OS thread
*
* @param Thread The internal FEX thread state object
*
* The OS thread will wait until RunThread is executed
*/
void InitializeThread(FEXCore::Core::InternalThreadState *Thread) override;
/**
* @brief Starts the OS thread object to start executing guest code
*
* @param Thread The internal FEX thread state object
*/
void RunThread(FEXCore::Core::InternalThreadState *Thread) override;
void StopThread(FEXCore::Core::InternalThreadState *Thread) override;
/**
* @brief Destroys this FEX thread object and stops tracking it internally
*
* @param Thread The internal FEX thread state object
*/
void DestroyThread(FEXCore::Core::InternalThreadState *Thread) override;
void CleanupAfterFork(FEXCore::Core::InternalThreadState *Thread) override;
void SetSignalDelegator(FEXCore::SignalDelegator *SignalDelegation) override;
void SetSyscallHandler(FEXCore::HLE::SyscallHandler *Handler) override;
FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) override;
FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) override;
FEXCore::IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(const std::string& Name) override;
void UnloadAOTIRCacheEntry(FEXCore::IR::AOTIRCacheEntry *Entry) override;
void SetAOTIRLoader(std::function<int(const std::string&)> CacheReader) override {
IRCaptureCache.SetAOTIRLoader(CacheReader);
}
void SetAOTIRWriter(std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) override {
IRCaptureCache.SetAOTIRWriter(CacheWriter);
}
void SetAOTIRRenamer(std::function<void(const std::string&)> CacheRenamer) override {
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
}
void FinalizeAOTIRCache() override {
IRCaptureCache.FinalizeAOTIRCache();
}
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) override {
IRCaptureCache.WriteFilesWithCode(Writer);
}
void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length) override;
void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> callback) override;
void MarkMemoryShared() override;
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) override;
// returns false if a handler was already registered
CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr) override;
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) override;
public:
friend class FEXCore::HLE::SyscallHandler;
#ifdef JIT_ARM64
friend class FEXCore::CPU::Arm64JITCore;
@@ -116,7 +239,6 @@ namespace FEXCore::Context {
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
FEX_CONFIG_OPT(CacheObjectCodeCompilation, CACHEOBJECTCODECOMPILATION);
FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION);
FEX_CONFIG_OPT(x86dec_SynchronizeRIPOnAllBlocks, X86DEC_SYNCHRONIZERIPONALLBLOCKS);
FEX_CONFIG_OPT(EnableAVX, ENABLEAVX);
} Config;
@@ -152,36 +274,27 @@ namespace FEXCore::Context {
SignalDelegator *SignalDelegation{};
X86GeneratedCode X86CodeGen;
VDSOSigReturn VDSOPointers{};
Context();
~Context();
ContextImpl();
~ContextImpl();
FEXCore::Core::InternalThreadState* InitCore(uint64_t InitialRIP, uint64_t StackPointer);
FEXCore::Context::ExitReason RunUntilExit();
int GetProgramStatus() const;
bool IsPaused() const { return !Running; }
void Pause();
void Run();
void WaitForThreadsToRun();
void Step();
void Stop(bool IgnoreCurrentThread);
void WaitForIdle();
void StopThread(FEXCore::Core::InternalThreadState *Thread);
void SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event);
bool GetGdbServerStatus() const { return DebugServer != nullptr; }
void StartGdbServer();
void StopGdbServer();
void HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
void RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required);
void RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required);
static void ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
static void ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker);
template<auto Fn>
static uint64_t ThreadExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame, uint64_t *record) {
FHU::ScopedSignalMaskWithSharedLock lk(Frame->Thread->CTX->CodeInvalidationMutex);
FHU::ScopedSignalMaskWithSharedLock lk(static_cast<ContextImpl*>(Frame->Thread->CTX)->CodeInvalidationMutex);
return Fn(Frame, record);
}
@@ -190,21 +303,17 @@ namespace FEXCore::Context {
// Must be called from owning thread
static void ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
auto Thread = Frame->Thread;
LogMan::Throw::AFmt(Thread->ThreadManager.GetTID() == FHU::Syscalls::gettid(), "Must be called from owning thread {}, not {}", Thread->ThreadManager.GetTID(), FHU::Syscalls::gettid());
FHU::ScopedSignalMaskWithUniqueLock lk(Thread->CTX->CodeInvalidationMutex);
FHU::ScopedSignalMaskWithUniqueLock lk(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex);
ThreadRemoveCodeEntry(Thread, GuestRIP);
}
// returns false if a handler was already registered
CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data);
void RemoveCustomIREntrypoint(uintptr_t Entrypoint);
// Debugger interface
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
uint64_t GetThreadCount() const;
FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(uint64_t Thread);
bool GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data);
@@ -236,29 +345,6 @@ namespace FEXCore::Context {
void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
// Used for thread creation from syscalls
/**
* @brief Used to create FEX thread objects in preparation for creating a true OS thread. Does set a TID or PID.
*
* @param NewThreadState The initial thread state to setup for our state
* @param ParentTID The PID that was the parent thread that created this
*
* @return The InternalThreadState object that tracks all of the emulated thread's state
*
* Usecases:
* OS thread Creation:
* - Thread = CreateThread(NewState, PPID);
* - InitializeThread(Thread);
* OS fork (New thread created with a clone of thread state):
* - clone{2, 3}
* - Thread = CreateThread(CopyOfThreadState, PPID);
* - ExecutionThread(Thread); // Starts executing without creating another host thread
* Thunk callback executing guest code from native host thread
* - Thread = CreateThread(NewState, PPID);
* - InitializeThreadTLSData(Thread);
* - HandleCallback(Thread, RIP);
*/
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
/**
* @brief Initializes TID, PID and TLS data for a thread
*
@@ -266,71 +352,28 @@ namespace FEXCore::Context {
*/
void InitializeThreadTLSData(FEXCore::Core::InternalThreadState *Thread);
/**
* @brief Initializes the OS thread object and prepares to start executing on that new OS thread
*
* @param Thread The internal FEX thread state object
*
* The OS thread will wait until RunThread is executed
*/
void InitializeThread(FEXCore::Core::InternalThreadState *Thread);
/**
* @brief Starts the OS thread object to start executing guest code
*
* @param Thread The internal FEX thread state object
*/
void RunThread(FEXCore::Core::InternalThreadState *Thread);
/**
* @brief Destroys this FEX thread object and stops tracking it internally
*
* @param Thread The internal FEX thread state object
*/
void DestroyThread(FEXCore::Core::InternalThreadState *Thread);
void CopyMemoryMapping(FEXCore::Core::InternalThreadState *ParentThread, FEXCore::Core::InternalThreadState *ChildThread);
void CleanupAfterFork(FEXCore::Core::InternalThreadState *ExceptForThread);
std::vector<FEXCore::Core::InternalThreadState*>* GetThreads() { return &Threads; }
uint8_t GetGPRSize() const { return Config.Is64BitMode ? 8 : 4; }
IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(const std::string &filename);
void UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry *Entry);
FEXCore::JITSymbols Symbols;
// Public for threading
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
void SetVDSOSigReturn(const VDSOSigReturn &Pointers) override {
VDSOPointers = Pointers;
if (VDSOPointers.VDSO_kernel_sigreturn == nullptr) {
VDSOPointers.VDSO_kernel_sigreturn = reinterpret_cast<void*>(X86CodeGen.sigreturn_32);
}
void FinalizeAOTIRCache() {
IRCaptureCache.FinalizeAOTIRCache();
if (VDSOPointers.VDSO_kernel_rt_sigreturn == nullptr) {
VDSOPointers.VDSO_kernel_rt_sigreturn = reinterpret_cast<void*>(X86CodeGen.rt_sigreturn_32);
}
}
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
IRCaptureCache.WriteFilesWithCode(Writer);
}
void SetAOTIRLoader(std::function<int(const std::string&)> CacheReader) {
IRCaptureCache.SetAOTIRLoader(CacheReader);
}
void SetAOTIRWriter(std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) {
IRCaptureCache.SetAOTIRWriter(CacheWriter);
}
void SetAOTIRRenamer(std::function<void(const std::string&)> CacheRenamer) {
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
}
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
FEXCore::Utils::PooledAllocatorMMap OpDispatcherAllocator;
FEXCore::Utils::PooledAllocatorMMap FrontendAllocator;
void MarkMemoryShared();
bool IsTSOEnabled() { return (IsMemoryShared || !Config.TSOAutoMigration) && Config.TSOEnabled; }
protected:
@@ -5,6 +5,7 @@
#include "Interface/HLE/Thunks/Thunks.h"
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Utils/BitUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
@@ -20,7 +21,7 @@
namespace FEXCore::CPU {
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
Arm64Emitter::Arm64Emitter(FEXCore::Context::ContextImpl *ctx, size_t size)
: Emitter(size ? (uint8_t*)FEXCore::Allocator::mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) : nullptr, size)
, EmitterCTX {ctx} {
CPU.SetUp();
@@ -216,13 +217,13 @@ void Arm64Emitter::SpillStaticRegs(bool FPRs, uint32_t GPRSpillMask, uint32_t FP
if (((1U << Reg.Idx()) & FPRSpillMask) != 0) {
mov(ARMEmitter::Size::i64Bit, TMP4.R(), offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
st1b<ARMEmitter::SubRegSize::i8Bit>(Reg, PRED_TMP_32B, STATE.R(), TMP4.R());
st1b<ARMEmitter::SubRegSize::i8Bit>(Reg.Z(), PRED_TMP_32B, STATE.R(), TMP4.R());
}
}
} else {
if (GPRSpillMask && FPRSpillMask == ~0U) {
// Optimize the common case where we can spill four registers per instruction
auto TmpReg = SRA64[__builtin_ffs(GPRSpillMask)];
auto TmpReg = SRA64[FindFirstSetBit(GPRSpillMask)];
// Load the sse offset in to the temporary register
add(ARMEmitter::Size::i64Bit, TmpReg, STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[0][0]));
@@ -273,14 +274,14 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
const auto Reg = SRAFPR[i];
if (((1U << Reg.Idx()) & FPRFillMask) != 0) {
mov(ARMEmitter::Size::i64Bit, TMP4.R(), offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
ld1b<ARMEmitter::SubRegSize::i8Bit>(Reg, PRED_TMP_32B, STATE.R(), TMP4.R());
ld1b<ARMEmitter::SubRegSize::i8Bit>(Reg.Z(), PRED_TMP_32B.Zeroing(), STATE.R(), TMP4.R());
}
}
} else {
if (GPRFillMask && FPRFillMask == ~0U) {
// Optimize the common case where we can fill four registers per instruction.
// Use one of the filling static registers before we fill it.
auto TmpReg = SRA64[__builtin_ffs(GPRFillMask)];
auto TmpReg = SRA64[FindFirstSetBit(GPRFillMask)];
// Load the sse offset in to the temporary register
add(ARMEmitter::Size::i64Bit, TmpReg, STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[0][0]));
@@ -347,7 +348,7 @@ void Arm64Emitter::PushDynamicRegsAndLR(FEXCore::ARMEmitter::Register TmpReg) {
const auto Reg2 = RAFPR[i + 1];
const auto Reg3 = RAFPR[i + 2];
const auto Reg4 = RAFPR[i + 3];
st4b(Reg1, Reg2, Reg3, Reg4, PRED_TMP_32B, TmpReg, 0);
st4b(Reg1.Z(), Reg2.Z(), Reg3.Z(), Reg4.Z(), PRED_TMP_32B, TmpReg, 0);
add(ARMEmitter::Size::i64Bit, TmpReg, TmpReg, 32 * 4);
}
} else {
@@ -373,7 +374,7 @@ void Arm64Emitter::PopDynamicRegsAndLR() {
const auto Reg2 = RAFPR[i + 1];
const auto Reg3 = RAFPR[i + 2];
const auto Reg4 = RAFPR[i + 3];
ld4b(Reg1, Reg2, Reg3, Reg4, PRED_TMP_32B, ARMEmitter::Reg::rsp);
ld4b(Reg1.Z(), Reg2.Z(), Reg3.Z(), Reg4.Z(), PRED_TMP_32B.Zeroing(), ARMEmitter::Reg::rsp);
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, 32 * 4);
}
} else {
@@ -85,10 +85,10 @@ constexpr FEXCore::ARMEmitter::PRegister PRED_TMP_32B = FEXCore::ARMEmitter::PRe
// be used by both Arm64 JIT and ARM64 Dispatcher
class Arm64Emitter : public FEXCore::ARMEmitter::Emitter {
protected:
Arm64Emitter(FEXCore::Context::Context *ctx, size_t size);
Arm64Emitter(FEXCore::Context::ContextImpl *ctx, size_t size);
~Arm64Emitter();
FEXCore::Context::Context *EmitterCTX;
FEXCore::Context::ContextImpl *EmitterCTX;
vixl::aarch64::CPU CPU;
void LoadConstant(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register Reg, uint64_t Constant, bool NOPPad = false);
@@ -257,8 +257,8 @@ public:
void sxth(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn) {
sbfm(s, rd, rn, 0, 15);
}
void sxtw(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::XRegister rn) {
sbfm(ARMEmitter::Size::i64Bit, rd, rn, 0, 31);
void sxtw(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn) {
sbfm(ARMEmitter::Size::i64Bit, rd, rn.X(), 0, 31);
}
void sbfx(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t lsb, uint32_t width) {
LOGMAN_THROW_A_FMT(width > 0, "sbfx needs width > 0");
@@ -287,12 +287,12 @@ public:
void lsl(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t shift) {
const auto RegSize = RegSizeInBits(s);
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to asr a region larger than the register");
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to lsl a region larger than the register");
ubfm(s, rd, rn, (RegSize - shift) % RegSize, RegSize - shift - 1);
}
void lsr(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t shift) {
const auto RegSize = RegSizeInBits(s);
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to asr a region larger than the register");
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to lsr a region larger than the register");
ubfm(s, rd, rn, shift, RegSize - 1);
}
void ubfx(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t lsb, uint32_t width) {
@@ -303,8 +303,8 @@ public:
void bfi(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t lsb, uint32_t width) {
const auto RegSize = RegSizeInBits(s);
LOGMAN_THROW_A_FMT(width > 0, "sbfx needs width > 0");
LOGMAN_THROW_A_FMT((lsb + width) <= RegSize, "Tried to sbfx a region larger than the register");
LOGMAN_THROW_A_FMT(width > 0, "bfi needs width > 0");
LOGMAN_THROW_A_FMT((lsb + width) <= RegSize, "Tried to bfi a region larger than the register");
bfm(s, rd, rn, (RegSize - lsb) & (RegSize - 1), width - 1);
}
@@ -316,7 +316,6 @@ public:
}
void ror(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t Imm) {
LOGMAN_THROW_A_FMT(Imm < RegSizeInBits(s), "Tried to extr a region larger than the register");
extr(s, rd, rn, rn, Imm);
}
@@ -711,28 +710,28 @@ public:
DataProcessing_3Source(Op, 0, s, rd, rn, rm, ra);
}
void mul(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm) {
madd(s, rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
madd(s, rd, rn, rm, XReg::zr);
}
void msub(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm, FEXCore::ARMEmitter::Register ra) {
constexpr uint32_t Op = 0b001'1011'000U << 21;
DataProcessing_3Source(Op, 1, s, rd, rn, rm, ra);
}
void mneg(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm) {
msub(s, rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
msub(s, rd, rn, rm, XReg::zr);
}
void smaddl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm, FEXCore::ARMEmitter::XRegister ra) {
constexpr uint32_t Op = 0b001'1011'001U << 21;
DataProcessing_3Source(Op, 0, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
}
void smull(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
smaddl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
smaddl(rd, rn, rm, XReg::zr);
}
void smsubl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm, FEXCore::ARMEmitter::XRegister ra) {
constexpr uint32_t Op = 0b001'1011'001U << 21;
DataProcessing_3Source(Op, 1, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
}
void smnegl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
smsubl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
smsubl(rd, rn, rm, XReg::zr);
}
void smulh(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::XRegister rn, FEXCore::ARMEmitter::XRegister rm) {
constexpr uint32_t Op = 0b001'1011'010U << 21;
@@ -743,14 +742,14 @@ public:
DataProcessing_3Source(Op, 0, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
}
void umull(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
umaddl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
umaddl(rd, rn, rm, XReg::zr);
}
void umsubl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm, FEXCore::ARMEmitter::XRegister ra) {
constexpr uint32_t Op = 0b001'1011'101U << 21;
DataProcessing_3Source(Op, 1, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
}
void umnegl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
umsubl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
umsubl(rd, rn, rm, XReg::zr);
}
void umulh(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::XRegister rn, FEXCore::ARMEmitter::XRegister rm) {
constexpr uint32_t Op = 0b001'1011'110U << 21;
File diff suppressed because it is too large. Load diff
@@ -544,6 +544,36 @@ namespace FEXCore::ARMEmitter {
ROTATE_270 = 0b11,
};
// Concept for contraining some instructions to accept only an XRegister or WRegister.
// Particularly for operations that differ encodings depending on which one is used.
template <typename T>
concept IsXOrWRegister = std::is_same_v<T, XRegister> || std::is_same_v<T, WRegister>;
// Whether or not a given set of vector registers are sequential
// in increasing order as far as the register file is concerned (modulo its size)
//
// For example, a set of registers like:
//
// v1, v2, v3 and
// v31, v0, v1
//
// would both be considered sequential sequences, and some instructions in particular
// limit register lists to these kind of sequences.
//
template <typename T, typename... Args>
constexpr bool AreVectorsSequential(T first, const Args&... args) {
// Ensure we always have a pair of registers to compare against.
static_assert(sizeof...(args) >= 1, "Number of arguments must be greater than 1");
const auto fn = [](auto& lhs, const auto& rhs) {
const auto result = ((lhs.Idx() + 1) % 32) == rhs.Idx();
lhs = rhs;
return result;
};
return (fn(first, args) && ...);
}
// 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.
File diff suppressed because it is too large. Load diff
@@ -1,5 +1,8 @@
#pragma once
#include <FEXCore/Utils/EnumUtils.h>
#include <compare>
#include <cstdint>
namespace FEXCore::ARMEmitter {
@@ -15,13 +18,12 @@ namespace FEXCore::ARMEmitter {
constexpr explicit Register(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const Register&, const Register&) = default;
uint32_t Idx() const {
return Index;
}
operator WRegister() const;
operator XRegister() const;
WRegister W() const;
XRegister X() const;
@@ -41,9 +43,7 @@ namespace FEXCore::ARMEmitter {
constexpr explicit WRegister(uint32_t Idx)
: Index {Idx} {}
bool operator==(const WRegister &rhs) {
return Idx() == rhs.Idx();
}
friend constexpr auto operator<=>(const WRegister&, const WRegister&) = default;
uint32_t Idx() const {
return Index;
@@ -53,10 +53,7 @@ namespace FEXCore::ARMEmitter {
return Register(Index);
}
operator XRegister() const;
XRegister X() const;
Register R() const;
private:
@@ -75,9 +72,7 @@ namespace FEXCore::ARMEmitter {
constexpr explicit XRegister(uint32_t Idx)
: Index {Idx} {}
bool operator==(const XRegister &rhs) {
return Idx() == rhs.Idx();
}
friend constexpr auto operator<=>(const XRegister&, const XRegister&) = default;
uint32_t Idx() const {
return Index;
@@ -87,10 +82,7 @@ namespace FEXCore::ARMEmitter {
return Register(Index);
}
operator WRegister() const;
WRegister W() const;
Register R() const;
private:
@@ -101,45 +93,29 @@ namespace FEXCore::ARMEmitter {
static_assert(std::is_standard_layout_v<Register>, "Needs to be standard");
inline WRegister Register::W() const {
return *this;
return WRegister{Index};
}
inline XRegister Register::X() const {
return *this;
}
inline Register::operator WRegister () const {
return WRegister(Index);
}
inline Register::operator XRegister () const {
return XRegister(Index);
return XRegister{Index};
}
inline XRegister WRegister::X() const {
return *this;
return XRegister{Index};
}
inline Register WRegister::R() const {
return *this;
}
inline WRegister::operator XRegister () const {
return XRegister(Index);
}
inline WRegister XRegister::W() const {
return *this;
return WRegister{Index};
}
inline Register XRegister::R() const {
return *this;
}
inline XRegister::operator WRegister () const {
return WRegister(Index);
}
// Namespace containing all unsized GPR register objects.
namespace Reg {
constexpr static Register r0(0);
@@ -291,20 +267,15 @@ namespace FEXCore::ARMEmitter {
class VRegister {
public:
VRegister() = delete;
constexpr VRegister(uint32_t Idx)
constexpr explicit VRegister(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const VRegister&, const VRegister&) = default;
uint32_t Idx() const {
return Index;
}
operator BRegister() const;
operator HRegister() const;
operator SRegister() const;
operator DRegister() const;
operator QRegister() const;
operator ZRegister() const;
BRegister B() const;
HRegister H() const;
SRegister S() const;
@@ -328,16 +299,15 @@ namespace FEXCore::ARMEmitter {
constexpr explicit BRegister(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const BRegister&, const BRegister&) = default;
uint32_t Idx() const {
return Index;
}
operator VRegister() const;
operator HRegister() const;
operator SRegister() const;
operator DRegister() const;
operator QRegister() const;
operator ZRegister() const;
operator VRegister () const {
return VRegister(Index);
}
BRegister V() const;
HRegister H() const;
@@ -362,16 +332,15 @@ namespace FEXCore::ARMEmitter {
constexpr explicit HRegister(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const HRegister&, const HRegister&) = default;
uint32_t Idx() const {
return Index;
}
operator VRegister() const;
operator BRegister() const;
operator SRegister() const;
operator DRegister() const;
operator QRegister() const;
operator ZRegister() const;
operator VRegister() const {
return VRegister(Index);
}
HRegister V() const;
BRegister B() const;
@@ -396,16 +365,15 @@ namespace FEXCore::ARMEmitter {
constexpr explicit SRegister(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const SRegister&, const SRegister&) = default;
uint32_t Idx() const {
return Index;
}
operator VRegister() const;
operator BRegister() const;
operator HRegister() const;
operator DRegister() const;
operator QRegister() const;
operator ZRegister() const;
operator VRegister() const {
return VRegister(Index);
}
SRegister V() const;
BRegister B() const;
@@ -431,16 +399,15 @@ namespace FEXCore::ARMEmitter {
constexpr explicit DRegister(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const DRegister&, const DRegister&) = default;
uint32_t Idx() const {
return Index;
}
operator VRegister() const;
operator BRegister() const;
operator HRegister() const;
operator SRegister() const;
operator QRegister() const;
operator ZRegister() const;
operator VRegister() const {
return VRegister(Index);
}
DRegister V() const;
BRegister B() const;
@@ -466,16 +433,15 @@ namespace FEXCore::ARMEmitter {
constexpr explicit QRegister(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const QRegister&, const QRegister&) = default;
uint32_t Idx() const {
return Index;
}
operator VRegister() const;
operator BRegister() const;
operator HRegister() const;
operator SRegister() const;
operator DRegister() const;
operator ZRegister() const;
operator VRegister () const {
return VRegister(Index);
}
QRegister V() const;
BRegister B() const;
@@ -500,6 +466,8 @@ namespace FEXCore::ARMEmitter {
constexpr explicit ZRegister(uint32_t Idx)
: Index {Idx} {}
friend constexpr auto operator<=>(const ZRegister&, const ZRegister&) = default;
uint32_t Idx() const {
return Index;
}
@@ -520,41 +488,22 @@ namespace FEXCore::ARMEmitter {
// VRegister
inline BRegister VRegister::B() const {
return *this;
return BRegister{Index};
}
inline HRegister VRegister::H() const {
return *this;
return HRegister{Index};
}
inline SRegister VRegister::S() const {
return *this;
return SRegister{Index};
}
inline DRegister VRegister::D() const {
return *this;
return DRegister{Index};
}
inline QRegister VRegister::Q() const {
return *this;
return QRegister{Index};
}
inline ZRegister VRegister::Z() const {
return *this;
}
inline VRegister::operator BRegister () const {
return BRegister(Index);
}
inline VRegister::operator HRegister () const {
return HRegister(Index);
}
inline VRegister::operator SRegister () const {
return SRegister(Index);
}
inline VRegister::operator DRegister () const {
return DRegister(Index);
}
inline VRegister::operator QRegister () const {
return QRegister(Index);
}
inline VRegister::operator ZRegister () const {
return ZRegister(Index);
return ZRegister{Index};
}
// BRegister
@@ -562,38 +511,19 @@ namespace FEXCore::ARMEmitter {
return *this;
}
inline HRegister BRegister::H() const {
return *this;
return HRegister{Index};
}
inline SRegister BRegister::S() const {
return *this;
return SRegister{Index};
}
inline DRegister BRegister::D() const {
return *this;
return DRegister{Index};
}
inline QRegister BRegister::Q() const {
return *this;
return QRegister{Index};
}
inline ZRegister BRegister::Z() const {
return *this;
}
inline BRegister::operator VRegister () const {
return VRegister(Index);
}
inline BRegister::operator HRegister () const {
return HRegister(Index);
}
inline BRegister::operator SRegister () const {
return SRegister(Index);
}
inline BRegister::operator DRegister () const {
return DRegister(Index);
}
inline BRegister::operator QRegister () const {
return QRegister(Index);
}
inline BRegister::operator ZRegister () const {
return ZRegister(Index);
return ZRegister{Index};
}
// HRegister
@@ -601,38 +531,19 @@ namespace FEXCore::ARMEmitter {
return *this;
}
inline BRegister HRegister::B() const {
return *this;
return BRegister{Index};
}
inline SRegister HRegister::S() const {
return *this;
return SRegister{Index};
}
inline DRegister HRegister::D() const {
return *this;
return DRegister{Index};
}
inline QRegister HRegister::Q() const {
return *this;
return QRegister{Index};
}
inline ZRegister HRegister::Z() const {
return *this;
}
inline HRegister::operator VRegister () const {
return VRegister(Index);
}
inline HRegister::operator BRegister () const {
return BRegister(Index);
}
inline HRegister::operator SRegister () const {
return SRegister(Index);
}
inline HRegister::operator DRegister () const {
return DRegister(Index);
}
inline HRegister::operator QRegister () const {
return QRegister(Index);
}
inline HRegister::operator ZRegister () const {
return ZRegister(Index);
return ZRegister{Index};
}
// SRegister
@@ -640,77 +551,39 @@ namespace FEXCore::ARMEmitter {
return *this;
}
inline BRegister SRegister::B() const {
return *this;
return BRegister{Index};
}
inline HRegister SRegister::H() const {
return *this;
return HRegister{Index};
}
inline DRegister SRegister::D() const {
return *this;
return DRegister{Index};
}
inline QRegister SRegister::Q() const {
return *this;
return QRegister{Index};
}
inline ZRegister SRegister::Z() const {
return *this;
}
inline SRegister::operator VRegister () const {
return VRegister(Index);
}
inline SRegister::operator BRegister () const {
return BRegister(Index);
}
inline SRegister::operator HRegister () const {
return HRegister(Index);
}
inline SRegister::operator DRegister () const {
return DRegister(Index);
}
inline SRegister::operator QRegister () const {
return QRegister(Index);
}
inline SRegister::operator ZRegister () const {
return ZRegister(Index);
return ZRegister{Index};
}
// DRegister
inline DRegister DRegister::V() const {
return *this;
return DRegister{Index};
}
inline BRegister DRegister::B() const {
return *this;
return BRegister{Index};
}
inline HRegister DRegister::H() const {
return *this;
return HRegister{Index};
}
inline SRegister DRegister::S() const {
return *this;
return SRegister{Index};
}
inline QRegister DRegister::Q() const {
return *this;
return QRegister{Index};
}
inline ZRegister DRegister::Z() const {
return *this;
}
inline DRegister::operator VRegister () const {
return VRegister(Index);
}
inline DRegister::operator BRegister () const {
return BRegister(Index);
}
inline DRegister::operator HRegister () const {
return HRegister(Index);
}
inline DRegister::operator SRegister () const {
return SRegister(Index);
}
inline DRegister::operator QRegister () const {
return QRegister(Index);
}
inline DRegister::operator ZRegister () const {
return ZRegister(Index);
return ZRegister{Index};
}
// QRegister
@@ -718,38 +591,19 @@ namespace FEXCore::ARMEmitter {
return *this;
}
inline BRegister QRegister::B() const {
return *this;
return BRegister{Index};
}
inline HRegister QRegister::H() const {
return *this;
return HRegister{Index};
}
inline SRegister QRegister::S() const {
return *this;
return SRegister{Index};
}
inline DRegister QRegister::D() const {
return *this;
return DRegister{Index};
}
inline ZRegister QRegister::Z() const {
return *this;
}
inline QRegister::operator VRegister () const {
return VRegister(Index);
}
inline QRegister::operator BRegister () const {
return BRegister(Index);
}
inline QRegister::operator HRegister () const {
return HRegister(Index);
}
inline QRegister::operator SRegister () const {
return SRegister(Index);
}
inline QRegister::operator DRegister () const {
return DRegister(Index);
}
inline QRegister::operator ZRegister () const {
return ZRegister(Index);
return ZRegister{Index};
}
// ZRegister
@@ -1069,17 +923,12 @@ namespace FEXCore::ARMEmitter {
constexpr PRegister(uint32_t Idx)
: Index {Idx} {}
operator uint32_t() const {
return Index;
}
friend constexpr auto operator<=>(const PRegister&, const PRegister&) = default;
uint32_t Idx() const {
return Index;
}
operator PRegisterZero() const;
operator PRegisterMerge() const;
PRegisterZero Zeroing() const;
PRegisterMerge Merging() const;
@@ -1097,16 +946,13 @@ namespace FEXCore::ARMEmitter {
constexpr PRegisterZero(uint32_t Idx)
: Index {Idx} {}
operator uint32_t() const {
return Index;
}
friend constexpr auto operator<=>(const PRegisterZero&, const PRegisterZero&) = default;
uint32_t Idx() const {
return Index;
}
operator PRegister() const;
operator PRegisterMerge() const;
PRegister P() const;
PRegisterMerge Merging() const;
@@ -1125,16 +971,13 @@ namespace FEXCore::ARMEmitter {
constexpr PRegisterMerge(uint32_t Idx)
: Index {Idx} {}
operator uint32_t() const {
return Index;
}
friend constexpr auto operator<=>(const PRegisterMerge&, const PRegisterMerge&) = default;
uint32_t Idx() const {
return Index;
}
operator PRegister() const;
operator PRegisterZero() const;
PRegister P() const;
PRegisterZero Zeroing() const;
@@ -1148,14 +991,6 @@ namespace FEXCore::ARMEmitter {
// PRegister
inline PRegister::operator PRegisterZero() const {
return PRegisterZero(Index);
}
inline PRegister::operator PRegisterMerge() const {
return PRegisterMerge(Index);
}
inline PRegisterZero PRegister::Zeroing() const {
return PRegisterZero(Idx());
}
@@ -1169,10 +1004,6 @@ namespace FEXCore::ARMEmitter {
return PRegister(Index);
}
inline PRegisterZero::operator PRegisterMerge() const {
return PRegisterMerge(Index);
}
inline PRegister PRegisterZero::P() const {
return PRegister(Idx());
}
@@ -1186,10 +1017,6 @@ namespace FEXCore::ARMEmitter {
return PRegisterZero(Index);
}
inline PRegisterMerge::operator PRegisterZero() const {
return PRegisterZero(Index);
}
inline PRegister PRegisterMerge::P() const {
return PRegister(Idx());
}
File diff suppressed because it is too large. Load diff
@@ -17,25 +17,17 @@
*/
public:
// Advanced SIMD scalar copy
void dup(FEXCore::ARMEmitter::ScalarRegSize size, FEXCore::ARMEmitter::VRegister rd, FEXCore::ARMEmitter::VRegister rn, uint32_t Index) {
void dup(ScalarRegSize size, VRegister rd, VRegister rn, uint32_t Index) {
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'01 << 10;
uint32_t imm5 = 0b00000;
if (size == ScalarRegSize::i8Bit) {
LOGMAN_THROW_AA_FMT(Index < 16, "Index too large");
imm5 = (Index << 1) | 1;
}
else if (size == ScalarRegSize::i16Bit) {
LOGMAN_THROW_AA_FMT(Index < 8, "Index too large");
imm5 = (Index << 2) | 0b10;
}
else if (size == ScalarRegSize::i32Bit) {
LOGMAN_THROW_AA_FMT(Index < 4, "Index too large");
imm5 = (Index << 3) | 0b100;
}
else if (size == ScalarRegSize::i64Bit) {
LOGMAN_THROW_AA_FMT(Index < 2, "Index too large");
imm5 = (Index << 4) | 0b1000;
}
const uint32_t SizeImm = FEXCore::ToUnderlying(size);
const uint32_t IndexShift = SizeImm + 1;
const uint32_t ElementSize = 1U << SizeImm;
const uint32_t MaxIndex = 128U / (ElementSize * 8);
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
ASIMDScalarCopy(Op, 1, imm5, 0b0000, rd, rn);
}
@@ -15,7 +15,6 @@ namespace FEXCore::ArchHelpers::Context {
enum ContextFlags : uint32_t {
CONTEXT_FLAG_INJIT = (1U << 0),
CONTEXT_FLAG_32BIT = (1U << 1),
};
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
@@ -238,8 +237,10 @@ static inline void BackupContext(void* ucontext, T *Backup) {
template <typename T>
static inline void RestoreContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, ArmContextBackup>::value) {
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
auto _ucontext = GetUContext(ucontext);
auto _mcontext = GetMContext(ucontext);
auto _mcontext = GetMContext(ucontext);
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
LOGMAN_THROW_AA_FMT(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x{:08x}", HostState->Head.Magic);
@@ -255,8 +256,6 @@ 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");
@@ -335,6 +334,8 @@ static inline void BackupContext(void* ucontext, T *Backup) {
template <typename T>
static inline void RestoreContext(void* ucontext, T *Backup) {
if constexpr (std::is_same<T, X86ContextBackup>::value) {
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
auto _ucontext = GetUContext(ucontext);
auto _mcontext = GetMContext(ucontext);
@@ -345,8 +346,6 @@ 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");
+2 -2
View File
@@ -60,8 +60,8 @@ auto CPUBackend::AllocateNewCodeBuffer(size_t Size) -> CodeBuffer {
FEXCore::Allocator::mmap(nullptr, Buffer.Size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
LOGMAN_THROW_AA_FMT(!!Buffer.Ptr, "Couldn't allocate code buffer");
if (ThreadState->CTX->Config.GlobalJITNaming()) {
ThreadState->CTX->Symbols.RegisterJITSpace(Buffer.Ptr, Buffer.Size);
if (static_cast<Context::ContextImpl*>(ThreadState->CTX)->Config.GlobalJITNaming()) {
static_cast<Context::ContextImpl*>(ThreadState->CTX)->Symbols.RegisterJITSpace(Buffer.Ptr, Buffer.Size);
}
return Buffer;
}
+5 -2
View File
@@ -410,6 +410,9 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) {
// XXX: Enable once the rest of the SSE4.2 instructions are emulated
uint32_t SupportsSSE42 = CTX->HostFeatures.SupportsCRC && false ? 1 : 0;
// Hypervisor bit is normally set but some applications have issues with it.
uint32_t Hypervisor = HideHypervisorBit() ? 0 : 1;
Res.eax = FAMILY_IDENTIFIER;
Res.ebx = 0 | // Brand index
@@ -449,7 +452,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) {
(SUPPORTS_AVX << 28) | // AVX
(0 << 29) | // F16C
(CTX->HostFeatures.SupportsRAND << 30) | // RDRAND
(1 << 31); // Hypervisor always returns one
(Hypervisor << 31);
Res.edx =
(1 << 0) | // FPU
@@ -1210,7 +1213,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_Reserved(uint32_t Leaf) {
return Res;
}
void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
void CPUIDEmu::Init(FEXCore::Context::ContextImpl *ctx) {
CTX = ctx;
// Setup some state tracking
+4 -3
View File
@@ -10,7 +10,7 @@
namespace FEXCore {
namespace Context {
struct Context;
class ContextImpl;
}
// Debugging define to switch what family of CPU we execute as.
@@ -31,7 +31,7 @@ public:
// if we report anything differently then applications are likely to break
constexpr static uint64_t CACHELINE_SIZE = 64;
void Init(FEXCore::Context::Context *ctx);
void Init(FEXCore::Context::ContextImpl *ctx);
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, uint32_t Leaf) {
if (Function < Primary.size()) {
@@ -64,9 +64,10 @@ public:
}
private:
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
bool Hybrid{};
FEX_CONFIG_OPT(Cores, THREADS);
FEX_CONFIG_OPT(HideHypervisorBit, HIDEHYPERVISORBIT);
using FunctionHandler = FEXCore::CPUID::FunctionResults (CPUIDEmu::*)(uint32_t Leaf);
struct CPUData {
+97 -89
View File
@@ -79,7 +79,7 @@ $end_info$
namespace FEXCore::CPU {
bool CreateCPUCore(FEXCore::Context::Context *CTX) {
bool CreateCPUCore(Context::ContextImpl *CTX) {
// This should be used for generating things that are shared between threads
CTX->CPUID.Init(CTX);
return true;
@@ -147,7 +147,7 @@ std::string_view const& GetGRegName(unsigned Reg) {
} // namespace FEXCore::Core
namespace FEXCore::Context {
Context::Context()
ContextImpl::ContextImpl()
: IRCaptureCache {this} {
#ifdef BLOCKSTATS
BlockData = std::make_unique<FEXCore::BlockSamplingData>();
@@ -172,7 +172,7 @@ namespace FEXCore::Context {
}
}
Context::~Context() {
ContextImpl::~ContextImpl() {
{
if (CodeObjectCacheService) {
CodeObjectCacheService->Shutdown();
@@ -214,7 +214,7 @@ namespace FEXCore::Context {
return NewThreadState;
}
FEXCore::Core::InternalThreadState* Context::InitCore(uint64_t InitialRIP, uint64_t StackPointer) {
FEXCore::Core::InternalThreadState* ContextImpl::InitCore(uint64_t InitialRIP, uint64_t StackPointer) {
// Initialize the CPU core signal handlers & DispatcherConfig
switch (Config.Core) {
#ifdef INTERPRETER_ENABLED
@@ -255,13 +255,13 @@ namespace FEXCore::Context {
// Initialize common signal handlers
auto PauseHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
return Thread->CTX->Dispatcher->HandleSignalPause(Thread, Signal, info, ucontext);
return static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->HandleSignalPause(Thread, Signal, info, ucontext);
};
SignalDelegation->RegisterHostSignalHandler(SignalDelegator::SIGNAL_FOR_PAUSE, PauseHandler, true);
auto GuestSignalHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) -> bool {
return Thread->CTX->Dispatcher->HandleGuestSignal(Thread, Signal, info, ucontext, GuestAction, GuestStack);
return static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->HandleGuestSignal(Thread, Signal, info, ucontext, GuestAction, GuestStack);
};
for (uint32_t Signal = 0; Signal <= SignalDelegator::MAX_SIGNALS; ++Signal) {
@@ -295,30 +295,45 @@ namespace FEXCore::Context {
return Thread;
}
void Context::StartGdbServer() {
void ContextImpl::StartGdbServer() {
if (!DebugServer) {
DebugServer = std::make_unique<GdbServer>(this);
StartPaused = true;
}
}
void Context::StopGdbServer() {
void ContextImpl::StopGdbServer() {
DebugServer.reset();
}
void Context::HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) {
Thread->CTX->Dispatcher->ExecuteJITCallback(Thread->CurrentFrame, RIP);
void ContextImpl::HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) {
static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->ExecuteJITCallback(Thread->CurrentFrame, RIP);
}
void Context::RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
void ContextImpl::HandleSignalHandlerReturn(bool RT) {
using SignalHandlerReturnFunc = void(*)();
SignalHandlerReturnFunc SignalHandlerReturn{};
if (RT) {
SignalHandlerReturn = reinterpret_cast<SignalHandlerReturnFunc>(Dispatcher->SignalHandlerReturnAddressRT);
}
else {
SignalHandlerReturn = reinterpret_cast<SignalHandlerReturnFunc>(Dispatcher->SignalHandlerReturnAddress);
}
SignalHandlerReturn();
FEX_UNREACHABLE;
}
void ContextImpl::RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
SignalDelegation->RegisterHostSignalHandler(Signal, Func, Required);
}
void Context::RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
void ContextImpl::RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
SignalDelegation->RegisterFrontendHostSignalHandler(Signal, Func, Required);
}
void Context::WaitForIdle() {
void ContextImpl::WaitForIdle() {
std::unique_lock<std::mutex> lk(IdleWaitMutex);
IdleWaitCV.wait(lk, [this] {
return IdleWaitRefCount.load() == 0;
@@ -327,7 +342,7 @@ namespace FEXCore::Context {
Running = false;
}
void Context::WaitForIdleWithTimeout() {
void ContextImpl::WaitForIdleWithTimeout() {
std::unique_lock<std::mutex> lk(IdleWaitMutex);
bool WaitResult = IdleWaitCV.wait_for(lk, std::chrono::milliseconds(1500),
[this] {
@@ -345,7 +360,7 @@ namespace FEXCore::Context {
WaitForIdle();
}
void Context::NotifyPause() {
void ContextImpl::NotifyPause() {
// Tell all the threads that they should pause
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
@@ -358,7 +373,7 @@ namespace FEXCore::Context {
}
}
void Context::Pause() {
void ContextImpl::Pause() {
// If we aren't running, WaitForIdle will never compete.
if (Running) {
NotifyPause();
@@ -367,7 +382,7 @@ namespace FEXCore::Context {
}
}
void Context::Run() {
void ContextImpl::Run() {
// Spin up all the threads
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
for (auto &Thread : Threads) {
@@ -379,7 +394,7 @@ namespace FEXCore::Context {
}
}
void Context::WaitForThreadsToRun() {
void ContextImpl::WaitForThreadsToRun() {
size_t NumThreads{};
{
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
@@ -395,7 +410,7 @@ namespace FEXCore::Context {
Running = true;
}
void Context::Step() {
void ContextImpl::Step() {
{
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
// Walk the threads and tell them to clear their caches
@@ -415,7 +430,7 @@ namespace FEXCore::Context {
this->Config.MaxInstPerBlock = PreviousMaxIntPerBlock;
}
void Context::Stop(bool IgnoreCurrentThread) {
void ContextImpl::Stop(bool IgnoreCurrentThread) {
pid_t tid = FHU::Syscalls::gettid();
FEXCore::Core::InternalThreadState* CurrentThread{};
@@ -453,21 +468,21 @@ namespace FEXCore::Context {
}
}
void Context::StopThread(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::StopThread(FEXCore::Core::InternalThreadState *Thread) {
if (Thread->RunningEvents.Running.exchange(false)) {
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Stop);
FHU::Syscalls::tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
}
}
void Context::SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event) {
void ContextImpl::SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event) {
if (Thread->RunningEvents.Running.load()) {
Thread->SignalReason.store(Event);
FHU::Syscalls::tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
}
}
FEXCore::Context::ExitReason Context::RunUntilExit() {
FEXCore::Context::ExitReason ContextImpl::RunUntilExit() {
if(!StartPaused) {
// We will only have one thread at this point, but just in case run notify everything
std::lock_guard lk(ThreadCreationMutex);
@@ -488,16 +503,16 @@ namespace FEXCore::Context {
}
}
int Context::GetProgramStatus() const {
int ContextImpl::GetProgramStatus() const {
return ParentThread->StatusCode;
}
void Context::InitializeThreadData(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::InitializeThreadData(FEXCore::Core::InternalThreadState *Thread) {
Thread->CPUBackend->Initialize();
}
struct ExecutionThreadHandler {
FEXCore::Context::Context *This;
ContextImpl *This;
FEXCore::Core::InternalThreadState *Thread;
};
@@ -508,7 +523,7 @@ namespace FEXCore::Context {
return nullptr;
}
void Context::InitializeThread(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::InitializeThread(FEXCore::Core::InternalThreadState *Thread) {
// This will create the execution thread but it won't actually start executing
ExecutionThreadHandler *Arg = reinterpret_cast<ExecutionThreadHandler*>(FEXCore::Allocator::malloc(sizeof(ExecutionThreadHandler)));
Arg->This = this;
@@ -530,7 +545,7 @@ namespace FEXCore::Context {
}
}
void Context::InitializeThreadTLSData(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::InitializeThreadTLSData(FEXCore::Core::InternalThreadState *Thread) {
// Let's do some initial bookkeeping here
Thread->ThreadManager.TID = FHU::Syscalls::gettid();
Thread->ThreadManager.PID = ::getpid();
@@ -538,12 +553,12 @@ namespace FEXCore::Context {
ThunkHandler->RegisterTLSState(Thread);
}
void Context::RunThread(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::RunThread(FEXCore::Core::InternalThreadState *Thread) {
// Tell the thread to start executing
Thread->StartRunning.NotifyAll();
}
void Context::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread) {
void ContextImpl::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread) {
Thread->OpDispatcher = std::make_unique<FEXCore::IR::OpDispatchBuilder>(this);
Thread->OpDispatcher->SetMultiblock(Config.Multiblock);
Thread->LookupCache = std::make_unique<FEXCore::LookupCache>(this);
@@ -595,7 +610,7 @@ namespace FEXCore::Context {
}
}
FEXCore::Core::InternalThreadState* Context::CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
FEXCore::Core::InternalThreadState* ContextImpl::CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
FEXCore::Core::InternalThreadState *Thread = new FEXCore::Core::InternalThreadState{};
// Copy over the new thread state to the new object
@@ -617,7 +632,7 @@ namespace FEXCore::Context {
return Thread;
}
void Context::DestroyThread(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::DestroyThread(FEXCore::Core::InternalThreadState *Thread) {
// remove new thread object
{
std::lock_guard lk(ThreadCreationMutex);
@@ -636,7 +651,7 @@ namespace FEXCore::Context {
delete Thread;
}
void Context::CleanupAfterFork(FEXCore::Core::InternalThreadState *LiveThread) {
void ContextImpl::CleanupAfterFork(FEXCore::Core::InternalThreadState *LiveThread) {
// This function is called after fork
// We need to cleanup some of the thread data that is dead
for (auto &DeadThread : Threads) {
@@ -675,11 +690,11 @@ namespace FEXCore::Context {
FEXCore::Threads::Thread::CleanupAfterFork();
}
void Context::AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr) {
void ContextImpl::AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr) {
Thread->LookupCache->AddBlockMapping(Address, Ptr);
}
void Context::ClearCodeCache(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::ClearCodeCache(FEXCore::Core::InternalThreadState *Thread) {
FEXCORE_PROFILE_INSTANT("ClearCodeCache");
{
@@ -697,7 +712,7 @@ namespace FEXCore::Context {
static void IRDumper(FEXCore::Core::InternalThreadState *Thread, IR::IREmitter *IREmitter, uint64_t GuestRIP, IR::RegisterAllocationData* RA) {
FILE* f = nullptr;
bool CloseAfter = false;
const auto DumpIRStr = Thread->CTX->Config.DumpIR();
const auto DumpIRStr = static_cast<ContextImpl*>(Thread->CTX)->Config.DumpIR();
// DumpIRStr might be no if not dumping but ShouldDump is set in OpDisp
if (DumpIRStr =="stderr" || DumpIRStr =="no") {
@@ -724,7 +739,7 @@ namespace FEXCore::Context {
}
};
static void ValidateIR(FEXCore::Context::Context *ctx, IR::IREmitter *IREmitter) {
static void ValidateIR(ContextImpl *ctx, IR::IREmitter *IREmitter) {
// Convert to text, Parse, Convert to text again and make sure the texts match
std::stringstream out;
static auto compaction = IR::CreateIRCompaction(ctx->OpDispatcherAllocator);
@@ -748,7 +763,7 @@ namespace FEXCore::Context {
}
}
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
ContextImpl::GenerateIRResult ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
FEXCORE_PROFILE_SCOPED("GenerateIR");
Thread->OpDispatcher->ReownOrClaimBuffer();
@@ -775,7 +790,7 @@ namespace FEXCore::Context {
Thread->FrontendDecoder->DecodeInstructionsAtEntry(GuestCode, GuestRIP, [Thread](uint64_t BlockEntry, uint64_t Start, uint64_t Length) {
if (Thread->LookupCache->AddBlockExecutableRange(BlockEntry, Start, Length)) {
Thread->CTX->SyscallHandler->MarkGuestExecutableRange(Start, Length);
static_cast<ContextImpl*>(Thread->CTX)->SyscallHandler->MarkGuestExecutableRange(Start, Length);
}
});
@@ -795,13 +810,6 @@ namespace FEXCore::Context {
// Reset any block-specific state
Thread->OpDispatcher->StartNewBlock();
if (Config.x86dec_SynchronizeRIPOnAllBlocks) {
// Ensure the RIP is synchronized to the context on block entry.
// In the case of block linking, the RIP may not have synchronized.
auto NewRIP = Thread->OpDispatcher->_EntrypointOffset(Block.Entry - GuestRIP, GPRSize);
Thread->OpDispatcher->_StoreContext(GPRSize, IR::GPRClass, NewRIP, offsetof(FEXCore::Core::CPUState, rip));
}
uint64_t InstsInBlock = Block.NumInstructions;
for (size_t i = 0; i < InstsInBlock; ++i) {
@@ -893,14 +901,14 @@ namespace FEXCore::Context {
IR::IREmitter *IREmitter = Thread->OpDispatcher.get();
auto ShouldDump = Thread->CTX->Config.DumpIR() != "no" || Thread->OpDispatcher->ShouldDump;
auto ShouldDump = static_cast<ContextImpl*>(Thread->CTX)->Config.DumpIR() != "no" || Thread->OpDispatcher->ShouldDump;
// Debug
{
if (ShouldDump) {
IRDumper(Thread, IREmitter, GuestRIP, nullptr);
}
if (Thread->CTX->Config.ValidateIRarser) {
if (static_cast<ContextImpl*>(Thread->CTX)->Config.ValidateIRarser) {
ValidateIR(this, IREmitter);
}
}
@@ -930,7 +938,7 @@ namespace FEXCore::Context {
};
}
Context::CompileCodeResult Context::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
FEXCore::IR::IRListView *IRList {};
FEXCore::Core::DebugData *DebugData {};
FEXCore::IR::RegisterAllocationData::UniquePtr RAData {};
@@ -1002,7 +1010,10 @@ namespace FEXCore::Context {
}
// Attempt to get the CPU backend to compile this code
return {
.CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, IRList, DebugData, RAData.get(), GetGdbServerStatus()),
// FEX currently throws away the CPUBackend::CompiledCode object other than the entrypoint
// In the future with code caching getting wired up, we will pass the rest of the data forward.
// TODO: Pass the data forward when code caching is wired up to this.
.CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, IRList, DebugData, RAData.get(), GetGdbServerStatus()).BlockEntry,
.IRData = IRList,
.DebugData = DebugData,
.RAData = std::move(RAData),
@@ -1012,7 +1023,7 @@ namespace FEXCore::Context {
};
}
void Context::CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
void ContextImpl::CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
auto NewBlock = CompileBlock(Frame, GuestRIP);
if (NewBlock == 0) {
@@ -1023,7 +1034,7 @@ namespace FEXCore::Context {
}
}
uintptr_t Context::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
FEXCORE_PROFILE_SCOPED("CompileBlock");
auto Thread = Frame->Thread;
@@ -1123,7 +1134,7 @@ namespace FEXCore::Context {
return (uintptr_t)CodePtr;
}
void Context::ExecutionThread(FEXCore::Core::InternalThreadState *Thread) {
void ContextImpl::ExecutionThread(FEXCore::Core::InternalThreadState *Thread) {
Core::ThreadData.Thread = Thread;
Thread->ExitReason = FEXCore::Context::ExitReason::EXIT_WAITING;
@@ -1134,7 +1145,7 @@ namespace FEXCore::Context {
// Now notify the thread that we are initialized
Thread->ThreadWaiting.NotifyAll();
if (Thread != Thread->CTX->ParentThread || StartPaused || Thread->StartPaused) {
if (Thread != static_cast<ContextImpl*>(Thread->CTX)->ParentThread || StartPaused || Thread->StartPaused) {
// Parent thread doesn't need to wait to run
Thread->StartRunning.Wait();
}
@@ -1146,7 +1157,7 @@ namespace FEXCore::Context {
Thread->RunningEvents.Running = true;
Thread->CTX->Dispatcher->ExecuteDispatch(Thread->CurrentFrame);
static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->ExecuteDispatch(Thread->CurrentFrame);
Thread->RunningEvents.Running = false;
}
@@ -1175,7 +1186,7 @@ namespace FEXCore::Context {
SignalDelegation->UninstallTLSState(Thread);
// If the parent thread is waiting to join, then we can't destroy our thread object
if (!Thread->DestroyedByParent && Thread != Thread->CTX->ParentThread) {
if (!Thread->DestroyedByParent && Thread != static_cast<ContextImpl*>(Thread->CTX)->ParentThread) {
Thread->CTX->DestroyThread(Thread);
}
}
@@ -1188,34 +1199,34 @@ namespace FEXCore::Context {
for (auto it = lower; it != upper; it++) {
for (auto Address: it->second) {
Context::ThreadRemoveCodeEntry(Thread, Address);
ContextImpl::ThreadRemoveCodeEntry(Thread, Address);
}
it->second.clear();
}
}
static void InvalidateGuestCodeRangeInternal(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
std::lock_guard lk(CTX->ThreadCreationMutex);
static void InvalidateGuestCodeRangeInternal(ContextImpl *CTX, uint64_t Start, uint64_t Length) {
std::lock_guard lk(static_cast<ContextImpl*>(CTX)->ThreadCreationMutex);
for (auto &Thread : CTX->Threads) {
for (auto &Thread : static_cast<ContextImpl*>(CTX)->Threads) {
InvalidateGuestThreadCodeRange(Thread, Start, Length);
}
}
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
void ContextImpl::InvalidateGuestCodeRange(uint64_t Start, uint64_t Length) {
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CodeInvalidationMutex);
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
InvalidateGuestCodeRangeInternal(this, Start, Length);
}
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> CallAfter) {
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
void ContextImpl::InvalidateGuestCodeRange(uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> CallAfter) {
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CodeInvalidationMutex);
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
InvalidateGuestCodeRangeInternal(this, Start, Length);
CallAfter(Start, Length);
}
void Context::MarkMemoryShared() {
void ContextImpl::MarkMemoryShared() {
if (!IsMemoryShared) {
IsMemoryShared = true;
@@ -1235,18 +1246,14 @@ namespace FEXCore::Context {
}
}
void MarkMemoryShared(FEXCore::Context::Context *CTX) {
CTX->MarkMemoryShared();
}
void Context::ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker) {
std::shared_lock lk(Thread->CTX->CodeInvalidationMutex);
void ContextImpl::ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker) {
std::shared_lock lk(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex);
Thread->LookupCache->AddBlockLink(GuestDestination, HostLink, delinker);
}
void Context::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
LogMan::Throw::AFmt(Thread->CTX->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
void ContextImpl::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
LogMan::Throw::AFmt(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
std::lock_guard<std::recursive_mutex> lk(Thread->LookupCache->WriteLock);
@@ -1254,7 +1261,7 @@ namespace FEXCore::Context {
Thread->LookupCache->Erase(GuestRIP);
}
CustomIRResult Context::AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data) {
CustomIRResult ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data) {
LOGMAN_THROW_A_FMT(Config.Is64BitMode || !(Entrypoint >> 32), "64-bit Entrypoint in 32-bit mode {:x}", Entrypoint);
std::unique_lock lk(CustomIRMutex);
@@ -1270,12 +1277,12 @@ namespace FEXCore::Context {
}
}
void Context::RemoveCustomIREntrypoint(uintptr_t Entrypoint) {
void ContextImpl::RemoveCustomIREntrypoint(uintptr_t Entrypoint) {
LOGMAN_THROW_A_FMT(Config.Is64BitMode || !(Entrypoint >> 32), "64-bit Entrypoint in 32-bit mode {:x}", Entrypoint);
std::scoped_lock lk(CustomIRMutex);
InvalidateGuestCodeRange(this, Entrypoint, 1, [this](uint64_t Entrypoint, uint64_t) {
InvalidateGuestCodeRange(Entrypoint, 1, [this](uint64_t Entrypoint, uint64_t) {
CustomIRHandlers.erase(Entrypoint);
});
}
@@ -1285,24 +1292,26 @@ namespace FEXCore::Context {
uint64_t RIPBackup = Thread->CurrentFrame->State.rip;
Thread->CurrentFrame->State.rip = RIP;
auto CTX = static_cast<ContextImpl*>(Thread->CTX);
// Erase the RIP from all the storage backings if it exists
ThreadRemoveCodeEntry(Thread, RIP);
CTX->ThreadRemoveCodeEntry(Thread, RIP);
// We don't care if compilation passes or not
CompileBlock(Thread->CurrentFrame, RIP);
CTX->CompileBlock(Thread->CurrentFrame, RIP);
Thread->CurrentFrame->State.rip = RIPBackup;
}
uint64_t Context::GetThreadCount() const {
uint64_t ContextImpl::GetThreadCount() const {
return Threads.size();
}
FEXCore::Core::RuntimeStats *Context::GetRuntimeStatsForThread(uint64_t Thread) {
FEXCore::Core::RuntimeStats *ContextImpl::GetRuntimeStatsForThread(uint64_t Thread) {
return &Threads[Thread]->Stats;
}
bool Context::GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data) {
bool ContextImpl::GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data) {
std::lock_guard<std::recursive_mutex> lk(ParentThread->LookupCache->WriteLock);
auto it = ParentThread->DebugStore.find(RIP);
if (it == ParentThread->DebugStore.end()) {
@@ -1313,7 +1322,7 @@ namespace FEXCore::Context {
return true;
}
bool Context::FindHostCodeForRIP(uint64_t RIP, uint8_t **Code) {
bool ContextImpl::FindHostCodeForRIP(uint64_t RIP, uint8_t **Code) {
uintptr_t HostCode = ParentThread->LookupCache->FindBlock(RIP);
if (!HostCode) {
return false;
@@ -1329,7 +1338,7 @@ namespace FEXCore::Context {
return Result;
}
IR::AOTIRCacheEntry *Context::LoadAOTIRCacheEntry(const std::string &filename) {
IR::AOTIRCacheEntry *ContextImpl::LoadAOTIRCacheEntry(const std::string &filename) {
auto rv = IRCaptureCache.LoadAOTIRCacheEntry(filename);
if (DebugServer) {
DebugServer->AlertLibrariesChanged();
@@ -1337,19 +1346,18 @@ namespace FEXCore::Context {
return rv;
}
void Context::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry *Entry) {
void ContextImpl::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry *Entry) {
IRCaptureCache.UnloadAOTIRCacheEntry(Entry);
if (DebugServer) {
DebugServer->AlertLibrariesChanged();
}
}
void Context::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
void ContextImpl::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
ThunkHandler->AppendThunkDefinitions(Definitions);
}
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
void ContextImpl::ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
}
+3 -3
View File
@@ -5,7 +5,7 @@ namespace FEXCore {
}
namespace FEXCore::Context {
struct Context;
class ContextImpl;
}
namespace FEXCore::CPU {
@@ -17,7 +17,7 @@ namespace FEXCore::CPU {
*
* @return true if core was able to be create
*/
bool CreateCPUCore(FEXCore::Context::Context *CTX);
bool CreateCPUCore(FEXCore::Context::ContextImpl *CTX);
bool LoadCode(FEXCore::Context::Context *CTX, FEXCore::CodeLoader *Loader);
bool LoadCode(FEXCore::Context::ContextImpl *CTX, FEXCore::CodeLoader *Loader);
}
@@ -35,7 +35,7 @@ namespace FEXCore::CPU {
constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config)
: FEXCore::CPU::Dispatcher(ctx, config), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE)
#ifdef VIXL_SIMULATOR
, Simulator {&Decoder}
@@ -578,10 +578,10 @@ size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t Gues
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
// This happens when single stepping
static_assert(sizeof(FEXCore::Context::Context::Config.RunningMode) == 4, "This is expected to be size of 4");
static_assert(sizeof(FEXCore::Context::ContextImpl::Config.RunningMode) == 4, "This is expected to be size of 4");
emit.ldr(ARMEmitter::XReg::x0, STATE_PTR(CpuStateFrame, Thread));
emit.ldr(ARMEmitter::XReg::x0, ARMEmitter::Reg::r0, offsetof(FEXCore::Core::InternalThreadState, CTX)); // Get Context
emit.ldr(ARMEmitter::WReg::w0, ARMEmitter::Reg::r0, offsetof(FEXCore::Context::Context, Config.RunningMode));
emit.ldr(ARMEmitter::WReg::w0, ARMEmitter::Reg::r0, offsetof(FEXCore::Context::ContextImpl, Config.RunningMode));
// If the value == 0 then we don't need to stop
emit.cbz(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, &RunBlock);
@@ -672,7 +672,7 @@ void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thr
}
}
std::unique_ptr<Dispatcher> Dispatcher::CreateArm64(FEXCore::Context::Context *CTX, const DispatcherConfig &Config) {
std::unique_ptr<Dispatcher> Dispatcher::CreateArm64(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config) {
return std::make_unique<Arm64Dispatcher>(CTX, Config);
}
@@ -7,10 +7,6 @@
#include <aarch64/simulator-aarch64.h>
#endif
namespace FEXCore::Context {
struct Context;
}
namespace FEXCore::Core {
struct InternalThreadState;
}
@@ -22,7 +18,7 @@ namespace FEXCore::CPU {
class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
public:
Arm64Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config);
Arm64Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config);
void InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) override;
size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) override;
size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) override;
@@ -22,7 +22,7 @@
namespace FEXCore::CPU {
void Dispatcher::SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame) {
void Dispatcher::SleepThread(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::CpuStateFrame *Frame) {
auto Thread = Frame->Thread;
--ctx->IdleWaitRefCount;
@@ -40,6 +40,27 @@ void Dispatcher::SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuS
ctx->IdleWaitCV.notify_all();
}
uint64_t Dispatcher::ReconstructRIPFromContext(FEXCore::Core::CpuStateFrame *Frame, void *ucontext) const {
const uint64_t HostPC = ArchHelpers::Context::GetPc(ucontext);
const uint64_t BlockBegin = Frame->State.InlineJITBlockHeader;
const CPUBackend::JITCodeHeader *InlineHeader = reinterpret_cast<const CPUBackend::JITCodeHeader *>(BlockBegin);
if (InlineHeader) {
const CPUBackend::JITCodeTail *InlineTail = reinterpret_cast<const CPUBackend::JITCodeTail *>(Frame->State.InlineJITBlockHeader + InlineHeader->OffsetToBlockTail);
// Check if the host PC is currently within a code block.
// If it is then RIP can be reconstructed from the beginning of the code block.
// This is currently as close as FEX can get RIP reconstructions.
if (HostPC >= reinterpret_cast<uint64_t>(BlockBegin) &&
HostPC < reinterpret_cast<uint64_t>(BlockBegin + InlineTail->Size)) {
return InlineTail->RIP;
}
}
// Fallback to what is stored in the RIP currently.
return Frame->State.rip;
}
ArchHelpers::Context::ContextBackup* Dispatcher::StoreThreadState(FEXCore::Core::InternalThreadState *Thread, int Signal, void *ucontext) {
// We can end up getting a signal at any point in our host state
// Jump to a handler that saves all state so we can safely return
@@ -70,12 +91,6 @@ ArchHelpers::Context::ContextBackup* Dispatcher::StoreThreadState(FEXCore::Core:
// Set the new SP
ArchHelpers::Context::SetSp(ucontext, NewSP);
// Signal frames are only used on the interpreter
// The JITS require the stack to be setup correctly on rt_sigreturn
if (CTX->Config.Core() == FEXCore::Config::CONFIG_INTERPRETER) {
SignalFrames.push(NewSP);
}
Context->Flags = 0;
Context->FPStateLocation = 0;
Context->UContextLocation = 0;
@@ -255,25 +270,93 @@ void Dispatcher::RestoreRTFrame_ia32(ArchHelpers::Context::ContextBackup* Contex
}
void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread, void *ucontext, RestoreType Type) {
// Pulling from context here
const bool Is64BitMode = CTX->Config.Is64BitMode;
const bool IsAVXEnabled = CTX->Config.EnableAVX;
uint64_t OldSP{};
if (CTX->Config.Core() == FEXCore::Config::CONFIG_IRJIT) {
if (Type == RestoreType::TYPE_PAUSE) [[unlikely]] {
OldSP = ArchHelpers::Context::GetSp(ucontext);
}
else {
LOGMAN_THROW_A_FMT(!SignalFrames.empty(), "Trying to restore a signal frame when we don't have any");
OldSP = SignalFrames.top();
SignalFrames.pop();
// Some fun introspection here.
// We store a pointer to our host-stack on the guest stack.
// We need to inspect the guest state coming in, so we can get our host stack back.
uint64_t GuestSP = Thread->CurrentFrame->State.gregs[X86State::REG_RSP];
if (Is64BitMode) {
// Signal frame layout on stack needs to be as follows
// void* ReturnPointer
// ucontext_t
// siginfo_t
// FP state
// Host stack location
GuestSP += sizeof(FEXCore::x86_64::ucontext_t);
GuestSP = AlignUp(GuestSP, alignof(FEXCore::x86_64::ucontext_t));
GuestSP += sizeof(siginfo_t);
GuestSP = AlignUp(GuestSP, alignof(siginfo_t));
if (IsAVXEnabled) {
GuestSP += sizeof(x86_64::xstate);
GuestSP = AlignUp(GuestSP, alignof(x86_64::xstate));
} else {
GuestSP += sizeof(x86_64::_libc_fpstate);
GuestSP = AlignUp(GuestSP, alignof(x86_64::_libc_fpstate));
}
}
else {
if (Type == RestoreType::TYPE_NONREALTIME) {
// Signal frame layout on stack needs to be as follows
// SigFrame_i32
// FPState
// Host stack location
// Remove the 4-byte pretcode /AND/ a legacy argument that is ignored.
GuestSP += sizeof(SigFrame_i32) - 8;
GuestSP = AlignUp(GuestSP, alignof(SigFrame_i32));
if (IsAVXEnabled) {
GuestSP += sizeof(x86::xstate);
GuestSP = AlignUp(GuestSP, alignof(x86::xstate));
} else {
GuestSP += sizeof(x86::_libc_fpstate);
GuestSP = AlignUp(GuestSP, alignof(x86::_libc_fpstate));
}
}
else {
// Signal frame layout on stack needs to be as follows
// RTSigFrame_i32
// FPState
// Host stack location
// Remove the 4-byte pretcode.
GuestSP += sizeof(RTSigFrame_i32) - 4;
GuestSP = AlignUp(GuestSP, alignof(RTSigFrame_i32));
if (IsAVXEnabled) {
GuestSP += sizeof(x86::xstate);
GuestSP = AlignUp(GuestSP, alignof(x86::xstate));
} else {
GuestSP += sizeof(x86::_libc_fpstate);
GuestSP = AlignUp(GuestSP, alignof(x86::_libc_fpstate));
}
}
}
OldSP = *reinterpret_cast<uint64_t*>(GuestSP);
}
uintptr_t NewSP = OldSP;
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
// First thing, reset the guest state
memcpy(Thread->CurrentFrame, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
// Now restore host state
// Restore host state
ArchHelpers::Context::RestoreContext(ucontext, Context);
// Reset the guest state
memcpy(Thread->CurrentFrame, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
if (Context->UContextLocation) {
auto Frame = Thread->CurrentFrame;
@@ -283,7 +366,7 @@ void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread,
// We can't currently support this since it might result in tearing without real state reconstruction
}
if (!(Context->Flags & ArchHelpers::Context::ContextFlags::CONTEXT_FLAG_32BIT)) {
if (Is64BitMode) {
RestoreFrame_x64(Context, Frame, ucontext);
}
else {
@@ -354,9 +437,12 @@ uint64_t Dispatcher::SetupFrame_ia32(
uint64_t NewGuestSP, const uint32_t eflags) {
const bool IsAVXEnabled = CTX->Config.EnableAVX;
const uint64_t SignalReturn = CTX->X86CodeGen.SignalReturn;
const uint64_t SignalReturn = reinterpret_cast<uint64_t>(CTX->VDSOPointers.VDSO_kernel_sigreturn);
ContextBackup->Flags |= ArchHelpers::Context::ContextFlags::CONTEXT_FLAG_32BIT;
NewGuestSP -= sizeof(uint64_t);
NewGuestSP = AlignDown(NewGuestSP, alignof(uint64_t));
uint64_t HostStackLocation = NewGuestSP;
if (IsAVXEnabled) {
NewGuestSP -= sizeof(x86::xstate);
@@ -377,6 +463,8 @@ uint64_t Dispatcher::SetupFrame_ia32(
ContextBackup->SigInfoLocation = 0;
SigFrame_i32 *guest_uctx = reinterpret_cast<SigFrame_i32*>(SigFrameLocation);
// Store where the host context lives in the guest stack.
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
// Pointer to where the fpreg memory is
guest_uctx->sc.fpstate = static_cast<uint32_t>(FPStateLocation);
@@ -400,7 +488,7 @@ uint64_t Dispatcher::SetupFrame_ia32(
guest_uctx->sc.err = ConvertSignalToError(ucontext, Signal, HostSigInfo);
}
guest_uctx->sc.ip = Frame->State.rip;
guest_uctx->sc.ip = ContextBackup->OriginalRIP;
guest_uctx->sc.flags = eflags;
guest_uctx->sc.sp_at_signal = 0;
@@ -453,9 +541,21 @@ uint64_t Dispatcher::SetupFrame_ia32(
// Copy over the signal information.
guest_uctx->Signal = Signal;
// Retcode needs to be bit-exact for debuggers
constexpr static uint8_t retcode[] = {
0x58, // pop eax
0xb8, // mov
0x77, 0x00, 0x00, 0x00, // 32-bit sigreturn
0xcd, 0x80, // int 0x80
};
memcpy(guest_uctx->retcode, &retcode, sizeof(retcode));
// 32-bit Guest can provide its own restorer or we need to provide our own.
// On a real host this restorer will live in VDSO.
if (GuestAction->restorer && incomplete_guest_restorer_support) {
constexpr uint32_t SA_RESTORER = 0x04000000;
const bool HasRestorer = (GuestAction->sa_flags & SA_RESTORER) == SA_RESTORER;
if (HasRestorer) {
// TODO: Support guest restorer
guest_uctx->pretcode = (uint32_t)(uint64_t)GuestAction->restorer;
}
@@ -479,9 +579,12 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
uint64_t NewGuestSP, const uint32_t eflags) {
const bool IsAVXEnabled = CTX->Config.EnableAVX;
const uint64_t SignalReturn = CTX->X86CodeGen.SignalReturnRT;
const uint64_t SignalReturn = reinterpret_cast<uint64_t>(CTX->VDSOPointers.VDSO_kernel_rt_sigreturn);
ContextBackup->Flags |= ArchHelpers::Context::ContextFlags::CONTEXT_FLAG_32BIT;
NewGuestSP -= sizeof(uint64_t);
NewGuestSP = AlignDown(NewGuestSP, alignof(uint64_t));
uint64_t HostStackLocation = NewGuestSP;
if (IsAVXEnabled) {
NewGuestSP -= sizeof(x86::xstate);
@@ -495,8 +598,11 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
NewGuestSP -= sizeof(RTSigFrame_i32);
NewGuestSP = AlignDown(NewGuestSP, alignof(RTSigFrame_i32));
uint64_t SigFrameLocation = NewGuestSP;
RTSigFrame_i32 *guest_uctx = reinterpret_cast<RTSigFrame_i32*>(SigFrameLocation);
// Store where the host context lives in the guest stack.
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
ContextBackup->FPStateLocation = FPStateLocation;
ContextBackup->UContextLocation = SigFrameLocation;
@@ -504,6 +610,7 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
// We have extended float information
guest_uctx->uc.uc_flags = FEXCore::x86::UC_FP_XSTATE;
guest_uctx->uc.uc_link = 0;
// Pointer to where the fpreg memory is
guest_uctx->uc.uc_mcontext.fpregs = static_cast<uint32_t>(FPStateLocation);
@@ -528,9 +635,10 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR] = ConvertSignalToError(ucontext, Signal, HostSigInfo);
}
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP] = Frame->State.rip;
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP] = ContextBackup->OriginalRIP;
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EFL] = eflags;
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_UESP] = 0;
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_UESP] = Frame->State.gregs[X86State::REG_RSP];
guest_uctx->uc.uc_mcontext.cr2 = 0;
#define COPY_REG(x) \
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_##x] = Frame->State.gregs[X86State::REG_##x];
@@ -576,7 +684,6 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
(Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14);
// Copy over signal stack information
guest_uctx->uc.uc_stack.ss_flags = GuestStack->ss_flags;
guest_uctx->uc.uc_stack.ss_sp = static_cast<uint32_t>(reinterpret_cast<uint64_t>(GuestStack->ss_sp));
@@ -605,7 +712,7 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
case SIGILL:
// Macro expansion to get the si_addr
// Can't really give a real result here. Pull from the context for now
guest_uctx->info._sifields._sigfault.addr = Frame->State.rip;
guest_uctx->info._sifields._sigfault.addr = ContextBackup->OriginalRIP;
break;
case SIGCHLD:
guest_uctx->info._sifields._sigchld.pid = HostSigInfo->si_pid;
@@ -630,9 +737,21 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
guest_uctx->pinfo = (uint32_t)(uint64_t)&guest_uctx->info;
guest_uctx->puc = (uint32_t)(uint64_t)&guest_uctx->uc;
// Retcode needs to be bit-exact for debuggers
constexpr static uint8_t rt_retcode[] = {
0xb8, // mov
0xad, 0x00, 0x00, 0x00, // 32-bit rt_sigreturn
0xcd, 0x80, // int 0x80
0x0, // Pad
};
memcpy(guest_uctx->retcode, &rt_retcode, sizeof(rt_retcode));
// 32-bit Guest can provide its own restorer or we need to provide our own.
// On a real host this restorer will live in VDSO.
if (GuestAction->restorer && incomplete_guest_restorer_support) {
constexpr uint32_t SA_RESTORER = 0x04000000;
const bool HasRestorer = (GuestAction->sa_flags & SA_RESTORER) == SA_RESTORER;
if (HasRestorer) {
// TODO: Support guest restorer
guest_uctx->pretcode = (uint32_t)(uint64_t)GuestAction->restorer;
}
@@ -734,7 +853,6 @@ uint64_t Dispatcher::SetupFrame_x64(
NewGuestSP -= 128;
const bool IsAVXEnabled = CTX->Config.EnableAVX;
const uint64_t SignalReturn = CTX->X86CodeGen.SignalReturn;
// On 64-bit the kernel sets up the siginfo_t and ucontext_t regardless of SA_SIGINFO set.
// This allows the application to /always/ get the siginfo and ucontext even if it didn't set this flag.
@@ -744,6 +862,12 @@ uint64_t Dispatcher::SetupFrame_x64(
// ucontext_t
// siginfo_t
// FP state
// Host stack location
NewGuestSP -= sizeof(uint64_t);
NewGuestSP = AlignDown(NewGuestSP, alignof(uint64_t));
uint64_t HostStackLocation = NewGuestSP;
if (IsAVXEnabled) {
NewGuestSP -= sizeof(x86_64::xstate);
NewGuestSP = AlignDown(NewGuestSP, alignof(x86_64::xstate));
@@ -768,6 +892,8 @@ uint64_t Dispatcher::SetupFrame_x64(
FEXCore::x86_64::ucontext_t *guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
siginfo_t *guest_siginfo = reinterpret_cast<siginfo_t*>(SigInfoLocation);
// Store where the host context lives in the guest stack.
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
// We have extended float information
guest_uctx->uc_flags = FEXCore::x86_64::UC_FP_XSTATE |
@@ -779,7 +905,7 @@ uint64_t Dispatcher::SetupFrame_x64(
auto *xstate = reinterpret_cast<x86_64::xstate*>(FPStateLocation);
SetXStateInfo(xstate, IsAVXEnabled);
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP] = Frame->State.rip;
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP] = ContextBackup->OriginalRIP;
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_EFL] = eflags;
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_CSGSFS] = 0;
@@ -866,14 +992,14 @@ uint64_t Dispatcher::SetupFrame_x64(
// The host is required to provide us a restorer.
// If the guest didn't provide a restorer then the application should fail with a SIGSEGV.
// TODO: Emulate SIGSEGV when the guest doesn't provide a restorer.
// TODO: Support the guest's restorer. Required for libunwind.
NewGuestSP -= 8;
if (incomplete_guest_restorer_support) {
if (GuestAction->restorer) {
// TODO: Once we support the guest's restorer.
*(uint64_t*)NewGuestSP = (uint64_t)GuestAction->restorer;
}
else {
*(uint64_t*)NewGuestSP = SignalReturn;
// XXX: Emulate SIGSEGV here
// *(uint64_t*)NewGuestSP = SignalReturn;
}
return NewGuestSP;
@@ -889,14 +1015,6 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
++Thread->CurrentFrame->SignalHandlerRefCounter;
uint64_t OldPC = ArchHelpers::Context::GetPc(ucontext);
// Set the new PC
ArchHelpers::Context::SetPc(ucontext, AbsoluteLoopTopAddressFillSRA);
// Set our state register to point to our guest thread data
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
uint64_t OldGuestSP = Frame->State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
// Pulling from context here
const bool Is64BitMode = CTX->Config.Is64BitMode;
@@ -912,14 +1030,7 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
// We need to spill SRA but only some of it, since some values have already been spilled
// Lower 16 bits tells us which registers are already spilled to the context
// So we ignore spilling those ones
uint16_t NumRegisters = std::popcount(Frame->InSyscallInfo & 0xFFFF);
if (NumRegisters >= 4) {
// Unhandled case
IgnoreMask = 0;
}
else {
IgnoreMask = Frame->InSyscallInfo & 0xFFFF;
}
IgnoreMask = Frame->InSyscallInfo & 0xFFFF;
}
else {
// We must spill everything
@@ -945,6 +1056,9 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
}
}
uint64_t OldGuestSP = Frame->State.gregs[X86State::REG_RSP];
uint64_t NewGuestSP = OldGuestSP;
// altstack is only used if the signal handler was setup with SA_ONSTACK
if (GuestAction->sa_flags & SA_ONSTACK) {
// Additionally the altstack is only used if the enabled (SS_DISABLE flag is not set)
@@ -967,7 +1081,7 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
siginfo_t *HostSigInfo = reinterpret_cast<siginfo_t*>(info);
// Backup where we think the RIP currently is
ContextBackup->OriginalRIP = Frame->State.rip;
ContextBackup->OriginalRIP = ReconstructRIPFromContext(Frame, ucontext);
// Calculate eflags upfront.
uint32_t eflags = 0;
for (size_t i = 0; i < Core::CPUState::NUM_EFLAG_BITS; ++i) {
@@ -998,6 +1112,11 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
Frame->State.FCW = 0x37F;
Frame->State.FTW = 0xFFFF;
// Set the new PC
ArchHelpers::Context::SetPc(ucontext, AbsoluteLoopTopAddressFillSRA);
// Set our state register to point to our guest thread data
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
return true;
}
@@ -1085,7 +1204,7 @@ bool Dispatcher::HandleSignalPause(FEXCore::Core::InternalThreadState *Thread, i
if (Thread->RunningEvents.ThreadSleeping) {
// If the thread was sleeping then its idle counter was decremented
// Reincrement it here to not break logic
++Thread->CTX->IdleWaitRefCount;
++static_cast<Context::ContextImpl*>(Thread->CTX)->IdleWaitRefCount;
}
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
@@ -1107,14 +1226,14 @@ bool Dispatcher::HandleSignalPause(FEXCore::Core::InternalThreadState *Thread, i
}
uint64_t Dispatcher::GetCompileBlockPtr() {
using ClassPtrType = void (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
using ClassPtrType = void (FEXCore::Context::ContextImpl::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast CompileBlockPtr;
CompileBlockPtr.ClassPtr = &FEXCore::Context::Context::CompileBlockJit;
CompileBlockPtr.ClassPtr = &FEXCore::Context::ContextImpl::CompileBlockJit;
return CompileBlockPtr.Data;
}
@@ -20,7 +20,7 @@ struct InternalThreadState;
}
namespace FEXCore::Context {
struct Context;
class ContextImpl;
}
namespace FEXCore::CPU {
@@ -74,8 +74,8 @@ public:
virtual size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) = 0;
virtual size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) = 0;
static std::unique_ptr<Dispatcher> CreateX86(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
static std::unique_ptr<Dispatcher> CreateArm64(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
static std::unique_ptr<Dispatcher> CreateX86(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config);
static std::unique_ptr<Dispatcher> CreateArm64(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config);
virtual void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
DispatchPtr(Frame);
@@ -86,17 +86,16 @@ public:
}
protected:
Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &Config)
Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &Config)
: CTX {ctx}
, config {Config}
{}
uint64_t ReconstructRIPFromContext(FEXCore::Core::CpuStateFrame *Frame, void *ucontext) const;
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,
@@ -160,10 +159,10 @@ protected:
virtual void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) {}
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
DispatcherConfig config;
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame);
static void SleepThread(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::CpuStateFrame *Frame);
static uint64_t GetCompileBlockPtr();
@@ -27,7 +27,7 @@ namespace FEXCore::CPU {
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE r14
X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
X86Dispatcher::X86Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config)
: Dispatcher(ctx, config)
, Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE,
FEXCore::Allocator::mmap(nullptr, MAX_DISPATCHER_CODE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
@@ -433,7 +433,7 @@ size_t X86Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestR
emit.mov(rax, reinterpret_cast<uint64_t>(CTX));
// If the value == 0 then we don't need to stop
emit.cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
emit.cmp(dword [rax + (offsetof(FEXCore::Context::ContextImpl, Config.RunningMode))], 0);
emit.je(RunBlock);
{
// Make sure RIP is syncronized to the context
@@ -499,7 +499,7 @@ void X86Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Threa
}
}
std::unique_ptr<Dispatcher> Dispatcher::CreateX86(FEXCore::Context::Context *CTX, const DispatcherConfig &Config) {
std::unique_ptr<Dispatcher> Dispatcher::CreateX86(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config) {
return std::make_unique<X86Dispatcher>(CTX, Config);
}
@@ -5,10 +5,6 @@
#define XBYAK64
#include <xbyak/xbyak.h>
namespace FEXCore::Context {
struct Context;
}
namespace FEXCore::Core {
struct InternalThreadState;
}
@@ -17,7 +13,7 @@ namespace FEXCore::CPU {
class X86Dispatcher final : public Dispatcher, public Xbyak::CodeGenerator {
public:
X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config);
X86Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config);
void InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) override;
size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) override;
size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) override;
+4 -5
View File
@@ -79,7 +79,7 @@ static uint32_t MapVEXToReg(uint8_t vvvv, bool HasXMM) {
}
}
Decoder::Decoder(FEXCore::Context::Context *ctx)
Decoder::Decoder(FEXCore::Context::ContextImpl *ctx)
: CTX {ctx}
, OSABI { ctx->SyscallHandler ? ctx->SyscallHandler->GetOSABI() : FEXCore::HLE::SyscallOSABI::OS_UNKNOWN }
, PoolObject {ctx->FrontendAllocator, sizeof(FEXCore::X86Tables::DecodedInst) * DefaultDecodedBufferSize} {
@@ -321,7 +321,6 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
const bool HasMODRM = !!(Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM);
const bool HasREX = !!(DecodeInst->Flags & DecodeFlags::FLAG_REX_PREFIX);
const bool HasHighXMM = HAS_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_HIGH_XMM_REG);
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
@@ -444,7 +443,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
// ADDITIONALLY:
// If there is a REX prefix then that allows extended GPR usage
CurrentDest->Type = DecodedOperand::OpType::GPR;
DecodeInst->Dest.Data.GPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM;
DecodeInst->Dest.Data.GPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100);
CurrentDest->Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, Op & 0b111, Is8BitDest, HasREX, false, false);
if (CurrentDest->Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
@@ -472,7 +471,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
// Decode the GPR source first
GPR.Type = DecodedOperand::OpType::GPR;
GPR.Data.GPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM;
GPR.Data.GPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX);
GPR.Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_R ? 1 : 0, ModRM.reg, GPR8Bit, HasREX, HasXMMGPR, HasMMGPR);
if (GPR.Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
@@ -482,7 +481,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
// ModRM.Mod != 0b11 == Register-direct addressing
if (ModRM.mod == 0b11) {
NonGPR.Type = DecodedOperand::OpType::GPR;
NonGPR.Data.GPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM;
NonGPR.Data.GPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX);
NonGPR.Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, NonGPR8Bit, HasREX, HasXMMNonGPR, HasMMNonGPR);
if (NonGPR.Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
return false;
+3 -3
View File
@@ -11,7 +11,7 @@
#include <vector>
namespace FEXCore::Context {
struct Context;
class ContextImpl;
}
namespace FEXCore::Frontend {
@@ -25,7 +25,7 @@ public:
bool HasInvalidInstruction{};
};
Decoder(FEXCore::Context::Context *ctx);
Decoder(FEXCore::Context::ContextImpl *ctx);
~Decoder();
void DecodeInstructionsAtEntry(uint8_t const* InstStream, uint64_t PC, std::function<void(uint64_t BlockEntry, uint64_t Start, uint64_t Length)> AddContainedCodePage);
@@ -52,7 +52,7 @@ private:
bool L; // VEX.L bit (if set then 256 bit operation, if unset then scalar or 128-bit operation)
};
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
const FEXCore::HLE::SyscallOSABI OSABI{};
bool DecodeInstruction(uint64_t PC);
+2 -2
View File
@@ -68,11 +68,11 @@ void GdbServer::WaitForThreadWakeup() {
ThreadBreakEvent.Wait();
}
GdbServer::GdbServer(FEXCore::Context::Context *ctx) : CTX(ctx) {
GdbServer::GdbServer(FEXCore::Context::ContextImpl *ctx) : CTX(ctx) {
// Pass all signals by default
std::fill(PassSignals.begin(), PassSignals.end(), true);
Context::SetExitHandler(ctx, [this](uint64_t ThreadId, FEXCore::Context::ExitReason ExitReason) {
ctx->SetExitHandler([this](uint64_t ThreadId, FEXCore::Context::ExitReason ExitReason) {
if (ExitReason == FEXCore::Context::ExitReason::EXIT_DEBUG) {
this->Break(SIGTRAP);
}
+3 -3
View File
@@ -19,12 +19,12 @@ $end_info$
namespace FEXCore {
namespace Context {
struct Context;
class ContextImpl;
}
class GdbServer {
public:
GdbServer(FEXCore::Context::Context *ctx);
GdbServer(FEXCore::Context::ContextImpl *ctx);
// Public for threading
void GdbServerLoop();
@@ -75,7 +75,7 @@ private:
std::string readRegs();
HandledPacketType readReg(const std::string& packet);
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
std::unique_ptr<FEXCore::Threads::Thread> gdbServerThread;
std::unique_ptr<std::iostream> CommsStream;
std::mutex sendMutex;
@@ -17,22 +17,8 @@ $end_info$
#include <unistd.h>
namespace FEXCore::CPU {
[[noreturn]]
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;
}
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(SignalReturn) {
auto Op = IROp->C<IR::IROp_SignalReturn>();
SignalReturn(Data->State, Op->IsRT);
}
DEF_OP(CallbackReturn) {
Data->State->CurrentFrame->Pointers.Interpreter.CallbackReturn(Data->State, Data->StackEntry);
}
@@ -93,7 +79,7 @@ DEF_OP(Syscall) {
Args.Argument[j] = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[j]);
}
uint64_t Res = FEXCore::Context::HandleSyscall(Data->State->CTX->SyscallHandler, Data->State->CurrentFrame, &Args);
uint64_t Res = FEXCore::Context::HandleSyscall(static_cast<Context::ContextImpl*>(Data->State->CTX)->SyscallHandler, Data->State->CurrentFrame, &Args);
GD = Res;
}
@@ -128,7 +114,7 @@ DEF_OP(InlineSyscall) {
DEF_OP(Thunk) {
auto Op = IROp->C<IR::IROp_Thunk>();
auto thunkFn = Data->State->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
auto thunkFn = static_cast<Context::ContextImpl*>(Data->State->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
thunkFn(*GetSrc<void**>(Data->SSAData, Op->ArgPtr));
}
@@ -144,7 +130,7 @@ DEF_OP(ValidateCode) {
}
DEF_OP(ThreadRemoveCodeEntry) {
Data->State->CTX->ThreadRemoveCodeEntryFromJit(Data->State->CurrentFrame, Data->CurrentEntry);
static_cast<Context::ContextImpl*>(Data->State->CTX)->ThreadRemoveCodeEntryFromJit(Data->State->CurrentFrame, Data->CurrentEntry);
}
DEF_OP(CPUID) {
@@ -153,7 +139,7 @@ DEF_OP(CPUID) {
const uint64_t Arg = *GetSrc<uint64_t*>(Data->SSAData, Op->Function);
const uint64_t Leaf = *GetSrc<uint64_t*>(Data->SSAData, Op->Leaf);
auto Results = Data->State->CTX->CPUID.RunFunction(Arg, Leaf);
auto Results = Data->State->CTX->RunCPUIDFunction(Arg, Leaf);
memcpy(DstPtr, &Results, sizeof(uint32_t) * 4);
}
@@ -62,6 +62,23 @@ DEF_OP(VCastFromGPR) {
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Src), Op->Header.ElementSize);
}
DEF_OP(VDupFromGPR) {
const auto Op = IROp->C<IR::IROp_VDupFromGPR>();
const auto OpSize = IROp->Size;
const auto ElementSize = IROp->ElementSize;
const auto NumElements = OpSize / IROp->ElementSize;
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
const auto *Src = GetSrc<void*>(Data->SSAData, Op->Src);
for (size_t i = 0; i < NumElements; i++) {
memcpy(Tmp + (i * ElementSize), Src, ElementSize);
}
memcpy(GDP, Tmp, sizeof(Tmp));
}
DEF_OP(Float_FromGPR_S) {
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
@@ -26,7 +26,7 @@ public:
[[nodiscard]] std::string GetName() override { return "Interpreter"; }
[[nodiscard]] void *CompileCode(uint64_t Entry,
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
@@ -35,7 +35,7 @@ public:
[[nodiscard]] bool NeedsOpDispatch() override { return true; }
static void InitializeSignalHandlers(FEXCore::Context::Context *CTX);
static void InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX);
void ClearCache() override;
@@ -49,7 +49,10 @@ InterpreterCore::InterpreterCore(Dispatcher *Dispatcher, FEXCore::Core::Internal
ClearCache();
}
void InterpreterCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
void InterpreterCore::InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
return reinterpret_cast<Context::ContextImpl*>(Thread->CTX)->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
}, true);
#ifdef _M_ARM_64
CTX->SignalDelegation->RegisterHostSignalHandler(SIGBUS, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
@@ -58,18 +61,21 @@ void InterpreterCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
#endif
}
void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
CPUBackend::CompiledCode InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
const auto IRSize = AlignUp(IR->GetInlineSize(), 16);
const auto MaxSize = IRSize + Dispatcher::MaxInterpreterTrampolineSize + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
if ((BufferUsed + MaxSize) > CurrentCodeBuffer->Size) {
ThreadState->CTX->ClearCodeCache(ThreadState);
static_cast<Context::ContextImpl*>(ThreadState->CTX)->ClearCodeCache(ThreadState);
}
const auto BufferStart = CurrentCodeBuffer->Ptr + BufferUsed;
CPUBackend::CompiledCode CodeData{};
auto DestBuffer = BufferStart;
const auto BufferStartOffset = BufferUsed;
CodeData.BlockBegin = CodeData.BlockEntry = CurrentCodeBuffer->Ptr + BufferStartOffset;
auto DestBuffer = CodeData.BlockBegin;
if (GDBEnabled) {
const auto GDBSize = Dispatch->GenerateGDBPauseCheck(DestBuffer, Entry);
@@ -86,7 +92,9 @@ void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR:
DestBuffer += IRSize;
BufferUsed += IRSize;
return BufferStart;
CodeData.Size = BufferUsed - BufferStartOffset;
return CodeData;
}
void InterpreterCore::ClearCache() {
@@ -95,11 +103,11 @@ void InterpreterCore::ClearCache() {
BufferUsed = 0;
}
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
return std::make_unique<InterpreterCore>(ctx->Dispatcher.get(), Thread);
}
void InitializeInterpreterSignalHandlers(FEXCore::Context::Context *CTX) {
void InitializeInterpreterSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
InterpreterCore::InitializeSignalHandlers(CTX);
}
@@ -3,7 +3,7 @@
#include <memory>
namespace FEXCore::Context {
struct Context;
class ContextImpl;
}
namespace FEXCore::Core {
@@ -14,9 +14,9 @@ namespace FEXCore::CPU {
class CPUBackend;
struct DispatcherConfig;
[[nodiscard]] std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx,
[[nodiscard]] std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::ContextImpl *ctx,
FEXCore::Core::InternalThreadState *Thread);
void InitializeInterpreterSignalHandlers(FEXCore::Context::Context *CTX);
void InitializeInterpreterSignalHandlers(FEXCore::Context::ContextImpl *CTX);
CPUBackendFeatures GetInterpreterBackendFeatures();
} // namespace FEXCore::CPU
@@ -113,7 +113,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
// Branch ops
REGISTER_OP(SIGNALRETURN, SignalReturn);
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
REGISTER_OP(EXITFUNCTION, ExitFunction);
REGISTER_OP(JUMP, Jump);
@@ -128,6 +127,7 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
// Conversion ops
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(VDUPFROMGPR, VDupFromGPR);
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
REGISTER_OP(FLOAT_FTOF, Float_FToF);
REGISTER_OP(VECTOR_STOF, Vector_SToF);
@@ -154,6 +154,7 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
REGISTER_OP(STOREMEM, StoreMem);
REGISTER_OP(LOADMEMTSO, LoadMem);
REGISTER_OP(STOREMEMTSO, StoreMem);
REGISTER_OP(MEMSET, MemSet);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
@@ -222,6 +223,8 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
REGISTER_OP(VZIP2, VZip);
REGISTER_OP(VUNZIP, VUnZip);
REGISTER_OP(VUNZIP2, VUnZip);
REGISTER_OP(VTRN, VTrn);
REGISTER_OP(VTRN2, VTrn);
REGISTER_OP(VBSL, VBSL);
REGISTER_OP(VCMPEQ, VCMPEQ);
REGISTER_OP(VCMPEQZ, VCMPEQZ);
@@ -142,7 +142,6 @@ namespace FEXCore::CPU {
DEF_OP(AtomicFetchNeg);
///< Branch ops
DEF_OP(SignalReturn);
DEF_OP(CallbackReturn);
DEF_OP(ExitFunction);
DEF_OP(Jump);
@@ -157,6 +156,7 @@ namespace FEXCore::CPU {
///< Conversion ops
DEF_OP(VInsGPR);
DEF_OP(VCastFromGPR);
DEF_OP(VDupFromGPR);
DEF_OP(Float_FromGPR_S);
DEF_OP(Float_FToF);
DEF_OP(Vector_SToF);
@@ -181,6 +181,7 @@ namespace FEXCore::CPU {
DEF_OP(StoreFlag);
DEF_OP(LoadMem);
DEF_OP(StoreMem);
DEF_OP(MemSet);
DEF_OP(CacheLineClear);
DEF_OP(CacheLineClean);
DEF_OP(CacheLineZero);
@@ -242,6 +243,7 @@ namespace FEXCore::CPU {
DEF_OP(VSMax);
DEF_OP(VZip);
DEF_OP(VUnZip);
DEF_OP(VTrn);
DEF_OP(VBSL);
DEF_OP(VCMPEQ);
DEF_OP(VCMPEQZ);
@@ -288,6 +288,111 @@ DEF_OP(StoreMem) {
}
}
DEF_OP(MemSet) {
const auto Op = IROp->C<IR::IROp_MemSet>();
const int32_t Size = Op->Size;
char *MemData = *GetSrc<char **>(Data->SSAData, Op->Addr);
const auto Value = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
const auto Length = *GetSrc<uint64_t*>(Data->SSAData, Op->Length);
const auto Direction = *GetSrc<uint8_t*>(Data->SSAData, Op->Direction);
auto MemSetElements = [](auto* Memory, uint64_t Value, size_t Length) {
for (size_t i = 0; i < Length; ++i) {
Memory[i] = Value;
}
};
auto MemSetElementsInverse = [](auto* Memory, uint64_t Value, size_t Length) {
for (size_t i = 0; i < Length; ++i) {
Memory[-i] = Value;
}
};
if (Direction == 0) { // Forward
if (Op->IsAtomic) {
switch (Size) {
case 1:
MemSetElements(reinterpret_cast<std::atomic<uint8_t>*>(MemData), Value, Length);
break;
case 2:
MemSetElements(reinterpret_cast<std::atomic<uint16_t>*>(MemData), Value, Length);
break;
case 4:
MemSetElements(reinterpret_cast<std::atomic<uint32_t>*>(MemData), Value, Length);
break;
case 8:
MemSetElements(reinterpret_cast<std::atomic<uint64_t>*>(MemData), Value, Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
else {
switch (Size) {
case 1:
MemSetElements(reinterpret_cast<uint8_t*>(MemData), Value, Length);
break;
case 2:
MemSetElements(reinterpret_cast<uint16_t*>(MemData), Value, Length);
break;
case 4:
MemSetElements(reinterpret_cast<uint32_t*>(MemData), Value, Length);
break;
case 8:
MemSetElements(reinterpret_cast<uint64_t*>(MemData), Value, Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
GD = reinterpret_cast<uint64_t>(MemData + (Length * Size));
}
else { // Backward
if (Op->IsAtomic) {
switch (Size) {
case 1:
MemSetElementsInverse(reinterpret_cast<std::atomic<uint8_t>*>(MemData), Value, Length);
break;
case 2:
MemSetElementsInverse(reinterpret_cast<std::atomic<uint16_t>*>(MemData), Value, Length);
break;
case 4:
MemSetElementsInverse(reinterpret_cast<std::atomic<uint32_t>*>(MemData), Value, Length);
break;
case 8:
MemSetElementsInverse(reinterpret_cast<std::atomic<uint64_t>*>(MemData), Value, Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
else {
switch (Size) {
case 1:
MemSetElementsInverse(reinterpret_cast<uint8_t*>(MemData), Value, Length);
break;
case 2:
MemSetElementsInverse(reinterpret_cast<uint16_t*>(MemData), Value, Length);
break;
case 4:
MemSetElementsInverse(reinterpret_cast<uint32_t*>(MemData), Value, Length);
break;
case 8:
MemSetElementsInverse(reinterpret_cast<uint64_t*>(MemData), Value, Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
GD = reinterpret_cast<uint64_t>(MemData - (Length * Size));
}
}
DEF_OP(CacheLineClear) {
auto Op = IROp->C<IR::IROp_CacheLineClear>();
@@ -902,6 +902,67 @@ DEF_OP(VZip) {
memcpy(GDP, Tmp, OpSize);
}
DEF_OP(VTrn) {
const auto Op = IROp->C<IR::IROp_VTrn>();
const uint8_t OpSize = IROp->Size;
void *Src1 = GetSrc<void*>(Data->SSAData, Op->VectorLower);
void *Src2 = GetSrc<void*>(Data->SSAData, Op->VectorUpper);
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
const uint8_t ElementSize = Op->Header.ElementSize;
uint8_t Elements = OpSize / ElementSize;
const uint8_t BaseOffset = IROp->Op == IR::OP_VTRN2 ? 1 : 0;
Elements >>= 1;
switch (ElementSize) {
case 1: {
auto *Dst_d = reinterpret_cast<uint8_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint8_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint8_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i*2] = Src1_d[i*2 + BaseOffset];
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
}
break;
}
case 2: {
auto *Dst_d = reinterpret_cast<uint16_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint16_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint16_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i*2] = Src1_d[i*2 + BaseOffset];
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
}
break;
}
case 4: {
auto *Dst_d = reinterpret_cast<uint32_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint32_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint32_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i*2] = Src1_d[i*2 + BaseOffset];
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
}
break;
}
case 8: {
auto *Dst_d = reinterpret_cast<uint64_t*>(Tmp);
auto *Src1_d = reinterpret_cast<uint64_t*>(Src1);
auto *Src2_d = reinterpret_cast<uint64_t*>(Src2);
for (unsigned i = 0; i < Elements; ++i) {
Dst_d[i*2] = Src1_d[i*2 + BaseOffset];
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
}
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
break;
}
memcpy(GDP, Tmp, OpSize);
}
DEF_OP(VUnZip) {
const auto Op = IROp->C<IR::IROp_VUnZip>();
const uint8_t OpSize = IROp->Size;
@@ -964,7 +1025,9 @@ DEF_OP(VUnZip) {
}
DEF_OP(VBSL) {
auto Op = IROp->C<IR::IROp_VBSL>();
const auto Op = IROp->C<IR::IROp_VBSL>();
const auto OpSize = IROp->Size;
const auto Src1 = *GetSrc<InterpVector256*>(Data->SSAData, Op->VectorMask);
const auto Src2 = *GetSrc<InterpVector256*>(Data->SSAData, Op->VectorTrue);
const auto Src3 = *GetSrc<InterpVector256*>(Data->SSAData, Op->VectorFalse);
@@ -974,7 +1037,8 @@ DEF_OP(VBSL) {
.Upper = (Src2.Upper & Src1.Upper) | (Src3.Upper & ~Src1.Upper),
};
memcpy(GDP, &Tmp, sizeof(Tmp));
memset(GDP, 0, sizeof(InterpVector256));
memcpy(GDP, &Tmp, OpSize);
}
DEF_OP(VCMPEQ) {
@@ -262,13 +262,13 @@ DEF_OP(MulH) {
const auto Src2 = GetReg(Op->Src2.ID());
if (OpSize == 4) {
sxtw(TMP1, Src1);
sxtw(TMP2, Src2);
sxtw(TMP1, Src1.W());
sxtw(TMP2, Src2.W());
mul(ARMEmitter::Size::i32Bit, Dst, TMP1, TMP2);
ubfx(ARMEmitter::Size::i32Bit, Dst, Dst, 32, 32);
}
else {
smulh(Dst, Src1, Src2);
smulh(Dst.X(), Src1.X(), Src2.X());
}
}
@@ -289,7 +289,7 @@ DEF_OP(UMulH) {
ubfx(ARMEmitter::Size::i64Bit, Dst, Dst, 32, 32);
}
else {
umulh(Dst, Src1, Src2);
umulh(Dst.X(), Src1.X(), Src2.X());
}
}
@@ -610,7 +610,7 @@ DEF_OP(LDiv) {
case 4: {
mov(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 32, 32);
sxtw(TMP2, Divisor);
sxtw(TMP2, Divisor.W());
sdiv(EmitSize, Dst, TMP1, TMP2);
break;
}
@@ -744,7 +744,7 @@ DEF_OP(LRem) {
case 4: {
mov(EmitSize, TMP1, Lower);
bfi(EmitSize, TMP1, Upper, 32, 32);
sxtw(TMP3, Divisor);
sxtw(TMP3, Divisor.W());
sdiv(EmitSize, TMP2, TMP1, TMP3);
msub(EmitSize, Dst, TMP2, TMP3, TMP1);
break;
@@ -1173,8 +1173,8 @@ DEF_OP(VExtractToGPR) {
// Inverting our dedicated predicate for 128-bit operations selects
// all of the top lanes. We can then compact those into a temporary.
const auto CompactPred = ARMEmitter::PReg::p0;
not_(CompactPred, PRED_TMP_32B, PRED_TMP_16B);
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1, CompactPred, Vector);
not_(CompactPred, PRED_TMP_32B.Zeroing(), PRED_TMP_16B);
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), CompactPred, Vector.Z());
// Sanitize the zero-based index to work on the now-moved
// upper half of the vector.
@@ -27,7 +27,7 @@ void Arm64JITCore::InsertNamedThunkRelocation(ARMEmitter::Register Reg, const IR
MoveABI.NamedThunkMove.Header.Type = FEXCore::CPU::RelocationTypes::RELOC_NAMED_THUNK_MOVE;
// Offset is the offset from the entrypoint of the block
auto CurrentCursor = GetCursorAddress<uint8_t *>();
MoveABI.NamedThunkMove.Offset = CurrentCursor - GuestEntry;
MoveABI.NamedThunkMove.Offset = CurrentCursor - CodeData.BlockBegin;
MoveABI.NamedThunkMove.Symbol = Sum;
MoveABI.NamedThunkMove.RegisterIndex = Reg.Idx();
@@ -58,7 +58,7 @@ Arm64JITCore::NamedSymbolLiteralPair Arm64JITCore::InsertNamedSymbolLiteral(FEXC
void Arm64JITCore::PlaceNamedSymbolLiteral(NamedSymbolLiteralPair &Lit) {
// Offset is the offset from the entrypoint of the block
auto CurrentCursor = GetCursorAddress<uint8_t *>();
Lit.MoveABI.NamedSymbolLiteral.Offset = CurrentCursor - GuestEntry;
Lit.MoveABI.NamedSymbolLiteral.Offset = CurrentCursor - CodeData.BlockBegin;
Bind(&Lit.Loc);
dc64(Lit.Lit);
@@ -70,7 +70,7 @@ void Arm64JITCore::InsertGuestRIPMove(ARMEmitter::Register Reg, uint64_t Constan
MoveABI.GuestRIPMove.Header.Type = FEXCore::CPU::RelocationTypes::RELOC_GUEST_RIP_MOVE;
// Offset is the offset from the entrypoint of the block
auto CurrentCursor = GetCursorAddress<uint8_t *>();
MoveABI.GuestRIPMove.Offset = CurrentCursor - GuestEntry;
MoveABI.GuestRIPMove.Offset = CurrentCursor - CodeData.BlockBegin;
MoveABI.GuestRIPMove.GuestRIP = Constant;
MoveABI.GuestRIPMove.RegisterIndex = Reg.Idx();
@@ -20,23 +20,6 @@ $end_info$
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
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);
}
DEF_OP(CallbackReturn) {
// spill back to CTX
SpillStaticRegs();
@@ -184,14 +167,23 @@ DEF_OP(Syscall) {
FEXCore::IR::SyscallFlags Flags = Op->Flags;
PushDynamicRegsAndLR(TMP1);
if ((Flags & FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) != FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) {
SpillStaticRegs();
}
else {
uint32_t GPRSpillMask = ~0U;
uint32_t FPRSpillMask = ~0U;
if ((Flags & FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) == FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) {
// Need to spill all caller saved registers still
SpillStaticRegs(true, CALLER_GPR_MASK, CALLER_FPR_MASK);
GPRSpillMask = CALLER_GPR_MASK;
FPRSpillMask = CALLER_FPR_MASK;
}
SpillStaticRegs(true, GPRSpillMask, FPRSpillMask);
// Now that we are spilled, store in the state that we are in a syscall
// Still without overwriting registers that matter
// 16bit LoadConstant to be a single instruction
// This gives the signal handler a value to check to see if we are in a syscall at all
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, GPRSpillMask & 0xFFFF);
str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
uint64_t SPOffset = AlignUp(FEXCore::HLE::SyscallArguments::MAX_ARGS * 8, 16);
sub(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset);
for (uint32_t i = 0; i < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++i) {
@@ -213,19 +205,17 @@ DEF_OP(Syscall) {
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset);
if ((Flags & FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) != FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY &&
(Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
FillStaticRegs();
}
else {
if ((Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
// Result is now in x0
// Fix the stack and any values that were stepped on
FillStaticRegs(true, CALLER_GPR_MASK, CALLER_FPR_MASK);
}
FillStaticRegs(true, GPRSpillMask, FPRSpillMask);
PopDynamicRegsAndLR();
// Now the registers we've spilled are back in their original host registers
// We can safely claim we are no longer in a syscall
str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
PopDynamicRegsAndLR();
if ((Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
// Move result to its destination register
mov(ARMEmitter::Size::i64Bit, GetReg(Node), ARMEmitter::Reg::r0);
}
@@ -255,9 +245,9 @@ DEF_OP(InlineSyscall) {
if (Op->Header.Args[i].IsInvalid()) break;
auto Reg = GetReg(Op->Header.Args[i].ID());
if (Reg.Idx() == ARMEmitter::Reg::r8.Idx() ||
Reg.Idx() == ARMEmitter::Reg::r4.Idx() ||
Reg.Idx() == ARMEmitter::Reg::r5.Idx()) {
if (Reg == ARMEmitter::Reg::r8 ||
Reg == ARMEmitter::Reg::r4 ||
Reg == ARMEmitter::Reg::r5) {
SpillMask |= (1U << Reg.Idx());
Intersects = true;
@@ -288,13 +278,13 @@ DEF_OP(InlineSyscall) {
// In the case of intersection with x4, x5, or x8 then these are currently SRA
// for registers RAX, RBX, and RSI. Which have just been spilled
// Just load back from the context. Could be slightly smarter but this is fairly uncommon
if (Reg.Idx() == FEXCore::ARMEmitter::Reg::r8.Idx()) {
if (Reg == ARMEmitter::Reg::r8) {
ldr(EmitSubSize, RegArgs[i].R(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSI]));
}
else if (Reg.Idx() == FEXCore::ARMEmitter::Reg::r4.Idx()) {
else if (Reg == ARMEmitter::Reg::r4) {
ldr(EmitSubSize, RegArgs[i].R(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RAX]));
}
else if (Reg.Idx() == FEXCore::ARMEmitter::Reg::r5.Idx()) {
else if (Reg == ARMEmitter::Reg::r5) {
ldr(EmitSubSize, RegArgs[i].R(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RBX]));
}
else {
@@ -341,7 +331,7 @@ DEF_OP(Thunk) {
mov(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, GetReg(Op->ArgPtr.ID()));
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
auto thunkFn = static_cast<Context::ContextImpl*>(ThreadState->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r2, (uintptr_t)thunkFn);
#ifdef VIXL_SIMULATOR
GenerateIndirectRuntimeCall<void, void*, void*>(ARMEmitter::Reg::r2);
@@ -55,7 +55,7 @@ DEF_OP(VInsGPR) {
// Move the upper lane down for the insertion.
const auto CompactPred = ARMEmitter::PReg::p0;
not_(CompactPred, PRED_TMP_32B.Zeroing(), PRED_TMP_16B);
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), CompactPred, DestVector);
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), CompactPred, DestVector.Z());
}
// Put data in place for destructive SPLICE below.
@@ -108,6 +108,32 @@ DEF_OP(VCastFromGPR) {
}
}
DEF_OP(VDupFromGPR) {
const auto Op = IROp->C<IR::IROp_VDupFromGPR>();
const auto OpSize = IROp->Size;
const auto Dst = GetVReg(Node);
const auto Src = GetReg(Op->Src.ID());
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = IROp->ElementSize;
LOGMAN_THROW_AA_FMT(ElementSize == 8 || ElementSize == 4 || ElementSize == 2 || ElementSize == 1,
"Unexpected {} element size: {}", __func__, ElementSize);
const auto SubEmitSize =
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 1 ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && Is256Bit) {
dup(SubEmitSize, Dst.Z(), Src);
} else {
dup(SubEmitSize, Dst.Q(), Src);
}
}
DEF_OP(Float_FromGPR_S) {
const auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
@@ -199,7 +225,7 @@ DEF_OP(Vector_FToZS) {
const auto Vector = GetVReg(Op->Vector.ID());
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B;
fcvtzs(Dst, SubEmitSize, Mask.Merging(), Vector, SubEmitSize);
fcvtzs(Dst.Z(), SubEmitSize, Mask.Merging(), Vector.Z(), SubEmitSize);
} else {
fcvtzs(SubEmitSize, Dst.Q(), Vector.Q());
}
@@ -222,8 +248,8 @@ DEF_OP(Vector_FToS) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B;
frinti(SubEmitSize, Dst, Mask.Merging(), Vector);
fcvtzs(Dst, SubEmitSize, Mask.Merging(), Dst, SubEmitSize);
frinti(SubEmitSize, Dst.Z(), Mask.Merging(), Vector.Z());
fcvtzs(Dst.Z(), SubEmitSize, Mask.Merging(), Dst.Z(), SubEmitSize);
} else {
const auto Dst = GetVReg(Node);
const auto Vector = GetVReg(Op->Vector.ID());
@@ -276,12 +302,12 @@ DEF_OP(Vector_FToF) {
break;
}
case 0x0204: { // Half <- Float
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i16Bit, Dst, Mask, Vector);
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i16Bit, Dst.Z(), Mask, Vector.Z());
uzp2(FEXCore::ARMEmitter::SubRegSize::i16Bit, Dst.Z(), Dst.Z(), Dst.Z());
break;
}
case 0x0408: { // Float <- Double
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i32Bit, Dst, Mask, Vector);
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i32Bit, Dst.Z(), Mask, Vector.Z());
uzp2(FEXCore::ARMEmitter::SubRegSize::i32Bit, Dst.Z(), Dst.Z(), Dst.Z());
break;
}
@@ -17,37 +17,73 @@ DEF_OP(AESImc) {
}
DEF_OP(AESEnc) {
auto Op = IROp->C<IR::IROp_VAESEnc>();
const auto Op = IROp->C<IR::IROp_VAESEnc>();
const auto OpSize = IROp->Size;
const auto Dst = GetVReg(Node);
const auto Key = GetVReg(Op->Key.ID());
const auto State = GetVReg(Op->State.ID());
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
"Currently only supports 128-bit operations.");
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
mov(VTMP1.Q(), State.Q());
aese(VTMP1, VTMP2);
aesmc(VTMP1, VTMP1);
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
eor(Dst.Q(), VTMP1.Q(), Key.Q());
}
DEF_OP(AESEncLast) {
auto Op = IROp->C<IR::IROp_VAESEncLast>();
const auto Op = IROp->C<IR::IROp_VAESEncLast>();
const auto OpSize = IROp->Size;
const auto Dst = GetVReg(Node);
const auto Key = GetVReg(Op->Key.ID());
const auto State = GetVReg(Op->State.ID());
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
"Currently only supports 128-bit operations.");
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
mov(VTMP1.Q(), State.Q());
aese(VTMP1, VTMP2);
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
eor(Dst.Q(), VTMP1.Q(), Key.Q());
}
DEF_OP(AESDec) {
auto Op = IROp->C<IR::IROp_VAESDec>();
const auto Op = IROp->C<IR::IROp_VAESDec>();
const auto OpSize = IROp->Size;
const auto Dst = GetVReg(Node);
const auto Key = GetVReg(Op->Key.ID());
const auto State = GetVReg(Op->State.ID());
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
"Currently only supports 128-bit operations.");
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
mov(VTMP1.Q(), State.Q());
aesd(VTMP1, VTMP2);
aesimc(VTMP1, VTMP1);
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
eor(Dst.Q(), VTMP1.Q(), Key.Q());
}
DEF_OP(AESDecLast) {
auto Op = IROp->C<IR::IROp_VAESDecLast>();
const auto Op = IROp->C<IR::IROp_VAESDecLast>();
const auto OpSize = IROp->Size;
const auto Dst = GetVReg(Node);
const auto Key = GetVReg(Op->Key.ID());
const auto State = GetVReg(Op->State.ID());
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
"Currently only supports 128-bit operations.");
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
mov(VTMP1.Q(), State.Q());
aesd(VTMP1, VTMP2);
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
eor(Dst.Q(), VTMP1.Q(), Key.Q());
}
DEF_OP(AESKeyGenAssist) {
@@ -101,18 +137,22 @@ DEF_OP(CRC32) {
crc32cw(Dst.W(), Src1.W(), Src2.W());
break;
case 8:
crc32cx(Dst, Src1, Src2);
crc32cx(Dst.X(), Src1.X(), Src2.X());
break;
default: LOGMAN_MSG_A_FMT("Unknown CRC32 size: {}", Op->SrcSize);
}
}
DEF_OP(PCLMUL) {
auto Op = IROp->C<IR::IROp_PCLMUL>();
const auto Op = IROp->C<IR::IROp_PCLMUL>();
const auto OpSize = IROp->Size;
auto Dst = GetVReg(Node);
auto Src1 = GetVReg(Op->Src1.ID());
auto Src2 = GetVReg(Op->Src2.ID());
const auto Dst = GetVReg(Node);
const auto Src1 = GetVReg(Op->Src1.ID());
const auto Src2 = GetVReg(Op->Src2.ID());
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
"Currently only supports 128-bit operations.");
switch (Op->Selector) {
case 0b00000000:
+59 -27
View File
@@ -484,7 +484,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
FEXCore::ARMEmitter::Emitter::ClearICache((void*)branch, 24);
// Add de-linking handler
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [branch, LinkerAddress]{
Context::ContextImpl::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [branch, LinkerAddress]{
FEXCore::ARMEmitter::Emitter emit((uint8_t*)(branch), 24);
FEXCore::ARMEmitter::ForwardLabel l_BranchHost;
emit.ldr(FEXCore::ARMEmitter::XReg::x0, &l_BranchHost);
@@ -498,7 +498,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
record[0] = HostCode;
// Add de-linking handler
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
Context::ContextImpl::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
record[0] = LinkerAddress;
});
}
@@ -509,7 +509,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
void Arm64JITCore::Op_NoOp(IR::IROp_Header const *IROp, IR::NodeID Node) {
}
Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread)
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
, Arm64Emitter(ctx, 0)
, HostSupportsSVE{ctx->HostFeatures.SupportsAVX}
@@ -543,7 +543,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadRemoveCodeEntryFromJit);
Common.CPUIDObj = reinterpret_cast<uint64_t>(&CTX->CPUID);
{
@@ -553,7 +553,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
Common.SyscallHandlerObj = reinterpret_cast<uint64_t>(CTX->SyscallHandler);
Common.SyscallHandlerFunc = reinterpret_cast<uint64_t>(FEXCore::Context::HandleSyscall);
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::Context::ThreadExitFunctionLink<Arm64JITCore_ExitFunctionLink>);
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadExitFunctionLink<Arm64JITCore_ExitFunctionLink>);
// Fill in the fallback handlers
@@ -572,9 +572,9 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
ClearCache();
}
void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
return Thread->CTX->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
return reinterpret_cast<Context::ContextImpl*>(Thread->CTX)->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
}, true);
#ifdef _M_ARM_64
@@ -584,7 +584,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
return false;
}
return FEXCore::ArchHelpers::Arm64::HandleSIGBUS(Thread->CTX->Config.ParanoidTSO(), Signal, info, ucontext);
return FEXCore::ArchHelpers::Arm64::HandleSIGBUS(static_cast<Context::ContextImpl*>(Thread->CTX)->Config.ParanoidTSO(), Signal, info, ucontext);
}, true);
#endif
}
@@ -656,7 +656,7 @@ bool Arm64JITCore::IsGPR(IR::NodeID Node) const {
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
}
void *Arm64JITCore::CompileCode(uint64_t Entry,
CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
FEXCore::IR::RegisterAllocationData *RAData,
@@ -669,6 +669,21 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
this->Entry = Entry;
this->RAData = RAData;
this->DebugData = DebugData;
this->IR = IR;
// Fairly excessive buffer range to make sure we don't overflow
uint32_t BufferRange = SSACount * 16 + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
if ((GetCursorOffset() + BufferRange) > CurrentCodeBuffer->Size) {
CTX->ClearCodeCache(ThreadState);
}
CodeData.BlockBegin = GetCursorAddress<uint8_t*>();
// Put the code header at the start of the data block.
ARMEmitter::BackwardLabel JITCodeHeaderLabel{};
Bind(&JITCodeHeaderLabel);
JITCodeHeader *CodeHeader = GetCursorAddress<JITCodeHeader *>();
CursorIncrement(sizeof(JITCodeHeader));
#ifdef VIXL_DISASSEMBLER
const auto DisasmBegin = GetCursorAddress<const vixl::aarch64::Instruction*>();
@@ -678,14 +693,6 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, Entry);
#endif
this->IR = IR;
// Fairly excessive buffer range to make sure we don't overflow
uint32_t BufferRange = SSACount * 16 + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
if ((GetCursorOffset() + BufferRange) > CurrentCodeBuffer->Size) {
CTX->ClearCodeCache(ThreadState);
}
// AAPCS64
// r30 = LR
// r29 = FP
@@ -706,10 +713,15 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
// X1-X3 = Temp
// X4-r18 = RA
GuestEntry = GetCursorAddress<uint8_t *>();
CodeData.BlockEntry = GetCursorAddress<uint8_t*>();
// Get the address of the JITCodeHeader and store in to the core state.
// Two instruction cost, each 1 cycle.
adr(TMP1, &JITCodeHeaderLabel);
str(TMP1, STATE, offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader));
if (GDBEnabled) {
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(GuestEntry, Entry);
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(CodeData.BlockEntry, Entry);
CursorIncrement(GDBSize);
}
@@ -822,7 +834,6 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
// Branch ops
REGISTER_OP(SIGNALRETURN, SignalReturn);
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
REGISTER_OP(EXITFUNCTION, ExitFunction);
REGISTER_OP(JUMP, Jump);
@@ -837,6 +848,7 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
// Conversion ops
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(VDUPFROMGPR, VDupFromGPR);
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
REGISTER_OP(FLOAT_FTOF, Float_FToF);
REGISTER_OP(VECTOR_STOF, Vector_SToF);
@@ -887,6 +899,8 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
Op_StoreMemTSO(IROp, ID);
}
break;
REGISTER_OP(MEMSET, MemSet);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
@@ -955,6 +969,8 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
REGISTER_OP(VZIP2, VZip2);
REGISTER_OP(VUNZIP, VUnZip);
REGISTER_OP(VUNZIP2, VUnZip2);
REGISTER_OP(VTRN, VTrn);
REGISTER_OP(VTRN2, VTrn2);
REGISTER_OP(VBSL, VBSL);
REGISTER_OP(VCMPEQ, VCMPEQ);
REGISTER_OP(VCMPEQZ, VCMPEQZ);
@@ -1009,7 +1025,7 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
if (DebugData) {
DebugData->Subblocks.push_back({
static_cast<uint32_t>(BlockStartHostCode - GuestEntry),
static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockEntry),
static_cast<uint32_t>(GetCursorAddress<uint8_t *>() - BlockStartHostCode)
});
}
@@ -1022,8 +1038,24 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
}
PendingTargetLabel = nullptr;
auto CodeEnd = GetCursorAddress<uint8_t *>();
ClearICache(GuestEntry, CodeEnd - GuestEntry);
// Add the JitCodeTail
auto JITBlockTailLocation = GetCursorAddress<uint8_t *>();
auto JITBlockTail = GetCursorAddress<JITCodeTail*>();
CursorIncrement(sizeof(JITCodeTail));
// Put the block's RIP entry in the tail.
// This will be used for RIP reconstruction in the future.
// TODO: This needs to be a data RIP relocation once code caching works.
// Current relocation code doesn't support this feature yet.
JITBlockTail->RIP = Entry;
CodeHeader->OffsetToBlockTail = JITBlockTailLocation - CodeData.BlockBegin;
CodeData.Size = GetCursorAddress<uint8_t *>() - CodeData.BlockBegin;
JITBlockTail->Size = CodeData.Size;
ClearICache(CodeData.BlockBegin, CodeData.Size);
#ifdef VIXL_DISASSEMBLER
const auto DisasmEnd = GetCursorAddress<const vixl::aarch64::Instruction*>();
@@ -1031,13 +1063,13 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
#endif
if (DebugData) {
DebugData->HostCodeSize = CodeEnd - GuestEntry;
DebugData->HostCodeSize = CodeData.Size;
DebugData->Relocations = &Relocations;
}
this->IR = nullptr;
return GuestEntry;
return CodeData;
}
void Arm64JITCore::ResetStack() {
@@ -1056,11 +1088,11 @@ void Arm64JITCore::ResetStack() {
}
}
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
return std::make_unique<Arm64JITCore>(ctx, Thread);
}
void InitializeArm64JITSignalHandlers(FEXCore::Context::Context *CTX) {
void InitializeArm64JITSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
Arm64JITCore::InitializeSignalHandlers(CTX);
}
+9 -10
View File
@@ -31,13 +31,13 @@ namespace FEXCore::Core {
namespace FEXCore::CPU {
class Arm64JITCore final : public CPUBackend, public Arm64Emitter {
public:
explicit Arm64JITCore(FEXCore::Context::Context *ctx,
explicit Arm64JITCore(FEXCore::Context::ContextImpl *ctx,
FEXCore::Core::InternalThreadState *Thread);
~Arm64JITCore() override;
[[nodiscard]] std::string GetName() override { return "JIT"; }
[[nodiscard]] void *CompileCode(uint64_t Entry,
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
@@ -48,7 +48,7 @@ public:
void ClearCache() override;
static void InitializeSignalHandlers(FEXCore::Context::Context *CTX);
static void InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX);
void ClearRelocations() override { Relocations.clear(); }
@@ -57,9 +57,10 @@ private:
const bool HostSupportsSVE{};
ARMEmitter::BiDirectionalLabel *PendingTargetLabel;
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
FEXCore::IR::IRListView const *IR;
uint64_t Entry;
CPUBackend::CompiledCode CodeData{};
std::map<IR::NodeID, ARMEmitter::BiDirectionalLabel> JumpTargets;
@@ -230,11 +231,6 @@ private:
/** @} */
uint32_t SpillSlots{};
/**
* @brief Current guest RIP entrypoint
*/
uint8_t *GuestEntry{};
#define DEF_OP(x) void Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
///< Unhandled handler
@@ -312,7 +308,6 @@ private:
DEF_OP(AtomicFetchNeg);
///< Branch ops
DEF_OP(SignalReturn);
DEF_OP(CallbackReturn);
DEF_OP(ExitFunction);
DEF_OP(Jump);
@@ -327,6 +322,7 @@ private:
///< Conversion ops
DEF_OP(VInsGPR);
DEF_OP(VCastFromGPR);
DEF_OP(VDupFromGPR);
DEF_OP(Float_FromGPR_S);
DEF_OP(Float_FToF);
DEF_OP(Vector_SToF);
@@ -353,6 +349,7 @@ private:
DEF_OP(StoreMem);
DEF_OP(LoadMemTSO);
DEF_OP(StoreMemTSO);
DEF_OP(MemSet);
DEF_OP(ParanoidLoadMemTSO);
DEF_OP(ParanoidStoreMemTSO);
DEF_OP(CacheLineClear);
@@ -417,6 +414,8 @@ private:
DEF_OP(VZip2);
DEF_OP(VUnZip);
DEF_OP(VUnZip2);
DEF_OP(VTrn);
DEF_OP(VTrn2);
DEF_OP(VBSL);
DEF_OP(VCMPEQ);
DEF_OP(VCMPEQZ);
+185 -22
View File
@@ -703,7 +703,7 @@ DEF_OP(SpillRegister) {
const auto Src = GetReg(Op->Value.ID());
switch (OpSize) {
case 1: {
if (SlotOffset > 4095) {
if (SlotOffset > LSByteMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
strb(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -713,7 +713,7 @@ DEF_OP(SpillRegister) {
break;
}
case 2: {
if (SlotOffset > 8190) {
if (SlotOffset > LSHalfMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
strh(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -723,7 +723,7 @@ DEF_OP(SpillRegister) {
break;
}
case 4: {
if (SlotOffset > 16380) {
if (SlotOffset > LSWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -733,7 +733,7 @@ DEF_OP(SpillRegister) {
break;
}
case 8: {
if (SlotOffset > 32760) {
if (SlotOffset > LSDWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -751,7 +751,7 @@ DEF_OP(SpillRegister) {
switch (OpSize) {
case 4: {
if (SlotOffset > 16380) {
if (SlotOffset > LSWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -761,7 +761,7 @@ DEF_OP(SpillRegister) {
break;
}
case 8: {
if (SlotOffset > 32760) {
if (SlotOffset > LSDWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -771,7 +771,7 @@ DEF_OP(SpillRegister) {
break;
}
case 16: {
if (SlotOffset > 65520) {
if (SlotOffset > LSQWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
str(Src.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -803,7 +803,7 @@ DEF_OP(FillRegister) {
const auto Dst = GetReg(Node);
switch (OpSize) {
case 1: {
if (SlotOffset > 4095) {
if (SlotOffset > LSByteMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldrb(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -813,7 +813,7 @@ DEF_OP(FillRegister) {
break;
}
case 2: {
if (SlotOffset > 8190) {
if (SlotOffset > LSHalfMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldrh(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -823,7 +823,7 @@ DEF_OP(FillRegister) {
break;
}
case 4: {
if (SlotOffset > 16380) {
if (SlotOffset > LSWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -833,7 +833,7 @@ DEF_OP(FillRegister) {
break;
}
case 8: {
if (SlotOffset > 32760) {
if (SlotOffset > LSDWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -851,7 +851,7 @@ DEF_OP(FillRegister) {
switch (OpSize) {
case 4: {
if (SlotOffset > 16380) {
if (SlotOffset > LSWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -861,7 +861,7 @@ DEF_OP(FillRegister) {
break;
}
case 8: {
if (SlotOffset > 32760) {
if (SlotOffset > LSDWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -871,7 +871,7 @@ DEF_OP(FillRegister) {
break;
}
case 16: {
if (SlotOffset > 65520) {
if (SlotOffset > LSQWordMaxUnsignedOffset) {
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
ldr(Dst.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
}
@@ -911,20 +911,20 @@ FEXCore::ARMEmitter::ExtendedMemOperand Arm64JITCore::GenerateMemOperand(uint8_t
IR::MemOffsetType OffsetType,
uint8_t OffsetScale) {
if (Offset.IsInvalid()) {
return FEXCore::ARMEmitter::ExtendedMemOperand(Base, ARMEmitter::IndexType::OFFSET, 0);
return ARMEmitter::ExtendedMemOperand(Base.X(), ARMEmitter::IndexType::OFFSET, 0);
} else {
if (OffsetScale != 1 && OffsetScale != AccessSize) {
LOGMAN_MSG_A_FMT("Unhandled GenerateMemOperand OffsetScale: {}", OffsetScale);
}
uint64_t Const;
if (IsInlineConstant(Offset, &Const)) {
return FEXCore::ARMEmitter::ExtendedMemOperand(Base, ARMEmitter::IndexType::OFFSET, Const);
return ARMEmitter::ExtendedMemOperand(Base.X(), ARMEmitter::IndexType::OFFSET, Const);
} else {
auto RegOffset = GetReg(Offset.ID());
switch(OffsetType.Val) {
case IR::MEM_OFFSET_SXTX.Val: return FEXCore::ARMEmitter::ExtendedMemOperand(Base, RegOffset, FEXCore::ARMEmitter::ExtendedType::SXTX, (int)std::log2(OffsetScale) );
case IR::MEM_OFFSET_UXTW.Val: return FEXCore::ARMEmitter::ExtendedMemOperand(Base, RegOffset, FEXCore::ARMEmitter::ExtendedType::UXTW, (int)std::log2(OffsetScale) );
case IR::MEM_OFFSET_SXTW.Val: return FEXCore::ARMEmitter::ExtendedMemOperand(Base, RegOffset, FEXCore::ARMEmitter::ExtendedType::SXTW, (int)std::log2(OffsetScale) );
case IR::MEM_OFFSET_SXTX.Val: return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::SXTX, (int)std::log2(OffsetScale) );
case IR::MEM_OFFSET_UXTW.Val: return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::UXTW, (int)std::log2(OffsetScale) );
case IR::MEM_OFFSET_SXTW.Val: return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::SXTW, (int)std::log2(OffsetScale) );
default: LOGMAN_MSG_A_FMT("Unhandled GenerateMemOperand OffsetType: {}", OffsetType.Val); break;
}
}
@@ -1101,14 +1101,14 @@ DEF_OP(LoadMemTSO) {
const auto Dst = GetReg(Node);
if (OpSize == 1) {
// 8bit load is always aligned to natural alignment
ldaprb(Dst, MemReg);
ldaprb(Dst.W(), MemReg);
}
else {
// Aligned
nop();
switch (OpSize) {
case 2:
ldaprh(Dst, MemReg);
ldaprh(Dst.W(), MemReg);
break;
case 4:
ldapr(Dst.W(), MemReg);
@@ -1340,6 +1340,169 @@ DEF_OP(StoreMemTSO) {
}
}
DEF_OP(MemSet) {
// TODO: A future looking task would be to support this with ARM's MOPS instructions.
// The 8-bit non-atomic forward path directly matches ARM's SETP/SETM/SETE instruction,
// while the backward version needs some fixup to convert it to a forward direction.
//
// Assuming non-atomicity and non-faulting behaviour, this can accelerate this implementation.
// Additionally: This is commonly used as a memset to zero. If we know up-front with an inline constant
// that the value is zero, we can optimize any operation larger than 8-bit down to 8-bit to use the MOPS implementation.
const auto Op = IROp->C<IR::IROp_MemSet>();
const int32_t Size = Op->Size;
const auto MemReg = GetReg(Op->Addr.ID());
const auto Value = GetReg(Op->Value.ID());
const auto Length = GetReg(Op->Length.ID());
const auto Direction = GetReg(Op->Direction.ID());
const auto Dst = GetReg(Node);
// If Direction == 0 then:
// MemReg is incremented (by size)
// else:
// MemReg is decremented (by size)
//
// Counter is decremented regardless.
ARMEmitter::ForwardLabel BackwardImpl{};
ARMEmitter::ForwardLabel Done{};
mov(TMP1, Length.X());
if (Op->Prefix.IsInvalid()) {
mov(TMP2, MemReg.X());
}
else {
const auto Prefix = GetReg(Op->Prefix.ID());
add(TMP2, Prefix.X(), MemReg.X());
}
// Backward or forwards implementation depends on flag
cbnz(ARMEmitter::Size::i64Bit, Direction, &BackwardImpl);
auto MemStore = [this](auto Value, uint32_t OpSize, int32_t Size) {
switch (OpSize) {
case 1:
strb<ARMEmitter::IndexType::POST>(Value.W(), TMP2, Size);
break;
case 2:
strh<ARMEmitter::IndexType::POST>(Value.W(), TMP2, Size);
break;
case 4:
str<ARMEmitter::IndexType::POST>(Value.W(), TMP2, Size);
break;
case 8:
str<ARMEmitter::IndexType::POST>(Value.X(), TMP2, Size);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
};
auto MemStoreTSO = [this](auto Value, uint32_t OpSize, int32_t Size) {
if (OpSize == 1) {
// 8bit load is always aligned to natural alignment
stlrb(Value.W(), TMP2);
}
else {
nop();
switch (OpSize) {
case 2:
stlrh(Value.W(), TMP2);
break;
case 4:
stlr(Value.W(), TMP2);
break;
case 8:
stlr(Value.X(), TMP2);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
nop();
}
if (Size >= 0) {
add(ARMEmitter::Size::i64Bit, TMP2, TMP2, OpSize);
}
else {
sub(ARMEmitter::Size::i64Bit, TMP2, TMP2, OpSize);
}
};
// Emit forward direction memset then backward direction memset.
for (int32_t Direction : { 1, -1 }) {
const int32_t OpSize = Size;
const int32_t SizeDirection = Size * Direction;
ARMEmitter::BackwardLabel AgainInternal{};
ARMEmitter::ForwardLabel DoneInternal{};
// Early exit if zero count.
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
Bind(&AgainInternal);
if (Op->IsAtomic) {
MemStoreTSO(Value, OpSize, SizeDirection);
}
else {
MemStore(Value, OpSize, SizeDirection);
}
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 1);
cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
Bind(&DoneInternal);
if (SizeDirection >= 0) {
switch (OpSize) {
case 1:
add(Dst.X(), MemReg.X(), Length.X());
break;
case 2:
add(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 1);
break;
case 4:
add(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 2);
break;
case 8:
add(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 3);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, OpSize);
break;
}
}
else {
switch (OpSize) {
case 1:
sub(Dst.X(), MemReg.X(), Length.X());
break;
case 2:
sub(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 1);
break;
case 4:
sub(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 2);
break;
case 8:
sub(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 3);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, OpSize);
break;
}
}
if (Direction == 1) {
b(&Done);
Bind(&BackwardImpl);
}
}
Bind(&Done);
// Destination already set to the final pointer.
}
DEF_OP(ParanoidLoadMemTSO) {
const auto Op = IROp->C<IR::IROp_LoadMemTSO>();
const auto OpSize = IROp->Size;
@@ -1473,7 +1636,7 @@ DEF_OP(ParanoidStoreMemTSO) {
}
case 32: {
dmb(FEXCore::ARMEmitter::BarrierScope::ISH);
st1b<ARMEmitter::SubRegSize::i8Bit>(Src, PRED_TMP_32B, Addr, 0);
st1b<ARMEmitter::SubRegSize::i8Bit>(Src.Z(), PRED_TMP_32B, Addr, 0);
dmb(FEXCore::ARMEmitter::BarrierScope::ISH);
break;
}
@@ -15,7 +15,7 @@ namespace FEXCore::CPU {
DEF_OP(GuestOpcode) {
auto Op = IROp->C<IR::IROp_GuestOpcode>();
// metadata
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, GetCursorAddress<uint8_t*>() - GuestEntry});
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, GetCursorAddress<uint8_t*>() - CodeData.BlockBegin});
}
DEF_OP(Fence) {
+204 -137
View File
@@ -11,12 +11,14 @@ $end_info$
namespace FEXCore::CPU {
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
DEF_OP(VectorZero) {
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
eor(Dst.Z(), Dst.Z(), Dst.Z());
} else {
const uint8_t OpSize = IROp->Size;
switch (OpSize) {
case 8: {
eor(Dst.D(), Dst.D(), Dst.D());
@@ -34,8 +36,11 @@ DEF_OP(VectorZero) {
}
DEF_OP(VectorImm) {
auto Op = IROp->C<IR::IROp_VectorImm>();
const uint8_t ElementSize = Op->Header.ElementSize;
const auto Op = IROp->C<IR::IROp_VectorImm>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
const auto SubRegSize =
@@ -46,7 +51,7 @@ DEF_OP(VectorImm) {
const auto Dst = GetVReg(Node);
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
if (ElementSize > 1 && (Op->Immediate & 0x80)) {
// SVE dup uses sign extension where VectorImm wants zext
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, Op->Immediate);
@@ -120,13 +125,15 @@ DEF_OP(VMov) {
}
DEF_OP(VAnd) {
auto Op = IROp->C<IR::IROp_VAnd>();
const auto Op = IROp->C<IR::IROp_VAnd>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto Vector1 = GetVReg(Op->Vector1.ID());
const auto Vector2 = GetVReg(Op->Vector2.ID());
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
and_(Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
and_(Dst.Q(), Vector1.Q(), Vector2.Q());
@@ -134,13 +141,15 @@ DEF_OP(VAnd) {
}
DEF_OP(VBic) {
auto Op = IROp->C<IR::IROp_VBic>();
const auto Op = IROp->C<IR::IROp_VBic>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto Vector1 = GetVReg(Op->Vector1.ID());
const auto Vector2 = GetVReg(Op->Vector2.ID());
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
bic(Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
bic(Dst.Q(), Vector1.Q(), Vector2.Q());
@@ -148,13 +157,15 @@ DEF_OP(VBic) {
}
DEF_OP(VOr) {
auto Op = IROp->C<IR::IROp_VOr>();
const auto Op = IROp->C<IR::IROp_VOr>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto Vector1 = GetVReg(Op->Vector1.ID());
const auto Vector2 = GetVReg(Op->Vector2.ID());
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
orr(Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
orr(Dst.Q(), Vector1.Q(), Vector2.Q());
@@ -162,13 +173,15 @@ DEF_OP(VOr) {
}
DEF_OP(VXor) {
auto Op = IROp->C<IR::IROp_VXor>();
const auto Op = IROp->C<IR::IROp_VXor>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto Vector1 = GetVReg(Op->Vector1.ID());
const auto Vector2 = GetVReg(Op->Vector2.ID());
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
eor(Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
eor(Dst.Q(), Vector1.Q(), Vector2.Q());
@@ -176,8 +189,10 @@ DEF_OP(VXor) {
}
DEF_OP(VAdd) {
auto Op = IROp->C<IR::IROp_VAdd>();
const auto Op = IROp->C<IR::IROp_VAdd>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -190,17 +205,19 @@ DEF_OP(VAdd) {
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
add(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
}
else {
} else {
add(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
}
}
DEF_OP(VSub) {
auto Op = IROp->C<IR::IROp_VSub>();
const auto Op = IROp->C<IR::IROp_VSub>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -213,17 +230,19 @@ DEF_OP(VSub) {
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
sub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
}
else {
} else {
sub(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
}
}
DEF_OP(VUQAdd) {
auto Op = IROp->C<IR::IROp_VUQAdd>();
const auto Op = IROp->C<IR::IROp_VUQAdd>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -236,17 +255,19 @@ DEF_OP(VUQAdd) {
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
uqadd(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
}
else {
} else {
uqadd(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
}
}
DEF_OP(VUQSub) {
auto Op = IROp->C<IR::IROp_VUQSub>();
const auto Op = IROp->C<IR::IROp_VUQSub>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -259,17 +280,19 @@ DEF_OP(VUQSub) {
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
uqsub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
}
else {
} else {
uqsub(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
}
}
DEF_OP(VSQAdd) {
auto Op = IROp->C<IR::IROp_VSQAdd>();
const auto Op = IROp->C<IR::IROp_VSQAdd>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -282,17 +305,19 @@ DEF_OP(VSQAdd) {
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
sqadd(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
}
else {
} else {
sqadd(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
}
}
DEF_OP(VSQSub) {
auto Op = IROp->C<IR::IROp_VSQSub>();
const auto Op = IROp->C<IR::IROp_VSQSub>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -305,10 +330,10 @@ DEF_OP(VSQSub) {
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
sqsub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
}
else {
} else {
sqsub(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
}
}
@@ -332,7 +357,7 @@ DEF_OP(VAddP) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
// SVE ADDP is a destructive operation, so we need a temporary
@@ -401,9 +426,10 @@ DEF_OP(VAddV) {
}
DEF_OP(VUMinV) {
auto Op = IROp->C<IR::IROp_VUMinV>();
const auto Op = IROp->C<IR::IROp_VUMinV>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -416,14 +442,9 @@ DEF_OP(VUMinV) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
LOGMAN_THROW_AA_FMT(OpSize == 16 || OpSize == 32,
"Unsupported vector length: {}", OpSize);
const auto Pred = OpSize == 16 ? PRED_TMP_16B
: PRED_TMP_32B;
uminv(SubRegSize, Dst.Z(), Pred, Vector.Z());
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B;
uminv(SubRegSize, Dst, Pred, Vector.Z());
} else {
// Vector
uminv(SubRegSize, Dst.Q(), Vector.Q());
@@ -465,7 +486,8 @@ DEF_OP(VAbs) {
const auto Op = IROp->C<IR::IROp_VAbs>();
const auto OpSize = IROp->Size;
const uint8_t ElementSize = Op->Header.ElementSize;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto Src = GetVReg(Op->Vector.ID());
@@ -477,7 +499,7 @@ DEF_OP(VAbs) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && OpSize == 32) {
if (HostSupportsSVE && Is256Bit) {
abs(SubRegSize, Dst.Z(), PRED_TMP_32B.Merging(), Src.Z());
} else {
if (ElementSize == OpSize) {
@@ -493,7 +515,9 @@ DEF_OP(VAbs) {
DEF_OP(VPopcount) {
const auto Op = IROp->C<IR::IROp_VPopcount>();
const auto OpSize = IROp->Size;
const bool IsScalar = OpSize == 8;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto IsScalar = OpSize == 8;
const auto ElementSize = Op->Header.ElementSize;
@@ -507,17 +531,13 @@ DEF_OP(VPopcount) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && !IsScalar) {
const auto Pred = OpSize == 16 ? PRED_TMP_16B.Merging()
: PRED_TMP_32B.Merging();
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
cnt(SubRegSize, Dst.Z(), Pred, Src.Z());
} else {
if (IsScalar) {
// Scalar
cnt(SubRegSize, Dst.D(), Src.D());
} else {
// Scalar
cnt(SubRegSize, Dst.Q(), Src.Q());
}
}
@@ -528,6 +548,7 @@ DEF_OP(VFAdd) {
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto IsScalar = ElementSize == OpSize;
const auto Dst = GetVReg(Node);
@@ -540,7 +561,7 @@ DEF_OP(VFAdd) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
fadd(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
if (IsScalar) {
@@ -571,21 +592,19 @@ DEF_OP(VFAddP) {
const auto Op = IROp->C<IR::IROp_VFAddP>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
const auto VectorLower = GetVReg(Op->VectorLower.ID());
const auto VectorUpper = GetVReg(Op->VectorUpper.ID());
const bool Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
LOGMAN_THROW_AA_FMT(ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
const auto SubRegSize =
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
@@ -613,6 +632,7 @@ DEF_OP(VFSub) {
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto IsScalar = ElementSize == OpSize;
const auto Dst = GetVReg(Node);
@@ -625,7 +645,7 @@ DEF_OP(VFSub) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
fsub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
if (IsScalar) {
@@ -657,6 +677,7 @@ DEF_OP(VFMul) {
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto IsScalar = ElementSize == OpSize;
const auto Dst = GetVReg(Node);
@@ -669,7 +690,7 @@ DEF_OP(VFMul) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
fmul(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
if (IsScalar) {
@@ -714,7 +735,7 @@ DEF_OP(VFDiv) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Merging();
// SVE VDIV is a destructive operation, so we need a temporary.
@@ -772,9 +793,8 @@ DEF_OP(VFMin) {
//
// * - Not exactly (differs slightly with SNaNs), but close enough for the explanation
if (HostSupportsSVE && !IsScalar) {
const auto Mask = Is256Bit ? PRED_TMP_32B
: PRED_TMP_16B;
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B;
const auto ComparePred = ARMEmitter::PReg::p0;
// General idea:
@@ -843,10 +863,10 @@ DEF_OP(VFMax) {
// NOTE: See VFMin implementation for reasons why we
// don't just use FMAX/FMIN for these implementations.
if (HostSupportsSVE && !IsScalar) {
const auto Mask = Is256Bit ? PRED_TMP_32B
: PRED_TMP_16B;
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B;
const auto ComparePred = ARMEmitter::PReg::p0;
fcmgt(SubRegSize, ComparePred, Mask.Zeroing(),
Vector2.Z(), Vector1.Z());
mov(VTMP1.Z(), Vector1.Z());
@@ -900,9 +920,8 @@ DEF_OP(VFRecp) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
if (HostSupportsSVE && !IsScalar) {
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
: PRED_TMP_16B.Merging();
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
fmov(SubRegSize.Vector, VTMP1.Z(), 1.0);
fdiv(SubRegSize.Vector, VTMP1.Z(), Pred, VTMP1.Z(), Vector.Z());
@@ -951,9 +970,8 @@ DEF_OP(VFSqrt) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && !IsScalar) {
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
: PRED_TMP_16B.Merging();
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
fsqrt(SubRegSize, Dst.Z(), Pred, Vector.Z());
} else {
@@ -998,7 +1016,7 @@ DEF_OP(VFRSqrt) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
fmov(SubRegSize.Vector, VTMP1.Z(), 1.0);
fsqrt(SubRegSize.Vector, VTMP2.Z(), Pred, Vector.Z());
@@ -1052,10 +1070,8 @@ DEF_OP(VNeg) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
: PRED_TMP_16B.Merging();
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
neg(SubRegSize, Dst.Z(), Pred, Vector.Z());
} else {
neg(SubRegSize, Dst.Q(), Vector.Q());
@@ -1078,9 +1094,8 @@ DEF_OP(VFNeg) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
: PRED_TMP_16B.Merging();
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
fneg(SubRegSize, Dst.Z(), Pred, Vector.Z());
} else {
@@ -1097,7 +1112,7 @@ DEF_OP(VNot) {
const auto Vector = GetVReg(Op->Vector.ID());
if (HostSupportsSVE && Is256Bit) {
not_(ARMEmitter::SubRegSize::i8Bit, Dst.Z(), PRED_TMP_32B, Vector.Z());
not_(ARMEmitter::SubRegSize::i8Bit, Dst.Z(), PRED_TMP_32B.Merging(), Vector.Z());
} else {
mvn(ARMEmitter::SubRegSize::i8Bit, Dst.Q(), Vector.Q());
}
@@ -1108,7 +1123,6 @@ DEF_OP(VUMin) {
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto IsScalar = ElementSize == OpSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
@@ -1122,7 +1136,7 @@ DEF_OP(VUMin) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
// SVE UMIN is a destructive operation so we need a temporary.
@@ -1156,7 +1170,6 @@ DEF_OP(VSMin) {
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto IsScalar = ElementSize == OpSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
@@ -1170,7 +1183,7 @@ DEF_OP(VSMin) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
// SVE SMIN is a destructive operation, so we need a temporary.
@@ -1204,7 +1217,6 @@ DEF_OP(VUMax) {
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto IsScalar = ElementSize == OpSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
@@ -1218,7 +1230,7 @@ DEF_OP(VUMax) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
// SVE UMAX is a destructive operation, so we need a temporary.
@@ -1252,7 +1264,6 @@ DEF_OP(VSMax) {
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto IsScalar = ElementSize == OpSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
@@ -1266,7 +1277,7 @@ DEF_OP(VSMax) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Pred = PRED_TMP_32B.Merging();
// SVE SMAX is a destructive operation, so we need a temporary.
@@ -1411,20 +1422,79 @@ DEF_OP(VUnZip2) {
}
}
DEF_OP(VTrn) {
const auto Op = IROp->C<IR::IROp_VTrn>();
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto VectorLower = GetVReg(Op->VectorLower.ID());
const auto VectorUpper = GetVReg(Op->VectorUpper.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) {
trn1(SubRegSize, Dst.Z(), VectorLower.Z(), VectorUpper.Z());
} else {
if (OpSize == 8) {
trn1(SubRegSize, Dst.D(), VectorLower.D(), VectorUpper.D());
} else {
trn1(SubRegSize, Dst.Q(), VectorLower.Q(), VectorUpper.Q());
}
}
}
DEF_OP(VTrn2) {
const auto Op = IROp->C<IR::IROp_VTrn2>();
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto VectorLower = GetVReg(Op->VectorLower.ID());
const auto VectorUpper = GetVReg(Op->VectorUpper.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) {
trn2(SubRegSize, Dst.Z(), VectorLower.Z(), VectorUpper.Z());
} else {
if (OpSize == 8) {
trn2(SubRegSize, Dst.D(), VectorLower.D(), VectorUpper.D());
} else {
trn2(SubRegSize, Dst.Q(), VectorLower.Q(), VectorUpper.Q());
}
}
}
DEF_OP(VBSL) {
const auto Op = IROp->C<IR::IROp_VBSL>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetVReg(Node);
const auto VectorFalse = GetVReg(Op->VectorFalse.ID());
const auto VectorTrue = GetVReg(Op->VectorTrue.ID());
const auto VectorMask = GetVReg(Op->VectorMask.ID());
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
// NOTE: Slight parameter difference from ASIMD
// ASIMD -> BSL Mask, True, False
// SVE -> BSL True, True, False, Mask
mov(VTMP1.Z(), VectorTrue.Z());
movprfx(VTMP1.Z(), VectorTrue.Z());
bsl(VTMP1.Z(), VTMP1.Z(), VectorFalse.Z(), VectorMask.Z());
mov(Dst.Z(), VTMP1.Z());
} else {
@@ -1459,7 +1529,7 @@ DEF_OP(VCMPEQ) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1501,7 +1571,7 @@ DEF_OP(VCMPEQZ) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1543,7 +1613,7 @@ DEF_OP(VCMPGT) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1585,7 +1655,7 @@ DEF_OP(VCMPGTZ) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1623,7 +1693,7 @@ DEF_OP(VCMPLTZ) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1661,7 +1731,7 @@ DEF_OP(VFCMPEQ) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1710,7 +1780,7 @@ DEF_OP(VFCMPNEQ) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1761,7 +1831,7 @@ DEF_OP(VFCMPLT) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1810,7 +1880,7 @@ DEF_OP(VFCMPGT) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1859,7 +1929,7 @@ DEF_OP(VFCMPLE) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1909,7 +1979,7 @@ DEF_OP(VFCMPORD) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -1970,7 +2040,7 @@ DEF_OP(VFCMPUNO) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
if (HostSupportsSVE && Is256Bit && !IsScalar) {
if (HostSupportsSVE && Is256Bit) {
const auto Mask = PRED_TMP_32B.Zeroing();
const auto ComparePred = ARMEmitter::PReg::p0;
@@ -2218,10 +2288,10 @@ DEF_OP(VInsElement) {
dup(SubRegSize, VTMP2.Z(), SrcVector.Z(), SrcIdx);
mov(Dst.Z(), Reg.Z());
if (ElementSize == 16) {
mov(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Predicate, VTMP2.Z());
mov(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Predicate.Merging(), VTMP2.Z());
}
else {
mov(SubRegSize, Dst.Z(), Predicate, VTMP2.Z());
mov(SubRegSize, Dst.Z(), Predicate.Merging(), VTMP2.Z());
}
// Set up a label to jump over the data we inserted, so we don't try and execute it.
@@ -2456,14 +2526,13 @@ DEF_OP(VUShrNI2) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && Is256Bit) {
mov(VTMP1.Z(), VectorLower.Z());
const auto Mask = PRED_TMP_16B;
shrnb(SubRegSize, VTMP2.Z(), VectorUpper.Z(), BitShift);
uzp1(SubRegSize, VTMP2.Z(), VTMP2.Z(), VTMP2.Z());
splice<ARMEmitter::OpType::Destructive>(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
mov(Dst.Z(), VTMP1.Z());
movprfx(Dst.Z(), VectorLower.Z());
splice<ARMEmitter::OpType::Destructive>(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
} else {
mov(VTMP1.Q(), VectorLower.Q());
shrn2(SubRegSize, VTMP1.Q(), VectorUpper.Q(), BitShift);
@@ -2560,7 +2629,7 @@ DEF_OP(VUXTL2) {
if (HostSupportsSVE && Is256Bit) {
uunpkhi(SubRegSize, Dst.Z(), Vector.Z());
} else {
uxtl2(SubRegSize, Dst.D(), Vector.D());
uxtl2(SubRegSize, Dst.Q(), Vector.Q());
}
}
@@ -2639,12 +2708,6 @@ DEF_OP(VSQXTN2) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE && Is256Bit) {
// Need to use the destructive variant of SPLICE, since
// the constructive variant requires a register list, and
// we can't guarantee VectorLower and VectorUpper will always
// have consecutive indexes with one another.
mov(VTMP1.Z(), VectorLower.Z());
// We use the 16 byte mask due to how SPLICE works. We only
// want to get at the first 16 bytes in the lower vector, so
// that SPLICE will then begin copying the first 16 bytes
@@ -2654,20 +2717,23 @@ DEF_OP(VSQXTN2) {
sqxtnb(SubRegSize, VTMP2.Z(), VectorUpper.Z());
uzp1(SubRegSize, VTMP2.Z(), VTMP2.Z(), VTMP2.Z());
splice<ARMEmitter::OpType::Destructive>(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
mov(Dst.Z(), VTMP1.Z());
// Need to use the destructive variant of SPLICE, since
// the constructive variant requires a register list, and
// we can't guarantee VectorLower and VectorUpper will always
// have consecutive indexes with one another.
movprfx(Dst.Z(), VectorLower.Z());
splice<ARMEmitter::OpType::Destructive>(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
} else {
mov(VTMP1.Q(), VectorLower.Q());
if (OpSize == 8) {
sqxtn(SubRegSize, VTMP2, VectorUpper);
ins(ARMEmitter::SubRegSize::i32Bit, VTMP1, 1, VTMP2, 0);
mov(Dst.Q(), VectorLower.Q());
ins(ARMEmitter::SubRegSize::i32Bit, Dst, 1, VTMP2, 0);
} else {
mov(VTMP1.Q(), VectorLower.Q());
sqxtn2(SubRegSize, VTMP1, VectorUpper);
mov(Dst.Q(), VTMP1.Q());
}
mov(Dst.Q(), VTMP1.Q());
}
}
@@ -2716,30 +2782,31 @@ DEF_OP(VSQXTUN2) {
// NOTE: See VSQXTN2 implementation for an in-depth explanation
// of everything going on here.
mov(VTMP1.Z(), VectorLower.Z());
const auto Mask = PRED_TMP_16B;
sqxtunb(SubRegSize, VTMP2.Z(), VectorUpper.Z());
uzp1(SubRegSize, VTMP2.Z(), VTMP2.Z(), VTMP2.Z());
splice<ARMEmitter::OpType::Destructive>(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
mov(Dst.Z(), VTMP1.Z());
movprfx(Dst.Z(), VectorLower.Z());
splice<ARMEmitter::OpType::Destructive>(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
} else {
mov(VTMP1.Q(), VectorLower.Q());
if (OpSize == 8) {
sqxtun(SubRegSize, VTMP2, VectorUpper);
ins(ARMEmitter::SubRegSize::i32Bit, VTMP1, 1, VTMP2, 0);
mov(Dst.Q(), VectorLower.Q());
ins(ARMEmitter::SubRegSize::i32Bit, Dst, 1, VTMP2, 0);
} else {
mov(VTMP1.Q(), VectorLower.Q());
sqxtun2(SubRegSize, VTMP1, VectorUpper);
mov(Dst.Q(), VTMP1.Q());
}
mov(Dst.Q(), VTMP1.Q());
}
}
DEF_OP(VMul) {
const auto Op = IROp->C<IR::IROp_VUMul>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto ElementSize = Op->Header.ElementSize;
const auto Dst = GetVReg(Node);
@@ -2753,7 +2820,7 @@ DEF_OP(VMul) {
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
if (HostSupportsSVE) {
if (HostSupportsSVE && Is256Bit) {
mul(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
} else {
mul(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
@@ -2905,7 +2972,7 @@ DEF_OP(VTBL1) {
switch (OpSize) {
case 8: {
tbl(Dst.D(), VectorTable.D(), VectorIndices.D());
tbl(Dst.D(), VectorTable.Q(), VectorIndices.D());
break;
}
case 16: {
+5 -5
View File
@@ -3,7 +3,7 @@
#include <memory>
namespace FEXCore::Context {
struct Context;
class ContextImpl;
}
namespace FEXCore::Core {
@@ -13,14 +13,14 @@ struct InternalThreadState;
namespace FEXCore::CPU {
class CPUBackend;
[[nodiscard]] std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx,
[[nodiscard]] std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::ContextImpl *ctx,
FEXCore::Core::InternalThreadState *Thread);
void InitializeX86JITSignalHandlers(FEXCore::Context::Context *CTX);
void InitializeX86JITSignalHandlers(FEXCore::Context::ContextImpl *CTX);
CPUBackendFeatures GetX86JITBackendFeatures();
[[nodiscard]] std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx,
[[nodiscard]] std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::ContextImpl *ctx,
FEXCore::Core::InternalThreadState *Thread);
void InitializeArm64JITSignalHandlers(FEXCore::Context::Context *CTX);
void InitializeArm64JITSignalHandlers(FEXCore::Context::ContextImpl *CTX);
CPUBackendFeatures GetArm64JITBackendFeatures();
} // namespace FEXCore::CPU
@@ -30,22 +30,6 @@ $end_info$
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
}
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) {
// Adjust the stack first for a regular return
if (SpillSlots) {
@@ -218,7 +202,7 @@ DEF_OP(Thunk) {
mov(rdi, GetSrc<RA_64>(Op->ArgPtr.ID()));
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
auto thunkFn = static_cast<Context::ContextImpl*>(ThreadState->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
mov(rax, reinterpret_cast<uintptr_t>(thunkFn));
call(rax);
@@ -321,7 +305,6 @@ DEF_OP(CPUID) {
#undef DEF_OP
void X86JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(SIGNALRETURN, SignalReturn);
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
REGISTER_OP(EXITFUNCTION, ExitFunction);
REGISTER_OP(JUMP, Jump);
@@ -111,6 +111,53 @@ DEF_OP(VCastFromGPR) {
}
}
DEF_OP(VDupFromGPR) {
const auto Op = IROp->C<IR::IROp_VDupFromGPR>();
const auto OpSize = IROp->Size;
const auto ElementSize = IROp->ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto Src = GetSrc<RA_64>(Op->Src.ID()).cvt64();
vmovq(Dst, Src);
switch (ElementSize) {
case 1:
if (Is256Bit) {
vpbroadcastb(ToYMM(Dst), Dst);
} else {
vpbroadcastb(Dst, Dst);
}
break;
case 2:
if (Is256Bit) {
vpbroadcastw(ToYMM(Dst), Dst);
} else {
vpbroadcastw(Dst, Dst);
}
break;
case 4:
if (Is256Bit) {
vpbroadcastd(ToYMM(Dst), Dst);
} else {
vpbroadcastd(Dst, Dst);
}
break;
case 8:
if (Is256Bit) {
vpbroadcastq(ToYMM(Dst), Dst);
} else {
vpbroadcastq(Dst, Dst);
}
break;
default:
LOGMAN_MSG_A_FMT("Unhandled element size: {}", ElementSize);
return;
}
}
DEF_OP(Float_FromGPR_S) {
const auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
@@ -357,6 +404,7 @@ void X86JITCore::RegisterConversionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VINSGPR, VInsGPR);
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
REGISTER_OP(VDUPFROMGPR, VDupFromGPR);
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
REGISTER_OP(FLOAT_FTOF, Float_FToF);
REGISTER_OP(VECTOR_STOF, Vector_SToF);
@@ -21,23 +21,67 @@ DEF_OP(AESImc) {
}
DEF_OP(AESEnc) {
auto Op = IROp->C<IR::IROp_VAESEnc>();
vaesenc(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
const auto Op = IROp->C<IR::IROp_VAESEnc>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto Key = GetSrc(Op->Key.ID());
const auto State = GetSrc(Op->State.ID());
if (Is256Bit) {
vaesenc(ToYMM(Dst), ToYMM(State), ToYMM(Key));
} else {
vaesenc(Dst, State, Key);
}
}
DEF_OP(AESEncLast) {
auto Op = IROp->C<IR::IROp_VAESEncLast>();
vaesenclast(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
const auto Op = IROp->C<IR::IROp_VAESEncLast>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto Key = GetSrc(Op->Key.ID());
const auto State = GetSrc(Op->State.ID());
if (Is256Bit) {
vaesenclast(ToYMM(Dst), ToYMM(State), ToYMM(Key));
} else {
vaesenclast(Dst, State, Key);
}
}
DEF_OP(AESDec) {
auto Op = IROp->C<IR::IROp_VAESDec>();
vaesdec(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
const auto Op = IROp->C<IR::IROp_VAESDec>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto Key = GetSrc(Op->Key.ID());
const auto State = GetSrc(Op->State.ID());
if (Is256Bit) {
vaesdec(ToYMM(Dst), ToYMM(State), ToYMM(Key));
} else {
vaesdec(Dst, State, Key);
}
}
DEF_OP(AESDecLast) {
auto Op = IROp->C<IR::IROp_VAESDecLast>();
vaesdeclast(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
const auto Op = IROp->C<IR::IROp_VAESDecLast>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto Key = GetSrc(Op->Key.ID());
const auto State = GetSrc(Op->State.ID());
if (Is256Bit) {
vaesdeclast(ToYMM(Dst), ToYMM(State), ToYMM(Key));
} else {
vaesdeclast(Dst, State, Key);
}
}
DEF_OP(AESKeyGenAssist) {
@@ -76,18 +120,24 @@ DEF_OP(CRC32) {
}
DEF_OP(PCLMUL) {
auto Op = IROp->C<IR::IROp_PCLMUL>();
const auto Op = IROp->C<IR::IROp_PCLMUL>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
auto Dst = GetDst(Node);
auto Src1 = GetSrc(Op->Src1.ID());
auto Src2 = GetSrc(Op->Src2.ID());
const auto Dst = GetDst(Node);
const auto Src1 = GetSrc(Op->Src1.ID());
const auto Src2 = GetSrc(Op->Src2.ID());
switch (Op->Selector) {
case 0b00000000:
case 0b00000001:
case 0b00010000:
case 0b00010001:
vpclmulqdq(Dst, Src1, Src2, Op->Selector);
if (Is256Bit) {
vpclmulqdq(ToYMM(Dst), ToYMM(Src1), ToYMM(Src2), Op->Selector);
} else {
vpclmulqdq(Dst, Src1, Src2, Op->Selector);
}
break;
default:
LOGMAN_MSG_A_FMT("Unknown PCLMUL selector: {}", Op->Selector);
+46 -15
View File
@@ -330,7 +330,7 @@ static uint64_t X86JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame,
}
auto LinkerAddress = Frame->Pointers.Common.ExitFunctionLinker;
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
Context::ContextImpl::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
// undo the link
record[0] = LinkerAddress;
});
@@ -342,7 +342,7 @@ static uint64_t X86JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame,
void X86JITCore::Op_NoOp(IR::IROp_Header *IROp, IR::NodeID Node) {
}
X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
X86JITCore::X86JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread)
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
, CodeGenerator(0, this, nullptr) // this is not used here
, CTX {ctx} {
@@ -379,7 +379,7 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadRemoveCodeEntryFromJit);
Common.CPUIDObj = reinterpret_cast<uint64_t>(&CTX->CPUID);
{
@@ -389,7 +389,7 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
Common.SyscallHandlerObj = reinterpret_cast<uint64_t>(CTX->SyscallHandler);
Common.SyscallHandlerFunc = reinterpret_cast<uint64_t>(FEXCore::Context::HandleSyscall);
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::Context::ThreadExitFunctionLink<X86JITCore_ExitFunctionLink>);
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadExitFunctionLink<X86JITCore_ExitFunctionLink>);
// Fill in the fallback handlers
InterpreterOps::FillFallbackIndexPointers(Common.FallbackHandlerPointers);
@@ -399,9 +399,9 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
ClearCache();
}
void X86JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
void X86JITCore::InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
return Thread->CTX->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
return static_cast<Context::ContextImpl*>(Thread->CTX)->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
}, true);
}
@@ -587,7 +587,7 @@ std::tuple<X86JITCore::SetCC, X86JITCore::CMovCC, X86JITCore::JCC> X86JITCore::G
return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
}
void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
CPUBackend::CompiledCode X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
FEXCORE_PROFILE_SCOPED("x86::CompileCode");
JumpTargets.clear();
@@ -603,12 +603,27 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
CTX->ClearCodeCache(ThreadState);
}
GuestEntry = getCurr<uint8_t*>();
CodeData.BlockBegin = getCurr<uint8_t*>();
// Put the code header at the start of the data block.
Label JITCodeHeaderLabel{};
L(JITCodeHeaderLabel);
JITCodeHeader *CodeHeader = getCurr<JITCodeHeader *>();
setSize(getSize() + sizeof(JITCodeHeader));
CodeData.BlockEntry = getCurr<uint8_t*>();
// Get the address of the JITCodeHeader and store in to the core state.
// Only two instructions, so very low overhead.
lea(TMP1, ptr [rip + JITCodeHeaderLabel]);
mov(qword [STATE + offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader)], TMP1);
CursorEntry = getSize();
this->IR = IR;
if (GDBEnabled) {
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(GuestEntry, Entry);
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(CodeData.BlockBegin, Entry);
setSize(getSize() + GDBSize);
}
@@ -731,7 +746,7 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
if (DebugData) {
DebugData->Subblocks.push_back({
static_cast<uint32_t>(BlockStartHostCode - GuestEntry),
static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockBegin),
static_cast<uint32_t>(getCurr<uint8_t *>() - BlockStartHostCode)
});
}
@@ -744,20 +759,36 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
}
PendingTargetLabel = nullptr;
void *GuestExit = getCurr<void*>();
// Add the JitCodeTail
auto JITBlockTailLocation = getCurr<uint8_t *>();
auto JITBlockTail = getCurr<JITCodeTail*>();
setSize(getSize() + sizeof(JITCodeTail));
// Put the block's RIP entry in the tail.
// This will be used for RIP reconstruction in the future.
// TODO: This needs to be a data RIP relocation once code caching works.
// Current relocation code doesn't support this feature yet.
JITBlockTail->RIP = Entry;
CodeHeader->OffsetToBlockTail = JITBlockTailLocation - CodeData.BlockBegin;
CodeData.Size = getCurr<uint8_t*>() - CodeData.BlockBegin;
JITBlockTail->Size = CodeData.Size;
this->IR = nullptr;
ready();
if (DebugData) {
DebugData->HostCodeSize = reinterpret_cast<uintptr_t>(GuestExit) - reinterpret_cast<uintptr_t>(GuestEntry);
DebugData->HostCodeSize = CodeData.Size;
DebugData->Relocations = &Relocations;
}
return GuestEntry;
return CodeData;
}
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
return std::make_unique<X86JITCore>(ctx, Thread);
}
@@ -765,7 +796,7 @@ CPUBackendFeatures GetX86JITBackendFeatures() {
return CPUBackendFeatures { };
}
void InitializeX86JITSignalHandlers(FEXCore::Context::Context *CTX) {
void InitializeX86JITSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
X86JITCore::InitializeSignalHandlers(CTX);
}
@@ -51,13 +51,13 @@ const std::array<Xbyak::Xmm, 11> RAXMM_x = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
class X86JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
public:
explicit X86JITCore(FEXCore::Context::Context *ctx,
explicit X86JITCore(FEXCore::Context::ContextImpl *ctx,
FEXCore::Core::InternalThreadState *Thread);
~X86JITCore() override;
[[nodiscard]] std::string GetName() override { return "JIT"; }
[[nodiscard]] void *CompileCode(uint64_t Entry,
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
@@ -68,7 +68,7 @@ public:
void ClearCache() override;
static void InitializeSignalHandlers(FEXCore::Context::Context *CTX);
static void InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX);
void ClearRelocations() override { Relocations.clear(); }
@@ -135,9 +135,10 @@ private:
/** @} */
Label* PendingTargetLabel{};
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
FEXCore::IR::IRListView const *IR;
uint64_t Entry;
CPUBackend::CompiledCode CodeData{};
std::unordered_map<IR::NodeID, Label> JumpTargets;
Xbyak::util::Cpu Features{};
@@ -205,10 +206,6 @@ private:
void EmitDetectionString();
uint32_t SpillSlots{};
/**
* @brief Current guest RIP entrypoint
*/
uint8_t *GuestEntry{};
using SetCC = void (X86JITCore::*)(const Operand& op);
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
@@ -308,7 +305,6 @@ private:
DEF_OP(AtomicFetchNeg);
///< Branch ops
DEF_OP(SignalReturn);
DEF_OP(CallbackReturn);
DEF_OP(ExitFunction);
DEF_OP(Jump);
@@ -322,6 +318,7 @@ private:
///< Conversion ops
DEF_OP(VInsGPR);
DEF_OP(VCastFromGPR);
DEF_OP(VDupFromGPR);
DEF_OP(Float_FromGPR_S);
DEF_OP(Float_FToF);
DEF_OP(Vector_UToF);
@@ -347,6 +344,7 @@ private:
DEF_OP(StoreFlag);
DEF_OP(LoadMem);
DEF_OP(StoreMem);
DEF_OP(MemSet);
DEF_OP(CacheLineClear);
DEF_OP(CacheLineClean);
DEF_OP(CacheLineZero);
@@ -409,6 +407,8 @@ private:
DEF_OP(VZip2);
DEF_OP(VUnZip);
DEF_OP(VUnZip2);
DEF_OP(VTrn);
DEF_OP(VTrn2);
DEF_OP(VBSL);
DEF_OP(VCMPEQ);
DEF_OP(VCMPEQZ);
@@ -766,6 +766,94 @@ DEF_OP(StoreMem) {
}
}
DEF_OP(MemSet) {
const auto Op = IROp->C<IR::IROp_MemSet>();
const int32_t Size = Op->Size;
const auto MemReg = GetSrc<RA_64>(Op->Addr.ID());
const auto Value = GetSrc<RA_64>(Op->Value.ID());
const auto Length = GetSrc<RA_64>(Op->Length.ID());
const auto Direction = GetSrc<RA_64>(Op->Direction.ID());
const auto Dst = GetSrc<RA_64>(Node);
// If Direction == 0 then:
// MemReg is incremented (by size)
// else:
// MemReg is decremented (by size)
//
// Counter is decremented regardless.
// TMP1 = rax
// TMP2 = rcx
// TMP4 = rdi
// That leaves us with TMP3 and TMP5
mov(rax, Value);
mov(rcx, Length);
mov(rdi, MemReg);
{
mov(TMP3, Length);
auto CalculateDest = [&]() {
mov(Dst, MemReg);
switch (Size) {
case 1:
break;
case 2:
shl(TMP3, 1);
break;
case 4:
shl(TMP3, 2);
break;
case 8:
shl(TMP3, 3);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
};
Label AfterDir;
Label BackwardDir;
cmp(Direction, 0);
jne(BackwardDir);
// Incrementing DF flag.
cld();
CalculateDest();
add(Dst, TMP3);
jmp(AfterDir);
L(BackwardDir);
// Decrementing DF flag.
std();
CalculateDest();
sub(Dst, TMP3);
L(AfterDir);
}
switch (Size) {
case 1:
rep(); stosb();
break;
case 2:
rep(); stosw();
break;
case 4:
rep(); stosd();
break;
case 8:
rep(); stosq();
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
// Ensure we set DF back to zero. Required by the ABI.
cld();
}
DEF_OP(CacheLineClear) {
auto Op = IROp->C<IR::IROp_CacheLineClear>();
@@ -820,6 +908,7 @@ void X86JITCore::RegisterMemoryHandlers() {
REGISTER_OP(STOREMEM, StoreMem);
REGISTER_OP(LOADMEMTSO, LoadMem);
REGISTER_OP(STOREMEMTSO, StoreMem);
REGISTER_OP(MEMSET, MemSet);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
@@ -24,7 +24,7 @@ namespace FEXCore::CPU {
DEF_OP(GuestOpcode) {
auto Op = IROp->C<IR::IROp_GuestOpcode>();
// metadata
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, getCurr<uint8_t*>() - GuestEntry});
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, getCurr<uint8_t*>() - CodeData.BlockBegin});
}
DEF_OP(Fence) {
@@ -1944,7 +1944,7 @@ DEF_OP(VUnZip2) {
}
case 8: {
if (Is256Bit) {
vshufpd(ToYMM(Dst), ToYMM(VectorLower), ToYMM(VectorUpper), 0b1'1);
vshufpd(ToYMM(Dst), ToYMM(VectorLower), ToYMM(VectorUpper), 0b11'11);
vpermq(ToYMM(Dst), ToYMM(Dst), 0b11'01'10'00);
} else {
vshufpd(Dst, VectorLower, VectorUpper, 0b1'1);
@@ -1958,6 +1958,191 @@ DEF_OP(VUnZip2) {
}
}
DEF_OP(VTrn) {
const auto Op = IROp->C<IR::IROp_VTrn>();
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto VectorLower = GetSrc(Op->VectorLower.ID());
const auto VectorUpper = GetSrc(Op->VectorUpper.ID());
const auto LoadPshufbReg = [&](Xbyak::Xmm reg, uint64_t lower) {
mov(rax, lower);
mov(rcx, 0x80'80'80'80'80'80'80'80);
vmovq(reg, rax);
pinsrq(reg, rcx, 1);
};
switch (ElementSize) {
case 1: {
LoadPshufbReg(xmm15, 0x0E'0C'0A'08'06'04'02'00);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpcklbw(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpcklbw(Dst, xmm14, xmm13);
}
break;
}
case 2: {
LoadPshufbReg(xmm15, 0x0D'0C'09'08'05'04'01'00);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpcklwd(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpcklwd(Dst, xmm14, xmm13);
}
break;
}
case 4: {
LoadPshufbReg(xmm15, 0x0B'0A'09'08'03'02'01'00);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpckldq(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpckldq(Dst, xmm14, xmm13);
}
break;
}
case 8: {
LoadPshufbReg(xmm15, 0x07'06'05'04'03'02'01'00);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpcklqdq(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpcklqdq(Dst, xmm14, xmm13);
}
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
return;
}
}
DEF_OP(VTrn2) {
const auto Op = IROp->C<IR::IROp_VTrn2>();
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto VectorLower = GetSrc(Op->VectorLower.ID());
const auto VectorUpper = GetSrc(Op->VectorUpper.ID());
const auto LoadPshufbReg = [&](Xbyak::Xmm reg, uint64_t lower) {
mov(rax, lower);
mov(rcx, 0x80'80'80'80'80'80'80'80);
vmovq(reg, rax);
pinsrq(reg, rcx, 1);
};
switch (ElementSize) {
case 1: {
LoadPshufbReg(xmm15, 0x0F'0D'0B'09'07'05'03'01);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpcklbw(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpcklbw(Dst, xmm14, xmm13);
}
break;
}
case 2: {
LoadPshufbReg(xmm15, 0x0F'0E'0B'0A'07'06'03'02);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpcklwd(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpcklwd(Dst, xmm14, xmm13);
}
break;
}
case 4: {
LoadPshufbReg(xmm15, 0x0F'0E'0D'0C'07'06'05'04);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpckldq(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpckldq(Dst, xmm14, xmm13);
}
break;
}
case 8: {
LoadPshufbReg(xmm15, 0x0F'0E'0D'0C'0B'0A'09'08);
if (Is256Bit) {
vinserti128(ymm15, ymm15, xmm15, 1);
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
vpunpcklqdq(ToYMM(Dst), ymm14, ymm13);
} else {
vpshufb(xmm14, VectorLower, xmm15);
vpshufb(xmm13, VectorUpper, xmm15);
vpunpcklqdq(Dst, xmm14, xmm13);
}
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
return;
}
}
DEF_OP(VBSL) {
const auto Op = IROp->C<IR::IROp_VBSL>();
@@ -3147,6 +3332,23 @@ DEF_OP(VShlI) {
const auto Vector = GetSrc(Op->Vector.ID());
switch (ElementSize) {
case 1: {
const auto Mask = 0xFFU >> BitShift;
mov(rax, Mask);
vmovq(xmm15, rax);
if (Is256Bit) {
vpsllw(ToYMM(Dst), ToYMM(Vector), BitShift);
vpbroadcastb(ymm15, xmm15);
vpand(ToYMM(Dst), ToYMM(Dst), ymm15);
} else {
vpsllw(Dst, Vector, BitShift);
vpbroadcastb(xmm15, xmm15);
vpand(Dst, Dst, ymm15);
}
break;
}
case 2: {
if (Is256Bit) {
vpsllw(ToYMM(Dst), ToYMM(Vector), BitShift);
@@ -4304,6 +4506,8 @@ void X86JITCore::RegisterVectorHandlers() {
REGISTER_OP(VZIP2, VZip2);
REGISTER_OP(VUNZIP, VUnZip);
REGISTER_OP(VUNZIP2, VUnZip2);
REGISTER_OP(VTRN, VTrn);
REGISTER_OP(VTRN2, VTrn2);
REGISTER_OP(VBSL, VBSL);
REGISTER_OP(VCMPEQ, VCMPEQ);
REGISTER_OP(VCMPEQZ, VCMPEQZ);
+1 -1
View File
@@ -14,7 +14,7 @@ $end_info$
#include <sys/mman.h>
namespace FEXCore {
LookupCache::LookupCache(FEXCore::Context::Context *CTX)
LookupCache::LookupCache(FEXCore::Context::ContextImpl *CTX)
: ctx {CTX} {
TotalCacheSize = ctx->Config.VirtualMemSize / 4096 * 8 + CODE_SIZE + L1_SIZE;
+3 -5
View File
@@ -1,4 +1,5 @@
#pragma once
#include "Interface/Context/Context.h"
#include <FEXCore/Utils/LogManager.h>
#include <cstdint>
@@ -12,9 +13,6 @@
#include <tsl/robin_map.h>
namespace FEXCore {
namespace Context {
struct Context;
}
class LookupCache {
public:
@@ -24,7 +22,7 @@ public:
uintptr_t GuestCode;
};
LookupCache(FEXCore::Context::Context *CTX);
LookupCache(FEXCore::Context::ContextImpl *CTX);
~LookupCache();
uintptr_t FindBlock(uint64_t Address) {
@@ -260,7 +258,7 @@ private:
size_t AllocateOffset {};
FEXCore::Context::Context *ctx;
FEXCore::Context::ContextImpl *ctx;
uint64_t VirtualMemSize{};
};
}
@@ -4,7 +4,7 @@
#include <FEXCore/Config/Config.h>
namespace FEXCore::CodeSerialize {
NamedRegionObjectHandler::NamedRegionObjectHandler(FEXCore::Context::Context *ctx) {
NamedRegionObjectHandler::NamedRegionObjectHandler(FEXCore::Context::ContextImpl *ctx) {
DefaultSerializationConfig.Cookie = CODE_COOKIE;
// Initialize the Arch from CPUID
@@ -13,7 +13,7 @@ namespace {
}
namespace FEXCore::CodeSerialize {
CodeObjectSerializeService::CodeObjectSerializeService(FEXCore::Context::Context *ctx)
CodeObjectSerializeService::CodeObjectSerializeService(FEXCore::Context::ContextImpl *ctx)
: CTX {ctx}
, AsyncHandler { &NamedRegionHandler , this }
, NamedRegionHandler { ctx } {
@@ -253,7 +253,7 @@ namespace FEXCore::CodeSerialize {
class NamedRegionObjectHandler final {
public:
NamedRegionObjectHandler(FEXCore::Context::Context *ctx);
NamedRegionObjectHandler(FEXCore::Context::ContextImpl *ctx);
void HandleNamedRegionObjectJobs();
@@ -338,7 +338,7 @@ namespace FEXCore::CodeSerialize {
*/
class CodeObjectSerializeService final {
public:
CodeObjectSerializeService(FEXCore::Context::Context *ctx);
CodeObjectSerializeService(FEXCore::Context::ContextImpl *ctx);
/**
* @brief Initialize the internal interface
@@ -440,7 +440,7 @@ namespace FEXCore::CodeSerialize {
void NotifyWork() { WorkAvailable.NotifyOne(); }
private:
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
Event WorkAvailable{};
std::unique_ptr<FEXCore::Threads::Thread> WorkerThread;
+99 -218
View File
@@ -277,18 +277,6 @@ void OpDispatchBuilder::IRETOp(OpcodeArgs) {
BlockSetRIP = true;
}
void OpDispatchBuilder::SIGRETOp(OpcodeArgs) {
uint8_t Literal = Op->Src[0].Data.Literal.Value;
const uint8_t GPRSize = CTX->GetGPRSize();
// Store the new RIP
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);
BlockSetRIP = true;
}
void OpDispatchBuilder::CallbackReturnOp(OpcodeArgs) {
const uint8_t GPRSize = CTX->GetGPRSize();
// Store the new RIP
@@ -876,7 +864,7 @@ OrderedNode *OpDispatchBuilder::SelectCC(uint8_t OP, OrderedNode *TrueValue, Ord
case 0x7: { // JA - Jump if CF == 0 && ZF == 0
auto Flag1 = GetRFLAG(FEXCore::X86State::RFLAG_ZF_LOC);
auto Flag2 = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC);
auto Check = _Or(Flag1, _Lshl(Flag2, _Constant(1)));
auto Check = _Or(Flag1, Flag2);
SrcCond = _Select(FEXCore::IR::COND_EQ,
Check, ZeroConst, TrueValue, FalseValue);
break;
@@ -3805,73 +3793,21 @@ void OpDispatchBuilder::STOSOp(OpcodeArgs) {
StoreGPRRegister(X86State::REG_RDI, TailDest);
}
else {
// Calculate deffered flags.
// This block is ending and it needs flag status
CalculateDeferredFlags();
// FEX doesn't support partial faulting REP instructions.
// Converting this to a `MemSet` IR op optimizes this quite significantly in our codegen.
// If FEX is to gain support for faulting REP instructions, then this implementation needs to change significantly.
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Dest = LoadGPRRegister(X86State::REG_RDI);
// Create all our blocks
auto LoopHead = CreateNewCodeBlockAfter(GetCurrentBlock());
auto LoopTail = CreateNewCodeBlockAfter(LoopHead);
auto LoopEnd = CreateNewCodeBlockAfter(LoopTail);
// Only ES prefix
auto Segment = GetSegment(0, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
// At the time this was written, our RA can't handle accessing nodes across blocks.
// So we need to re-load and re-calculate essential values each iteration of the loop.
// First thing we need to do is finish this block and jump to the start of the loop.
// RA can now better allocate things, move these ops before the header, to avoid accessing
// DF on every iteration
auto SizeConst = _Constant(Size);
auto NegSizeConst = _Constant(-Size);
// Calculate direction.
OrderedNode *Counter = LoadGPRRegister(X86State::REG_RCX);
auto DF = GetRFLAG(FEXCore::X86State::RFLAG_DF_LOC);
auto PtrDir = _Select(FEXCore::IR::COND_EQ,
DF, _Constant(0),
SizeConst, NegSizeConst);
_Jump(LoopHead);
SetCurrentCodeBlock(LoopHead);
{
OrderedNode *Counter = LoadGPRRegister(X86State::REG_RCX);
// Can we end the block?
_CondJump(Counter, LoopEnd, LoopTail, {COND_EQ});
}
SetCurrentCodeBlock(LoopTail);
{
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Dest = LoadGPRRegister(X86State::REG_RDI);
// Only ES prefix
Dest = AppendSegmentOffset(Dest, 0, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
// Store to memory where RDI points
_StoreMemAutoTSO(GPRClass, Size, Dest, Src, Size);
OrderedNode *TailCounter = LoadGPRRegister(X86State::REG_RCX);
OrderedNode *TailDest = LoadGPRRegister(X86State::REG_RDI);
// Decrement counter
TailCounter = _Sub(TailCounter, _Constant(1));
// Store the counter so we don't have to deal with PHI here
StoreGPRRegister(X86State::REG_RCX, TailCounter);
// Offset the pointer
TailDest = _Add(TailDest, PtrDir);
StoreGPRRegister(X86State::REG_RDI, TailDest);
// Jump back to the start, we have more work to do
_Jump(LoopHead);
}
// Make sure to start a new block after ending this one
SetCurrentCodeBlock(LoopEnd);
auto Result = _MemSet(CTX->IsTSOEnabled(), Size, Segment ?: InvalidNode, Dest, Src, Counter, DF);
StoreGPRRegister(X86State::REG_RCX, _Constant(0));
StoreGPRRegister(X86State::REG_RDI, Result);
}
}
@@ -4844,20 +4780,19 @@ uint32_t OpDispatchBuilder::GetDstBitSize(X86Tables::DecodedOp Op) const {
return GetDstSize(Op) * 8;
}
OrderedNode *OpDispatchBuilder::AppendSegmentOffset(OrderedNode *Value, uint32_t Flags, uint32_t DefaultPrefix, bool Override) {
OrderedNode *OpDispatchBuilder::GetSegment(uint32_t Flags, uint32_t DefaultPrefix, bool Override) {
const uint8_t GPRSize = CTX->GetGPRSize();
if (CTX->Config.Is64BitMode) {
if (Flags & FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX) {
Value = _Add(Value, _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached)));
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached));
}
else if (Flags & FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX) {
Value = _Add(Value, _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached)));
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached));
}
// If there was any other segment in 64bit then it is ignored
}
else {
OrderedNode *Segment{};
uint32_t Prefix = Flags & FEXCore::X86Tables::DecodeFlags::FLAG_SEGMENTS;
if (!Prefix || Override) {
// If there was no prefix then use the default one if available
@@ -4867,29 +4802,28 @@ OrderedNode *OpDispatchBuilder::AppendSegmentOffset(OrderedNode *Value, uint32_t
// With the segment register optimization we store the GDT bases directly in the segment register to remove indexed loads
switch (Prefix) {
case FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX:
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, es_cached));
break;
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, es_cached));
case FEXCore::X86Tables::DecodeFlags::FLAG_CS_PREFIX:
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, cs_cached));
break;
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, cs_cached));
case FEXCore::X86Tables::DecodeFlags::FLAG_SS_PREFIX:
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ss_cached));
break;
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ss_cached));
case FEXCore::X86Tables::DecodeFlags::FLAG_DS_PREFIX:
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ds_cached));
break;
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ds_cached));
case FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX:
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached));
break;
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached));
case FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX:
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached));
break;
default: break; // Do nothing
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached));
default:
break; // Do nothing
}
}
return nullptr;
}
if (Segment) {
Value = _Add(Value, Segment);
}
OrderedNode *OpDispatchBuilder::AppendSegmentOffset(OrderedNode *Value, uint32_t Flags, uint32_t DefaultPrefix, bool Override) {
auto Segment = GetSegment(Flags, DefaultPrefix, Override);
if (Segment) {
Value = _Add(Value, Segment);
}
return Value;
@@ -4957,21 +4891,9 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
else if (gpr >= FEXCore::X86State::REG_XMM_0) {
const auto gprIndex = gpr - X86State::REG_XMM_0;
const auto regSize = CTX->HostFeatures.SupportsAVX ?
Core::CPUState::XMM_AVX_REG_SIZE :
Core::CPUState::XMM_SSE_REG_SIZE;
// Load the full register size if it is a XMM register source.
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.
// TODO: Have the instruction doing this load do the extract instead of here.
// We don't have enough information here to know if we can avoid this dup.
if (highIndex && OpSize < Core::CPUState::XMM_SSE_REG_SIZE) {
Src = _VDupElement(regSize, OpSize, Src, 1);
}
// Now extract the subregister if it was a partial load /smaller/ than SSE size
// TODO: Instead of doing the VMov implicitly on load, hunt down all use cases that require partial loads and do it after load.
// We don't have information here to know if the operation needs zero upper bits or can contain data.
@@ -5173,28 +5095,23 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
}
else if (gpr >= FEXCore::X86State::REG_XMM_0) {
const auto gprIndex = gpr - X86State::REG_XMM_0;
const auto highIndex = Operand.Data.GPR.HighBits ? 1 : 0;
const auto VectorSize = CTX->HostFeatures.SupportsAVX ? 32 : 16;
auto Result = Src;
if (highIndex || OpSize != VectorSize) {
// Partial writes can come from GPR or FPR.
if (OpSize != VectorSize) {
// Partial writes can come from FPRs.
// TODO: Fix the instructions doing partial writes rather than dealing with it here.
auto SrcVector = LoadXMMRegister(gprIndex);
if (Class == IR::GPRClass) {
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) {
Result = _VMov(OpSize, Src);
}
else {
Result = _VInsElement(VectorSize, OpSize, highIndex, 0, SrcVector, Src);
}
LOGMAN_THROW_AA_FMT(Class != IR::GPRClass, "Partial writes from GPR not allowed. Instruction: {}",
Op->TableInfo->Name);
// 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) {
Result = _VMov(OpSize, Src);
} else {
Result = _VInsElement(VectorSize, OpSize, 0, 0, SrcVector, Src);
}
}
@@ -5330,7 +5247,7 @@ void OpDispatchBuilder::StoreResult(FEXCore::IR::RegisterClassType Class, FEXCor
StoreResult(Class, Op, Op->Dest, Src, Align, AccessType);
}
OpDispatchBuilder::OpDispatchBuilder(FEXCore::Context::Context *ctx)
OpDispatchBuilder::OpDispatchBuilder(FEXCore::Context::ContextImpl *ctx)
: IREmitter {ctx->OpDispatcherAllocator}
, CTX {ctx} {
ResetWorkingList();
@@ -5365,58 +5282,7 @@ void OpDispatchBuilder::MOVGPRNTOp(OpcodeArgs) {
StoreResult(GPRClass, Op, Src, 1, MemoryAccessType::ACCESS_STREAM);
}
void OpDispatchBuilder::ALUOp(OpcodeArgs) {
bool RequiresMask = false;
FEXCore::IR::IROps IROp;
switch (Op->OP) {
case 0x0:
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
IROp = FEXCore::IR::IROps::OP_ADD;
RequiresMask = true;
break;
case 0x8:
case 0x9:
case 0xA:
case 0xB:
case 0xC:
case 0xD:
IROp = FEXCore::IR::IROps::OP_OR;
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
IROp = FEXCore::IR::IROps::OP_AND;
break;
case 0x28:
case 0x29:
case 0x2A:
case 0x2B:
case 0x2C:
case 0x2D:
IROp = FEXCore::IR::IROps::OP_SUB;
RequiresMask = true;
break;
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
IROp = FEXCore::IR::IROps::OP_XOR;
break;
default:
IROp = FEXCore::IR::IROps::OP_LAST;
LOGMAN_MSG_A_FMT("Unknown ALU Op: 0x{:x}", Op->OP);
break;
}
void OpDispatchBuilder::ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask) {
auto Size = GetDstSize(Op);
// X86 basic ALU ops just do the operation between the destination and a single source
@@ -5429,43 +5295,24 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
HandledLock = true;
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
switch (IROp) {
case FEXCore::IR::IROps::OP_ADD: {
Dest = _AtomicFetchAdd(Size, Src, DestMem);
Result = _Add(Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_SUB: {
Dest = _AtomicFetchSub(Size, Src, DestMem);
Result = _Sub(Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_OR: {
Dest = _AtomicFetchOr(Size, Src, DestMem);
Result = _Or(Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_AND: {
Dest = _AtomicFetchAnd(Size, Src, DestMem);
Result = _And(Dest, Src);
break;
}
case FEXCore::IR::IROps::OP_XOR: {
Dest = _AtomicFetchXor(Size, Src, DestMem);
Result = _Xor(Dest, Src);
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Atomic IR Op: {}", ToUnderlying(IROp));
break;
}
auto FetchOp = _AtomicFetchAdd(Size, Src, DestMem);
// Overwrite our atomic op type
FetchOp.first->Header.Op = AtomicFetchOp;
Dest = FetchOp;
auto ALUOp = _Add(Dest, Src);
// Overwrite our IR's op type
ALUOp.first->Header.Op = ALUIROp;
Result = ALUOp;
}
else {
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1);
auto ALUOp = _Add(Dest, Src);
// Overwrite our IR's op type
ALUOp.first->Header.Op = IROp;
ALUOp.first->Header.Op = ALUIROp;
Result = ALUOp;
StoreResult(GPRClass, Op, Result, -1);
@@ -5477,7 +5324,7 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
// Flags set
{
switch (IROp) {
switch (ALUIROp) {
case FEXCore::IR::IROps::OP_ADD:
GenerateFlags_ADD(Op, Result, Dest, Src);
break;
@@ -5495,6 +5342,11 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
}
}
template<FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask>
void OpDispatchBuilder::ALUOp(OpcodeArgs) {
ALUOpImpl(Op, ALUIROp, AtomicFetchOp, RequiresMask);
}
void OpDispatchBuilder::INTOp(OpcodeArgs) {
IR::BreakDefinition Reason;
bool SetRIPToNext = false;
@@ -5826,8 +5678,12 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
static constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> AVXTable[] = {
{OPD(1, 0b00, 0x10), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
{OPD(1, 0b01, 0x10), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
{OPD(1, 0b10, 0x10), 1, &OpDispatchBuilder::VMOVSSOp},
{OPD(1, 0b11, 0x10), 1, &OpDispatchBuilder::VMOVSDOp},
{OPD(1, 0b00, 0x11), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
{OPD(1, 0b01, 0x11), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
{OPD(1, 0b10, 0x11), 1, &OpDispatchBuilder::VMOVSSOp},
{OPD(1, 0b11, 0x11), 1, &OpDispatchBuilder::VMOVSDOp},
{OPD(1, 0b00, 0x12), 1, &OpDispatchBuilder::VMOVLPOp},
{OPD(1, 0b01, 0x12), 1, &OpDispatchBuilder::VMOVLPOp},
@@ -5946,6 +5802,10 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(1, 0b01, 0x6F), 1, &OpDispatchBuilder::VMOVAPS_VMOVAPD_Op},
{OPD(1, 0b10, 0x6F), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
{OPD(1, 0b01, 0x70), 1, &OpDispatchBuilder::VPSHUFWOp<4, true>},
{OPD(1, 0b10, 0x70), 1, &OpDispatchBuilder::VPSHUFWOp<2, false>},
{OPD(1, 0b11, 0x70), 1, &OpDispatchBuilder::VPSHUFWOp<2, true>},
{OPD(1, 0b01, 0x74), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPEQ, 1>},
{OPD(1, 0b01, 0x75), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPEQ, 2>},
{OPD(1, 0b01, 0x76), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPEQ, 4>},
@@ -5954,6 +5814,8 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(1, 0b01, 0x7C), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VFADDP, 8>},
{OPD(1, 0b11, 0x7C), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VFADDP, 4>},
{OPD(1, 0b01, 0x7D), 1, &OpDispatchBuilder::VHSUBPOp<8>},
{OPD(1, 0b11, 0x7D), 1, &OpDispatchBuilder::VHSUBPOp<4>},
{OPD(1, 0b01, 0x7E), 1, &OpDispatchBuilder::MOVBetweenGPR_FPR},
{OPD(1, 0b10, 0x7E), 1, &OpDispatchBuilder::MOVQOp},
@@ -5968,6 +5830,9 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(1, 0b01, 0xC5), 1, &OpDispatchBuilder::PExtrOp<2>},
{OPD(1, 0b00, 0xC6), 1, &OpDispatchBuilder::VSHUFOp<4>},
{OPD(1, 0b01, 0xC6), 1, &OpDispatchBuilder::VSHUFOp<8>},
{OPD(1, 0b01, 0xD0), 1, &OpDispatchBuilder::VADDSUBPOp<8>},
{OPD(1, 0b11, 0xD0), 1, &OpDispatchBuilder::VADDSUBPOp<4>},
@@ -6015,6 +5880,7 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(1, 0b01, 0xF2), 1, &OpDispatchBuilder::VPSLLOp<4>},
{OPD(1, 0b01, 0xF3), 1, &OpDispatchBuilder::VPSLLOp<8>},
{OPD(1, 0b01, 0xF4), 1, &OpDispatchBuilder::VPMULLOp<4, false>},
{OPD(1, 0b01, 0xF5), 1, &OpDispatchBuilder::VPMADDWDOp},
{OPD(1, 0b01, 0xF7), 1, &OpDispatchBuilder::MASKMOVOp},
{OPD(1, 0b01, 0xF8), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VSUB, 1>},
@@ -6025,17 +5891,24 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(1, 0b01, 0xFD), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VADD, 2>},
{OPD(1, 0b01, 0xFE), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VADD, 4>},
{OPD(2, 0b01, 0x00), 1, &OpDispatchBuilder::VPSHUFBOp},
{OPD(2, 0b01, 0x01), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VADDP, 2>},
{OPD(2, 0b01, 0x02), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VADDP, 4>},
{OPD(2, 0b01, 0x03), 1, &OpDispatchBuilder::VPHADDSWOp},
{OPD(2, 0b01, 0x05), 1, &OpDispatchBuilder::VPHSUBOp<2>},
{OPD(2, 0b01, 0x06), 1, &OpDispatchBuilder::VPHSUBOp<4>},
{OPD(2, 0b01, 0x07), 1, &OpDispatchBuilder::VPHSUBSWOp},
{OPD(2, 0b01, 0x08), 1, &OpDispatchBuilder::VPSIGN<1>},
{OPD(2, 0b01, 0x09), 1, &OpDispatchBuilder::VPSIGN<2>},
{OPD(2, 0b01, 0x0A), 1, &OpDispatchBuilder::VPSIGN<4>},
{OPD(2, 0b01, 0x0B), 1, &OpDispatchBuilder::VPMULHRSWOp},
{OPD(2, 0b01, 0x0C), 1, &OpDispatchBuilder::VPERMILRegOp<4>},
{OPD(2, 0b01, 0x0D), 1, &OpDispatchBuilder::VPERMILRegOp<8>},
{OPD(2, 0b01, 0x16), 1, &OpDispatchBuilder::VPERMDOp},
{OPD(2, 0b01, 0x17), 1, &OpDispatchBuilder::PTestOp},
{OPD(2, 0b01, 0x18), 1, &OpDispatchBuilder::VBROADCASTOp<4>},
{OPD(2, 0b01, 0x19), 1, &OpDispatchBuilder::VBROADCASTOp<8>},
{OPD(2, 0b01, 0x1A), 1, &OpDispatchBuilder::VBROADCASTOp<16>},
@@ -6061,6 +5934,7 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(2, 0b01, 0x33), 1, &OpDispatchBuilder::AVXExtendVectorElements<2, 4, false>},
{OPD(2, 0b01, 0x34), 1, &OpDispatchBuilder::AVXExtendVectorElements<2, 8, false>},
{OPD(2, 0b01, 0x35), 1, &OpDispatchBuilder::AVXExtendVectorElements<4, 8, false>},
{OPD(2, 0b01, 0x36), 1, &OpDispatchBuilder::VPERMDOp},
{OPD(2, 0b01, 0x37), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPGT, 8>},
{OPD(2, 0b01, 0x38), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VSMIN, 1>},
@@ -6100,6 +5974,9 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{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, 0x0D), 1, &OpDispatchBuilder::VBLENDPDOp},
{OPD(3, 0b01, 0x0E), 1, &OpDispatchBuilder::VPBLENDWOp},
{OPD(3, 0b01, 0x0F), 1, &OpDispatchBuilder::VPALIGNROp},
{OPD(3, 0b01, 0x14), 1, &OpDispatchBuilder::PExtrOp<1>},
{OPD(3, 0b01, 0x15), 1, &OpDispatchBuilder::PExtrOp<2>},
@@ -6107,16 +5984,22 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
{OPD(3, 0b01, 0x17), 1, &OpDispatchBuilder::PExtrOp<4>},
{OPD(3, 0b01, 0x18), 1, &OpDispatchBuilder::VINSERTOp},
{OPD(3, 0b01, 0x19), 1, &OpDispatchBuilder::VEXTRACT128Op},
{OPD(3, 0b01, 0x21), 1, &OpDispatchBuilder::VINSERTPSOp},
{OPD(3, 0b01, 0x38), 1, &OpDispatchBuilder::VINSERTOp},
{OPD(3, 0b01, 0x39), 1, &OpDispatchBuilder::VEXTRACT128Op},
{OPD(3, 0b01, 0x40), 1, &OpDispatchBuilder::VDPPOp<4>},
{OPD(3, 0b01, 0x41), 1, &OpDispatchBuilder::VDPPOp<8>},
{OPD(3, 0b01, 0x46), 1, &OpDispatchBuilder::VPERM2Op},
{OPD(3, 0b01, 0x4A), 1, &OpDispatchBuilder::AVXVectorVariableBlend<4>},
{OPD(3, 0b01, 0x4B), 1, &OpDispatchBuilder::AVXVectorVariableBlend<8>},
{OPD(3, 0b01, 0x4C), 1, &OpDispatchBuilder::AVXVectorVariableBlend<1>},
{OPD(3, 0b01, 0xDF), 1, &OpDispatchBuilder::VAESKeyGenAssistOp},
};
#undef OPD
@@ -6186,19 +6069,19 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
void InstallOpcodeHandlers(Context::OperatingMode Mode) {
constexpr std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr> BaseOpTable[] = {
// Instructions
{0x00, 6, &OpDispatchBuilder::ALUOp},
{0x00, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_ADD, FEXCore::IR::IROps::OP_ATOMICFETCHADD, true>},
{0x08, 6, &OpDispatchBuilder::ALUOp},
{0x08, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_OR, FEXCore::IR::IROps::OP_ATOMICFETCHOR, false>},
{0x10, 6, &OpDispatchBuilder::ADCOp<0>},
{0x18, 6, &OpDispatchBuilder::SBBOp<0>},
{0x20, 6, &OpDispatchBuilder::ALUOp},
{0x20, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_AND, FEXCore::IR::IROps::OP_ATOMICFETCHAND, false>},
{0x28, 6, &OpDispatchBuilder::ALUOp},
{0x28, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_SUB, FEXCore::IR::IROps::OP_ATOMICFETCHSUB, true>},
{0x30, 6, &OpDispatchBuilder::ALUOp},
{0x30, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_XOR, FEXCore::IR::IROps::OP_ATOMICFETCHXOR, false>},
{0x38, 6, &OpDispatchBuilder::CMPOp<0>},
{0x50, 8, &OpDispatchBuilder::PUSHREGOp},
@@ -6327,9 +6210,8 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{0x12, 2, &OpDispatchBuilder::MOVLPOp},
{0x14, 1, &OpDispatchBuilder::PUNPCKLOp<4>},
{0x15, 1, &OpDispatchBuilder::PUNPCKHOp<4>},
{0x16, 1, &OpDispatchBuilder::MOVLHPSOp},
{0x17, 1, &OpDispatchBuilder::MOVUPSOp},
{0x28, 2, &OpDispatchBuilder::MOVUPSOp},
{0x16, 2, &OpDispatchBuilder::MOVHPDOp},
{0x28, 2, &OpDispatchBuilder::MOVAPSOp},
{0x2A, 1, &OpDispatchBuilder::MMX_To_XMM_Vector_CVT_Int_To_Float<4, false>},
{0x2B, 1, &OpDispatchBuilder::MOVVectorNTOp},
{0x2C, 1, &OpDispatchBuilder::Vector_CVT_Float_To_Int<4, false, false>},
@@ -6419,7 +6301,6 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{0xFE, 1, &OpDispatchBuilder::VectorALUOp<IR::OP_VADD, 4>},
// FEX reserved instructions
{0x36, 1, &OpDispatchBuilder::SIGRETOp},
{0x37, 1, &OpDispatchBuilder::CallbackReturnOp},
};
+65 -5
View File
@@ -75,7 +75,7 @@ public:
OrderedNode* flagsOpDestSigned{};
OrderedNode* flagsOpSrcSigned{};
FEXCore::Context::Context *CTX{};
FEXCore::Context::ContextImpl *CTX{};
// Used during new op bringup
bool ShouldDump {false};
@@ -149,7 +149,7 @@ public:
return false;
}
OpDispatchBuilder(FEXCore::Context::Context *ctx);
OpDispatchBuilder(FEXCore::Context::ContextImpl *ctx);
OpDispatchBuilder(FEXCore::Utils::IntrusivePooledAllocator &Allocator);
void ResetWorkingList();
@@ -168,6 +168,7 @@ public:
void MOVGPRNTOp(OpcodeArgs);
void MOVVectorOp(OpcodeArgs);
void MOVVectorNTOp(OpcodeArgs);
template<FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask>
void ALUOp(OpcodeArgs);
void INTOp(OpcodeArgs);
void SyscallOp(OpcodeArgs);
@@ -176,7 +177,6 @@ public:
void NOPOp(OpcodeArgs);
void RETOp(OpcodeArgs);
void IRETOp(OpcodeArgs);
void SIGRETOp(OpcodeArgs);
void CallbackReturnOp(OpcodeArgs);
void SecondaryALUOp(OpcodeArgs);
template<uint32_t SrcIndex>
@@ -304,7 +304,6 @@ public:
// SSE
void MOVAPSOp(OpcodeArgs);
void MOVUPSOp(OpcodeArgs);
void MOVLHPSOp(OpcodeArgs);
void MOVLPOp(OpcodeArgs);
void MOVSHDUPOp(OpcodeArgs);
void MOVSLDUPOp(OpcodeArgs);
@@ -437,14 +436,22 @@ public:
void VANDNOp(OpcodeArgs);
void VBLENDPDOp(OpcodeArgs);
void VPBLENDDOp(OpcodeArgs);
void VPBLENDWOp(OpcodeArgs);
template <size_t ElementSize>
void VBROADCASTOp(OpcodeArgs);
template <size_t ElementSize>
void VDPPOp(OpcodeArgs);
void VEXTRACT128Op(OpcodeArgs);
template <IROps IROp, size_t ElementSize>
void VHADDPOp(OpcodeArgs);
template <size_t ElementSize>
void VHSUBPOp(OpcodeArgs);
void VINSERTOp(OpcodeArgs);
void VINSERTPSOp(OpcodeArgs);
@@ -459,6 +466,9 @@ public:
void VMOVSHDUPOp(OpcodeArgs);
void VMOVSLDUPOp(OpcodeArgs);
void VMOVSDOp(OpcodeArgs);
void VMOVSSOp(OpcodeArgs);
void VMOVVectorNTOp(OpcodeArgs);
template <size_t ElementSize>
@@ -467,18 +477,26 @@ public:
template <size_t ElementSize>
void VPACKUSOp(OpcodeArgs);
void VPBLENDDOp(OpcodeArgs);
void VPALIGNROp(OpcodeArgs);
void VPERM2Op(OpcodeArgs);
void VPERMDOp(OpcodeArgs);
void VPERMQOp(OpcodeArgs);
template <size_t ElementSize>
void VPERMILImmOp(OpcodeArgs);
template <size_t ElementSize>
void VPERMILRegOp(OpcodeArgs);
void VPHADDSWOp(OpcodeArgs);
void VPHMINPOSUWOp(OpcodeArgs);
template <size_t ElementSize>
void VPHSUBOp(OpcodeArgs);
void VPHSUBSWOp(OpcodeArgs);
void VPMADDWDOp(OpcodeArgs);
void VPMULHRSWOp(OpcodeArgs);
@@ -488,6 +506,11 @@ public:
template <size_t ElementSize, bool Signed>
void VPMULLOp(OpcodeArgs);
void VPSHUFBOp(OpcodeArgs);
template <size_t ElementSize, bool Low>
void VPSHUFWOp(OpcodeArgs);
template <size_t ElementSize>
void VPSLLOp(OpcodeArgs);
void VPSLLDQOp(OpcodeArgs);
@@ -515,6 +538,9 @@ public:
template <size_t ElementSize>
void VPSRLIOp(OpcodeArgs);
template <size_t ElementSize>
void VSHUFOp(OpcodeArgs);
void VZEROOp(OpcodeArgs);
// X87 Ops
@@ -755,6 +781,8 @@ private:
FEXCore::IR::IROp_IRHeader *Current_Header{};
OrderedNode *Current_HeaderNode{};
void ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask);
// Opcode helpers for generalizing behavior across VEX and non-VEX variants.
OrderedNode* ADDSUBPOpImpl(OpcodeArgs, size_t ElementSize,
@@ -764,6 +792,9 @@ private:
void AVXVectorScalarALUOpImpl(OpcodeArgs, IROps IROp, size_t ElementSize);
void AVXVectorUnaryOpImpl(OpcodeArgs, IROps IROp, size_t ElementSize, bool Scalar);
template <size_t ElementSize>
void AVXVectorVariableBlend(OpcodeArgs);
OrderedNode* AESKeyGenAssistImpl(OpcodeArgs);
OrderedNode* AESIMCImpl(OpcodeArgs);
@@ -774,6 +805,10 @@ private:
OrderedNode* ExtendVectorElementsImpl(OpcodeArgs, size_t ElementSize,
size_t DstElementSize, bool Signed);
OrderedNode* HSUBPOpImpl(OpcodeArgs, size_t ElementSize,
const X86Tables::DecodedOperand& Src1Op,
const X86Tables::DecodedOperand& Src2Op);
OrderedNode* InsertPSOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
const X86Tables::DecodedOperand& Src2,
const X86Tables::DecodedOperand& Imm);
@@ -784,11 +819,24 @@ private:
OrderedNode* PACKUSOpImpl(OpcodeArgs, size_t ElementSize,
OrderedNode *Src1, OrderedNode *Src2);
OrderedNode* PALIGNROpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
const X86Tables::DecodedOperand& Src2,
const X86Tables::DecodedOperand& Imm);
OrderedNode* PHADDSOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
const X86Tables::DecodedOperand& Src2);
OrderedNode* PHMINPOSUWOpImpl(OpcodeArgs);
OrderedNode* PHSUBOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
const X86Tables::DecodedOperand& Src2, size_t ElementSize);
OrderedNode* PHSUBSOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1Op,
const X86Tables::DecodedOperand& Src2Op);
OrderedNode* PMADDWDOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
const X86Tables::DecodedOperand& Src2);
OrderedNode* PMULHRSWOpImpl(OpcodeArgs, OrderedNode *Src1, OrderedNode *Src2);
OrderedNode* PMULHWOpImpl(OpcodeArgs, bool Signed,
@@ -797,6 +845,9 @@ private:
OrderedNode* PMULLOpImpl(OpcodeArgs, size_t ElementSize, bool Signed,
OrderedNode *Src1, OrderedNode *Src2);
OrderedNode* PSHUFBOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
const X86Tables::DecodedOperand& Src2);
OrderedNode* PSIGNImpl(OpcodeArgs, size_t ElementSize,
OrderedNode *Src1, OrderedNode *Src2);
@@ -812,6 +863,13 @@ private:
OrderedNode* PSRLDOpImpl(OpcodeArgs, size_t ElementSize,
OrderedNode *Src, OrderedNode *ShiftVec);
OrderedNode* SHUFOpImpl(OpcodeArgs, size_t ElementSize,
const X86Tables::DecodedOperand& Src1,
const X86Tables::DecodedOperand& Src2,
const X86Tables::DecodedOperand& Imm);
void VMOVScalarOpImpl(OpcodeArgs, size_t ElementSize);
OrderedNode* VFCMPOpImpl(OpcodeArgs, size_t ElementSize, bool Scalar,
OrderedNode *Src1, OrderedNode *Src2, uint8_t CompType);
@@ -831,6 +889,8 @@ private:
#undef OpcodeArgs
OrderedNode *AppendSegmentOffset(OrderedNode *Value, uint32_t Flags, uint32_t DefaultPrefix = 0, bool Override = false);
OrderedNode *GetSegment(uint32_t Flags, uint32_t DefaultPrefix = 0, bool Override = false);
void UpdatePrefixFromSegment(OrderedNode *Segment, uint32_t SegmentReg);
enum class MemoryAccessType {
@@ -280,7 +280,7 @@ void OpDispatchBuilder::VAESIMCOp(OpcodeArgs) {
void OpDispatchBuilder::AESEncOp(OpcodeArgs) {
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Result = _VAESEnc(Dest, Src);
OrderedNode *Result = _VAESEnc(16, Dest, Src);
StoreResult(FPRClass, Op, Result, -1);
}
@@ -293,7 +293,7 @@ void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
OrderedNode *Result = _VAESEnc(State, Key);
OrderedNode *Result = _VAESEnc(DstSize, State, Key);
if (Is128Bit) {
Result = _VMov(16, Result);
@@ -304,7 +304,7 @@ void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
void OpDispatchBuilder::AESEncLastOp(OpcodeArgs) {
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Result = _VAESEncLast(Dest, Src);
OrderedNode *Result = _VAESEncLast(16, Dest, Src);
StoreResult(FPRClass, Op, Result, -1);
}
@@ -317,7 +317,7 @@ void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
OrderedNode *Result = _VAESEncLast(State, Key);
OrderedNode *Result = _VAESEncLast(DstSize, State, Key);
if (Is128Bit) {
Result = _VMov(16, Result);
@@ -328,7 +328,7 @@ void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
void OpDispatchBuilder::AESDecOp(OpcodeArgs) {
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Result = _VAESDec(Dest, Src);
OrderedNode *Result = _VAESDec(16, Dest, Src);
StoreResult(FPRClass, Op, Result, -1);
}
@@ -341,7 +341,7 @@ void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
OrderedNode *Result = _VAESDec(State, Key);
OrderedNode *Result = _VAESDec(DstSize, State, Key);
if (Is128Bit) {
Result = _VMov(16, Result);
@@ -352,7 +352,7 @@ void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
void OpDispatchBuilder::AESDecLastOp(OpcodeArgs) {
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Result = _VAESDecLast(Dest, Src);
OrderedNode *Result = _VAESDecLast(16, Dest, Src);
StoreResult(FPRClass, Op, Result, -1);
}
@@ -365,7 +365,7 @@ void OpDispatchBuilder::VAESDecLastOp(OpcodeArgs) {
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
OrderedNode *Result = _VAESDecLast(State, Key);
OrderedNode *Result = _VAESDecLast(DstSize, State, Key);
if (Is128Bit) {
Result = _VMov(16, Result);
@@ -399,7 +399,7 @@ void OpDispatchBuilder::PCLMULQDQOp(OpcodeArgs) {
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
const auto Selector = static_cast<uint8_t>(Op->Src[1].Data.Literal.Value);
auto Res = _PCLMUL(Dest, Src, Selector);
auto Res = _PCLMUL(16, Dest, Src, Selector);
StoreResult(FPRClass, Op, Res, -1);
}
@@ -413,7 +413,7 @@ void OpDispatchBuilder::VPCLMULQDQOp(OpcodeArgs) {
OrderedNode *Src2 = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
const auto Selector = static_cast<uint8_t>(Op->Src[2].Data.Literal.Value);
OrderedNode *Res = _PCLMUL(Src1, Src2, Selector);
OrderedNode *Res = _PCLMUL(DstSize, Src1, Src2, Selector);
if (Is128Bit) {
Res = _VMov(16, Res);
}
@@ -271,10 +271,8 @@ void OpDispatchBuilder::CalculcateFlags_ADC(uint8_t SrcSize, OrderedNode *Res, O
// SF
{
auto SignBitConst = _Constant(Size - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
}
// PF
@@ -342,10 +340,8 @@ void OpDispatchBuilder::CalculcateFlags_SBB(uint8_t SrcSize, OrderedNode *Res, O
// SF
{
auto SignBitConst = _Constant(SrcSize * 8 - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
}
// PF
@@ -412,10 +408,8 @@ void OpDispatchBuilder::CalculcateFlags_SUB(uint8_t SrcSize, OrderedNode *Res, O
// SF
{
auto SignBitConst = _Constant(SrcSize * 8 - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
}
// PF
@@ -469,10 +463,8 @@ void OpDispatchBuilder::CalculcateFlags_ADD(uint8_t SrcSize, OrderedNode *Res, O
// SF
{
auto SignBitConst = _Constant(SrcSize * 8 - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
}
// PF
@@ -583,10 +575,8 @@ void OpDispatchBuilder::CalculcateFlags_Logical(uint8_t SrcSize, OrderedNode *Re
// SF
{
auto SignBitConst = _Constant(SrcSize * 8 - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
}
// PF
@@ -750,10 +740,8 @@ void OpDispatchBuilder::CalculcateFlags_SignShiftRight(uint8_t SrcSize, OrderedN
// SF
{
auto SignBitConst = _Constant(SrcSize * 8 - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
COND_FLAG_SET(Src2, RFLAG_SF_LOC, LshrOp);
auto SignBitOp = _Bfe(1, SrcSize * 8 - 1, Res);
COND_FLAG_SET(Src2, RFLAG_SF_LOC, SignBitOp);
}
// OF
@@ -802,15 +790,14 @@ void OpDispatchBuilder::CalculcateFlags_ShiftLeftImmediate(uint8_t SrcSize, Orde
// SF
{
auto LshrOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
// OF
// In the case of left shift. OF is only set from the result of <Top Source Bit> XOR <Top Result Bit>
if (Shift == 1) {
auto SourceBit = _Bfe(1, SrcSize * 8 - 1, Src1);
SetRFLAG<FEXCore::X86State::RFLAG_OF_LOC>(_Xor(SourceBit, LshrOp));
SetRFLAG<FEXCore::X86State::RFLAG_OF_LOC>(_Xor(SourceBit, SignOp));
}
}
}
@@ -851,10 +838,8 @@ void OpDispatchBuilder::CalculcateFlags_SignShiftRightImmediate(uint8_t SrcSize,
// SF
{
auto SignBitConst = _Constant(SrcSize * 8 - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignBitOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignBitOp);
// OF
// Only defined when Shift is 1 else undefined
@@ -902,10 +887,8 @@ void OpDispatchBuilder::CalculcateFlags_ShiftRightImmediate(uint8_t SrcSize, Ord
// SF
{
auto SignBitConst = _Constant(SrcSize * 8 - 1);
auto LshrOp = _Lshr(Res, SignBitConst);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
auto SignBitOp = _Bfe(1, SrcSize * 8 - 1, Res);
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignBitOp);
}
// OF
@@ -1115,10 +1098,8 @@ void OpDispatchBuilder::CalculcateFlags_BLSI(uint8_t SrcSize, OrderedNode *Src)
// SF
{
auto SignBit = _Constant(SrcSize * 8 - 1);
auto SFOp = _Lshr(Src, SignBit);
SetRFLAG<X86State::RFLAG_SF_LOC>(SFOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Src);
SetRFLAG<X86State::RFLAG_SF_LOC>(SignOp);
}
}
@@ -1174,10 +1155,8 @@ void OpDispatchBuilder::CalculcateFlags_BLSR(uint8_t SrcSize, OrderedNode *Resul
// SF
{
auto SignBit = _Constant(SrcSize * 8 - 1);
auto SFOp = _Lshr(Result, SignBit);
SetRFLAG<X86State::RFLAG_SF_LOC>(SFOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Result);
SetRFLAG<X86State::RFLAG_SF_LOC>(SignOp);
}
}
@@ -1230,9 +1209,8 @@ void OpDispatchBuilder::CalculcateFlags_BZHI(uint8_t SrcSize, OrderedNode *Resul
// SF
{
auto SFOp = _Lshr(Result, Bounds);
SetRFLAG<X86State::RFLAG_SF_LOC>(SFOp);
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Result);
SetRFLAG<X86State::RFLAG_SF_LOC>(SignOp);
}
}
File diff suppressed because it is too large. Load diff
@@ -1388,7 +1388,7 @@ void OpDispatchBuilder::X87FCMOV(OpcodeArgs) {
auto a = _LoadContextIndexed(top, 16, MMBaseOffset(), 16, FPRClass);
auto b = _LoadContextIndexed(arg, 16, MMBaseOffset(), 16, FPRClass);
auto Result = _VBSL(VecCond, b, a);
auto Result = _VBSL(16, VecCond, b, a);
// Write to ST[TOP]
_StoreContextIndexed(Result, top, 16, MMBaseOffset(), 16, FPRClass);
+29 -8
View File
@@ -23,17 +23,38 @@ X86GeneratedCode::X86GeneratedCode() {
// Allocate a page for our emulated guest
CodePtr = AllocateGuestCodeSpace(CODE_SIZE);
SignalReturn = reinterpret_cast<uint64_t>(CodePtr);
SignalReturnRT = reinterpret_cast<uint64_t>(CodePtr) + 3;
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr) + 6;
const std::vector<uint8_t> SignalReturnCode = {
0x0F, 0x36, 0x0, // SIGRET FEX instruction (Non-RT)
0x0F, 0x36, 0x1, // SIGRET FEX instruction (RT)
constexpr std::array<uint8_t, 2> SignalReturnCode = {
0x0F, 0x37, // CALLBACKRET FEX Instruction
};
memcpy(CodePtr, &SignalReturnCode.at(0), SignalReturnCode.size());
// Signal return handlers need to be bit-exact to what the Linux kernel provides in VDSO.
// GDB and unwinding libraries key off of these instructions to understand if the stack frame is a signal frame or not.
// This two code sections match exactly what libSegFault expects.
//
// Typically this handlers are provided by the 32-bit VDSO thunk library, but that isn't available in all cases.
// Falling back to this generated code segment still allows a backtrace to work, just might not show
// the symbol as VDSO since there is no ELF to parse.
constexpr std::array<uint8_t, 9> sigreturn_32_code = {
0x58, // pop eax
0xb8, 0x77, 0x00, 0x00, 0x00, // mov eax, 0x77
0xcd, 0x80, // int 0x80
0x90, // nop
};
constexpr std::array<uint8_t, 7> rt_sigreturn_32_code = {
0xb8, 0xad, 0x00, 0x00, 0x00, // mov eax, 0xad
0xcd, 0x80, // int 0x80
};
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr);
sigreturn_32 = CallbackReturn + SignalReturnCode.size();
rt_sigreturn_32 = sigreturn_32 + sigreturn_32_code.size();
memcpy(reinterpret_cast<void*>(CallbackReturn), &SignalReturnCode.at(0), SignalReturnCode.size());
memcpy(reinterpret_cast<void*>(sigreturn_32), &sigreturn_32_code.at(0), sigreturn_32_code.size());
memcpy(reinterpret_cast<void*>(rt_sigreturn_32), &rt_sigreturn_32_code.at(0), rt_sigreturn_32_code.size());
mprotect(CodePtr, CODE_SIZE, PROT_READ);
}
X86GeneratedCode::~X86GeneratedCode() {
+2 -2
View File
@@ -15,9 +15,9 @@ public:
X86GeneratedCode();
~X86GeneratedCode();
uint64_t SignalReturn{};
uint64_t SignalReturnRT{};
uint64_t CallbackReturn{};
uint64_t sigreturn_32{};
uint64_t rt_sigreturn_32{};
private:
void *CodePtr{};
@@ -42,7 +42,7 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
{0x14, 1, X86InstInfo{"UNPCKLPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{0x15, 1, X86InstInfo{"UNPCKHPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{0x16, 1, X86InstInfo{"MOVLHPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{0x17, 1, X86InstInfo{"MOVHPS", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_SF_HIGH_XMM_REG | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
{0x17, 1, X86InstInfo{"MOVHPS", TYPE_INST, GenFlagsSizes(SIZE_64BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
{0x18, 1, X86InstInfo{"", TYPE_GROUP_16, FLAGS_NO_OVERLAY, 0, nullptr}},
{0x19, 7, X86InstInfo{"NOP", TYPE_INST, FLAGS_DEBUG | FLAGS_MODRM | FLAGS_NO_OVERLAY, 0, nullptr}},
@@ -64,6 +64,7 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
{0x33, 1, X86InstInfo{"RDPMC", TYPE_PRIV, FLAGS_NO_OVERLAY, 0, nullptr}},
{0x34, 1, X86InstInfo{"SYSENTER", TYPE_PRIV, FLAGS_NO_OVERLAY, 0, nullptr}},
{0x35, 1, X86InstInfo{"SYSEXIT", TYPE_PRIV, FLAGS_NO_OVERLAY, 0, nullptr}},
{0x36, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0, nullptr}},
{0x38, 1, X86InstInfo{"", TYPE_0F38_TABLE, FLAGS_NO_OVERLAY, 0, nullptr}},
{0x39, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0, nullptr}},
{0x3A, 1, X86InstInfo{"", TYPE_0F3A_TABLE, FLAGS_NO_OVERLAY, 0, nullptr}},
@@ -257,8 +258,6 @@ 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, 1, nullptr}},
{0x37, 1, X86InstInfo{"CALLBACKRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0, nullptr}},
@@ -19,13 +19,13 @@ void InitializeVEXTables() {
// VEX Map 1
{OPD(1, 0b00, 0x10), 1, X86InstInfo{"VMOVUPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0x10), 1, X86InstInfo{"VMOVUPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b10, 0x10), 1, X86InstInfo{"VMOVSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b11, 0x10), 1, X86InstInfo{"VMOVSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b10, 0x10), 1, X86InstInfo{"VMOVSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b11, 0x10), 1, X86InstInfo{"VMOVSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b00, 0x11), 1, X86InstInfo{"VMOVUPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0x11), 1, X86InstInfo{"VMOVUPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b10, 0x11), 1, X86InstInfo{"VMOVSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b11, 0x11), 1, X86InstInfo{"VMOVSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b10, 0x11), 1, X86InstInfo{"VMOVSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b11, 0x11), 1, X86InstInfo{"VMOVSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b00, 0x12), 1, X86InstInfo{"VMOVLPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
{OPD(1, 0b01, 0x12), 1, X86InstInfo{"VMOVLPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
@@ -83,9 +83,9 @@ void InitializeVEXTables() {
{OPD(1, 0b01, 0x66), 1, X86InstInfo{"VPCMPGTD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0x67), 1, X86InstInfo{"VPACKUSWB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0x70), 1, X86InstInfo{"VPSHUFD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b10, 0x70), 1, X86InstInfo{"VPSHUFHW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b11, 0x70), 1, X86InstInfo{"VPSHUFLW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b01, 0x70), 1, X86InstInfo{"VPSHUFD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(1, 0b10, 0x70), 1, X86InstInfo{"VPSHUFHW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(1, 0b11, 0x70), 1, X86InstInfo{"VPSHUFLW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(1, 0b01, 0x71), 1, X86InstInfo{"", TYPE_VEX_GROUP_12, FLAGS_NONE, 0, nullptr}}, // VEX Group 12
{OPD(1, 0b01, 0x72), 1, X86InstInfo{"", TYPE_VEX_GROUP_13, FLAGS_NONE, 0, nullptr}}, // VEX Group 13
@@ -105,8 +105,8 @@ void InitializeVEXTables() {
{OPD(1, 0b01, 0xC4), 1, X86InstInfo{"VPINSRW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b01, 0xC5), 1, X86InstInfo{"VPEXTRW", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_REG_ONLY | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(1, 0b00, 0xC6), 1, X86InstInfo{"VSHUFPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b01, 0xC6), 1, X86InstInfo{"VSHUFPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b00, 0xC6), 1, X86InstInfo{"VSHUFPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(1, 0b01, 0xC6), 1, X86InstInfo{"VSHUFPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
// The above ops are defined from `Table A-17. VEX Opcode Map 1, Low Nibble = [0h:7h]` of AMD Architecture programmer's manual Volume 3
// This table doesn't state which VEX.pp is for which instruction
@@ -184,8 +184,8 @@ void InitializeVEXTables() {
{OPD(1, 0b01, 0x7C), 1, X86InstInfo{"VHADDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b11, 0x7C), 1, X86InstInfo{"VHADDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0x7D), 1, X86InstInfo{"VHSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b11, 0x7D), 1, X86InstInfo{"VHSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b01, 0x7D), 1, X86InstInfo{"VHSUBPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b11, 0x7D), 1, X86InstInfo{"VHSUBPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0x7E), 1, X86InstInfo{"VMOV*", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b10, 0x7E), 1, X86InstInfo{"VMOVQ", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
@@ -246,7 +246,7 @@ void InitializeVEXTables() {
{OPD(1, 0b01, 0xF2), 1, X86InstInfo{"VPSLLD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0xF3), 1, X86InstInfo{"VPSLLQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0xF4), 1, X86InstInfo{"VPMULUDQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0xF5), 1, X86InstInfo{"VPMADDWD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b01, 0xF5), 1, X86InstInfo{"VPMADDWD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(1, 0b01, 0xF6), 1, X86InstInfo{"VPSADBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(1, 0b01, 0xF7), 1, X86InstInfo{"VMASKMOVDQU", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_REG_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
@@ -259,27 +259,27 @@ void InitializeVEXTables() {
{OPD(1, 0b01, 0xFE), 1, X86InstInfo{"VPADDD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
// VEX Map 2
{OPD(2, 0b01, 0x00), 1, X86InstInfo{"VPSHUFB", TYPE_UNDEC, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x00), 1, X86InstInfo{"VPSHUFB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x01), 1, X86InstInfo{"VPHADDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x02), 1, X86InstInfo{"VPHADDD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x03), 1, X86InstInfo{"VPHADDSW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x03), 1, X86InstInfo{"VPHADDSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x04), 1, X86InstInfo{"VPMADDUBSW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x05), 1, X86InstInfo{"VPHSUBW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x06), 1, X86InstInfo{"VPHSUBD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x07), 1, X86InstInfo{"VPHSUBSW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x07), 1, X86InstInfo{"VPHSUBSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x08), 1, X86InstInfo{"VPSIGNB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x09), 1, X86InstInfo{"VPSIGNW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x0A), 1, X86InstInfo{"VPSIGND", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x0B), 1, X86InstInfo{"VPMULHRSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x0C), 1, X86InstInfo{"VPERMILPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x0D), 1, X86InstInfo{"VPERMILPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x0C), 1, X86InstInfo{"VPERMILPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x0D), 1, X86InstInfo{"VPERMILPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x0E), 1, X86InstInfo{"VTESTPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x0F), 1, X86InstInfo{"VTESTPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x13), 1, X86InstInfo{"VCVTPH2PS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x16), 1, X86InstInfo{"VPERMPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x17), 1, X86InstInfo{"VPTEST", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x16), 1, X86InstInfo{"VPERMPS", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x17), 1, X86InstInfo{"VPTEST", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x18), 1, X86InstInfo{"VBROADCASTSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x19), 1, X86InstInfo{"VBROADCASTSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
@@ -310,7 +310,7 @@ void InitializeVEXTables() {
{OPD(2, 0b01, 0x33), 1, X86InstInfo{"VPMOVZXWD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x34), 1, X86InstInfo{"VPMOVZXWQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x35), 1, X86InstInfo{"VPMOVZXDQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x36), 1, X86InstInfo{"VPERMD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(2, 0b01, 0x36), 1, X86InstInfo{"VPERMD", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x37), 1, X86InstInfo{"VPCMPGTQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
{OPD(2, 0b01, 0x38), 1, X86InstInfo{"VPMINSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
@@ -417,9 +417,9 @@ void InitializeVEXTables() {
{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_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}},
{OPD(3, 0b01, 0x0D), 1, X86InstInfo{"VBLENDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x0E), 1, X86InstInfo{"VPBLENDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x0F), 1, X86InstInfo{"VPALIGNR", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x14), 1, X86InstInfo{"VPEXTRB", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x15), 1, X86InstInfo{"VPEXTRW", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
@@ -427,7 +427,7 @@ void InitializeVEXTables() {
{OPD(3, 0b01, 0x17), 1, X86InstInfo{"VEXTRACTPS", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x18), 1, X86InstInfo{"VINSERTF128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x19), 1, X86InstInfo{"VEXTRACTF128", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x19), 1, X86InstInfo{"VEXTRACTF128", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_256BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x1D), 1, X86InstInfo{"VCVTPS2PH", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x20), 1, X86InstInfo{"VPINSRB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
@@ -435,7 +435,7 @@ void InitializeVEXTables() {
{OPD(3, 0b01, 0x22), 1, X86InstInfo{"VPINSRD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x38), 1, X86InstInfo{"VINSERTI128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x39), 1, X86InstInfo{"VEXTRACTI128", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x39), 1, X86InstInfo{"VEXTRACTI128", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_256BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x40), 1, X86InstInfo{"VDPPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x41), 1, X86InstInfo{"VDPPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
@@ -443,11 +443,9 @@ void InitializeVEXTables() {
{OPD(3, 0b01, 0x44), 1, X86InstInfo{"VPCLMULQDQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x46), 1, X86InstInfo{"VPERM2I128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x48), 1, X86InstInfo{"VPERMILzz2PS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x49), 1, X86InstInfo{"VPERMILzz2PD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x4A), 1, X86InstInfo{"VBLENDVPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x4B), 1, X86InstInfo{"VBLENDVPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x4C), 1, X86InstInfo{"VBLENDVB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x4A), 1, X86InstInfo{"VBLENDVPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x4B), 1, X86InstInfo{"VBLENDVPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x4C), 1, X86InstInfo{"VPBLENDVB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
{OPD(3, 0b01, 0x5C), 1, X86InstInfo{"VFMADDSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
{OPD(3, 0b01, 0x5D), 1, X86InstInfo{"VFMADDSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
+4 -4
View File
@@ -179,7 +179,7 @@ namespace FEXCore {
};
auto args = reinterpret_cast<args_t*>(argsv);
auto CTX = Thread->CTX;
auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
LOGMAN_THROW_AA_FMT(args->original_callee, "Tried to link null pointer address to guest function");
LOGMAN_THROW_AA_FMT(args->target_addr, "Tried to link address to null pointer guest function");
@@ -264,7 +264,7 @@ namespace FEXCore {
}
static void LoadLib(void *ArgsV) {
auto CTX = Thread->CTX;
auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
auto Args = reinterpret_cast<LoadlibArgs*>(ArgsV);
@@ -321,7 +321,7 @@ namespace FEXCore {
auto &[Name, rv] = *reinterpret_cast<ArgsRV_t*>(ArgsRV);
auto CTX = Thread->CTX;
auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
auto That = reinterpret_cast<ThunkHandler_impl*>(CTX->ThunkHandler.get());
{
@@ -385,7 +385,7 @@ namespace FEXCore {
HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) {
LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function");
const auto CTX = Thread->CTX;
const auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
const auto ThunkHandler = reinterpret_cast<ThunkHandler_impl *>(CTX->ThunkHandler.get());
const GuestcallInfo gci = { GuestUnpacker, GuestTarget };
+1 -1
View File
@@ -11,7 +11,7 @@ $end_info$
#include <vector>
namespace FEXCore::Context {
struct Context;
class ContextImpl;
}
namespace FEXCore::Core {
+5 -2
View File
@@ -17,6 +17,9 @@
namespace FEXCore::Core {
struct DebugData;
}
namespace FEXCore::Context {
class ContextImpl;
}
namespace FEXCore::IR {
class RegisterAllocationData;
@@ -87,7 +90,7 @@ namespace FEXCore::IR {
class AOTIRCaptureCache final {
public:
AOTIRCaptureCache(FEXCore::Context::Context *ctx) : CTX {ctx} {}
AOTIRCaptureCache(FEXCore::Context::ContextImpl *ctx) : CTX {ctx} {}
void FinalizeAOTIRCache();
void AOTIRCaptureCacheWriteoutQueue_Flush();
@@ -131,7 +134,7 @@ namespace FEXCore::IR {
}
private:
FEXCore::Context::Context *CTX;
FEXCore::Context::ContextImpl *CTX;
std::shared_mutex AOTIRCacheLock;
std::shared_mutex AOTIRCaptureCacheWriteoutLock;
+35 -16
View File
@@ -22,7 +22,7 @@
"",
"Eg:",
"IR op with no result and no arguments",
" SignalReturn",
" CallbackReturn",
"",
"IR op with result and no arguments",
" GPR = ProcessorID",
@@ -264,9 +264,6 @@
"Break BreakDefinition:$Reason": {
"HasSideEffects": true
},
"SignalReturn i8:$IsRT": {
"HasSideEffects": true
},
"CallbackReturn": {
"HasSideEffects": true
},
@@ -479,6 +476,14 @@
]
},
"GPR = MemSet i1:$IsAtomic, u8:$Size, GPR:$Prefix, GPR:$Addr, GPR:$Value, GPR:$Length, GPR:$Direction": {
"Desc": ["Duplicates behaviour of x86 STOS repeat",
"Returns the final address that gets generated without the prefix appended."
],
"HasSideEffects": true,
"DestSize": "8"
},
"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",
@@ -1191,6 +1196,14 @@
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VTrn u8:#RegisterSize, u8:#ElementSize, FPR:$VectorLower, FPR:$VectorUpper": {
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VTrn2 u8:#RegisterSize, u8:#ElementSize, FPR:$VectorLower, FPR:$VectorUpper": {
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VFAdd u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
"DestSize": "RegisterSize",
@@ -1353,12 +1366,12 @@
"DestSize": "RegisterSize"
},
"FPR = VBSL FPR:$VectorMask, FPR:$VectorTrue, FPR:$VectorFalse": {
"FPR = VBSL u8:#RegisterSize, FPR:$VectorMask, FPR:$VectorTrue, FPR:$VectorFalse": {
"Desc": ["Does a vector bitwise select.",
"If the bit in the field is 1 then the corresponding bit is pulled from VectorTrue",
"If the bit in the field is 0 then the corresponding bit is pulled from VectorFalse"
],
"DestSize": "16"
"DestSize": "RegisterSize"
}
},
"Conv": {
@@ -1370,6 +1383,12 @@
"NumElements": "RegisterSize / ElementSize"
},
"FPR = VDupFromGPR u8:#RegisterSize, u8:#ElementSize, GPR:$Src": {
"Desc": ["Broadcasts a value in a GPR into each ElementSize-sized element in a vector"],
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize"
},
"FPR = Float_FromGPR_S u8:#DstElementSize, u8:$SrcElementSize, GPR:$Src": {
"Desc": ["Scalar op: Converts signed GPR to Scalar float",
"Zeroes the upper bits of the vector register"
@@ -1418,21 +1437,21 @@
"Desc": "Does a stage of the inverse mix column transformation",
"DestSize": "16"
},
"FPR = VAESEnc FPR:$State, FPR:$Key": {
"FPR = VAESEnc u8:#RegisterSize, FPR:$State, FPR:$Key": {
"Desc": "Does a step of AES encryption",
"DestSize": "16"
"DestSize": "RegisterSize"
},
"FPR = VAESEncLast FPR:$State, FPR:$Key": {
"FPR = VAESEncLast u8:#RegisterSize, FPR:$State, FPR:$Key": {
"Desc": "Does the last step of AES encryption",
"DestSize": "16"
"DestSize": "RegisterSize"
},
"FPR = VAESDec FPR:$State, FPR:$Key": {
"FPR = VAESDec u8:#RegisterSize, FPR:$State, FPR:$Key": {
"Desc": "Does a step of AES decryption",
"DestSize": "16"
"DestSize": "RegisterSize"
},
"FPR = VAESDecLast FPR:$State, FPR:$Key": {
"FPR = VAESDecLast u8:#RegisterSize, FPR:$State, FPR:$Key": {
"Desc": "Does the last step of AES decryption",
"DestSize": "16"
"DestSize": "RegisterSize"
},
"FPR = VAESKeyGenAssist FPR:$Src, u8:$RCON": {
"Desc": "Assists in key generation",
@@ -1443,7 +1462,7 @@
],
"DestSize": "std::max<uint8_t>(4, GetOpSize(_Src1))"
},
"FPR = PCLMUL FPR:$Src1, FPR:$Src2, u8:$Selector": {
"FPR = PCLMUL u8:#RegisterSize, FPR:$Src1, FPR:$Src2, u8:$Selector": {
"Desc": [
"Performs carryless multiplication of 64-bit elements depending on the selector.",
"Selector = 0b00000000: Uses low 64-bit elements from both input vectors",
@@ -1451,7 +1470,7 @@
"Selector = 0b00010000: Uses low 64-bit element from Src1 and high 64-bit element from Src2",
"Selector = 0b00010001: Uses high 64-bit elements from both input vectors"
],
"DestSize": "16"
"DestSize": "RegisterSize"
}
},
"F64": {
+1 -1
View File
@@ -17,7 +17,7 @@ $end_info$
namespace FEXCore::IR {
class IREmitter;
void PassManager::AddDefaultPasses(FEXCore::Context::Context *ctx, bool InlineConstants, bool StaticRegisterAllocation) {
void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl *ctx, bool InlineConstants, bool StaticRegisterAllocation) {
FEX_CONFIG_OPT(DisablePasses, O0);
if (!DisablePasses()) {
+5 -1
View File
@@ -14,6 +14,10 @@ $end_info$
#include <utility>
#include <vector>
namespace FEXCore::Context {
class ContextImpl;
}
namespace FEXCore::HLE {
class SyscallHandler;
}
@@ -40,7 +44,7 @@ protected:
class PassManager final {
friend class SyscallOptimization;
public:
void AddDefaultPasses(FEXCore::Context::Context *ctx, bool InlineConstants, bool StaticRegisterAllocation);
void AddDefaultPasses(FEXCore::Context::ContextImpl *ctx, bool InlineConstants, bool StaticRegisterAllocation);
void AddDefaultValidationPasses();
Pass* InsertPass(std::unique_ptr<Pass> Pass, std::string Name = "") {
Pass->RegisterPassManager(this);
@@ -219,8 +219,8 @@ namespace {
ContextClassification->emplace_back(ContextMemberInfo{
ContextMemberClassification {
offsetof(FEXCore::Core::CPUState, _pad2),
sizeof(FEXCore::Core::CPUState::_pad2),
offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader),
sizeof(FEXCore::Core::CPUState::InlineJITBlockHeader),
},
ACCESS_INVALID,
FEXCore::IR::InvalidClass,
+39 -4
View File
@@ -36,7 +36,7 @@ namespace CPU {
struct CPUBackendFeatures {
bool SupportsStaticRegisterAllocation = false;
};
class CPUBackend {
public:
struct CodeBuffer {
@@ -55,6 +55,42 @@ namespace CPU {
* @return The name of this backend
*/
[[nodiscard]] virtual std::string GetName() = 0;
struct CompiledCode {
// Where this code block begins.
uint8_t* BlockBegin;
/**
* The function entrypoint to this codeblock.
*
* This may or may not equal `BlockBegin` above. Depending on the CPU backend, it may stick data
* prior to the BlockEntry.
*
* Is actually a function pointer of type `void (FEXCore::Core::ThreadState *Thread)`
*/
uint8_t* BlockEntry;
// The total size of the codeblock from [BlockBegin, BlockBegin+Size).
size_t Size;
};
// Header that can live at the start of a JIT block.
// We want the header to be quite small, with most data living in the tail object.
struct JITCodeHeader {
// Offset from the start of this header to where the tail lives.
// Only 32-bit since the tail block won't ever be more than 4GB away.
uint32_t OffsetToBlockTail;
};
// Header that can live at the end of the JIT block.
// For any state reconstruction or other data, this is where it should live.
// Any data that is explicitly tied to the JIT code and needs to be cached with it
// should end up in this data structure.
struct JITCodeTail {
// The total size of the codeblock from [BlockBegin, BlockBegin+Size).
size_t Size;
// RIP that the block's entry comes from.
uint64_t RIP;
};
/**
* @brief Tells this CPUBackend to compile code for the provided IR and DebugData
*
@@ -69,10 +105,9 @@ namespace CPU {
* @param IR - IR that maps to the IR for this RIP
* @param DebugData - Debug data that is available for this IR indirectly
*
* @return An executable function pointer that is theoretically compiled from this point.
* Is actually a function pointer of type `void (FEXCore::Core::ThreadState *Thread)
* @return Information about the compiled code block.
*/
[[nodiscard]] virtual void *CompileCode(uint64_t Entry,
[[nodiscard]] virtual CompiledCode CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) = 0;
+214 -212
View File
@@ -41,7 +41,7 @@ namespace FEXCore::IR {
}
namespace FEXCore::Context {
struct Context;
class Context;
enum ExitReason {
EXIT_NONE,
EXIT_WAITING,
@@ -69,224 +69,226 @@ namespace FEXCore::Context {
std::unique_lock<std::shared_mutex> lock;
};
struct VDSOSigReturn {
void *VDSO_kernel_sigreturn;
void *VDSO_kernel_rt_sigreturn;
};
using CustomCPUFactoryType = std::function<std::unique_ptr<FEXCore::CPU::CPUBackend> (FEXCore::Context::Context*, FEXCore::Core::InternalThreadState *Thread)>;
using ExitHandler = std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)>;
class Context {
public:
virtual ~Context() = default;
/**
* @brief [[threadsafe]] Create a new FEXCore context object
*
* This is necessary to do when running threaded contexts
*
* @return a new context object
*/
FEX_DEFAULT_VISIBILITY static FEXCore::Context::Context *CreateNewContext();
/**
* @brief Post creation context initialization
* Once configurations have been set, do the post-creation initialization with that configuration
*
* @param CTX The context that we created
*
* @return true if we managed to initialize correctly
*/
FEX_DEFAULT_VISIBILITY virtual bool InitializeContext() = 0;
/**
* @brief Destroy the context object
*
* @param CTX
*/
FEX_DEFAULT_VISIBILITY static void DestroyContext(FEXCore::Context::Context * CTX);
FEX_DEFAULT_VISIBILITY virtual void DestroyContext() = 0;
/**
* @brief Allows setting up in memory code and other things prior to launchign code execution
*
* @param CTX The context that we created
* @param Loader The loader that will be doing all the code loading
*
* @return true if we loaded code
*/
FEX_DEFAULT_VISIBILITY virtual FEXCore::Core::InternalThreadState* InitCore(uint64_t InitialRIP, uint64_t StackPointer) = 0;
FEX_DEFAULT_VISIBILITY virtual void SetExitHandler(ExitHandler handler) = 0;
FEX_DEFAULT_VISIBILITY virtual ExitHandler GetExitHandler() const = 0;
/**
* @brief Pauses execution on the CPU core
*
* Blocks until all threads have paused.
*/
FEX_DEFAULT_VISIBILITY virtual void Pause() = 0;
/**
* @brief Starts (or continues) the CPU core
*
* This function is async and returns immediately.
* Use RunUntilExit() for synchonous executions
*
*/
FEX_DEFAULT_VISIBILITY virtual void Run() = 0;
/**
* @brief Tells the core to shutdown
*
* Blocks until shutdown
*/
FEX_DEFAULT_VISIBILITY virtual void Stop() = 0;
/**
* @brief Executes one instruction
*
* Returns once execution is complete.
*/
FEX_DEFAULT_VISIBILITY virtual void Step() = 0;
/**
* @brief Runs the CPU core until it exits
*
* If an Exit handler has been registered, this function won't return until the core
* has shutdown.
*
* @param CTX The context that we created
*
* @return The ExitReason for the parentthread.
*/
FEX_DEFAULT_VISIBILITY virtual ExitReason RunUntilExit() = 0;
FEX_DEFAULT_VISIBILITY virtual void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) = 0;
/**
* @brief Gets the program exit status
*
*
* @param CTX The context that we created
*
* @return The program exit status
*/
FEX_DEFAULT_VISIBILITY virtual int GetProgramStatus() const = 0;
/**
* @brief [[threadsafe]] Returns the ExitReason of the parent thread. Typically used for async result status
*
* @param CTX The context that we created
*
* @return The ExitReason for the parentthread
*/
FEX_DEFAULT_VISIBILITY virtual ExitReason GetExitReason() = 0;
/**
* @brief [[theadsafe]] Checks if the Context is either done working or paused(in the case of single stepping)
*
* Use this when the context is async running to determine if it is done
*
* @param CTX the context that we created
*
* @return true if the core is done or paused
*/
FEX_DEFAULT_VISIBILITY virtual bool IsDone() const = 0;
/**
* @brief Gets a copy the CPUState of the parent thread
*
* @param CTX The context that we created
* @param State The state object to populate
*/
FEX_DEFAULT_VISIBILITY virtual void GetCPUState(FEXCore::Core::CPUState *State) const = 0;
/**
* @brief Copies the CPUState provided to the parent thread
*
* @param CTX The context that we created
* @param State The satate object to copy from
*/
FEX_DEFAULT_VISIBILITY virtual void SetCPUState(const FEXCore::Core::CPUState *State) = 0;
/**
* @brief Allows the frontend to pass in a custom CPUBackend creation factory
*
* This allows the frontend to have its own frontend. Typically for debugging
*
* @param CTX The context that we created
* @param Factory The factory that the context will call if the DefaultCore config ise set to CUSTOM
*/
FEX_DEFAULT_VISIBILITY virtual void SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) = 0;
/**
* @brief Sets up memory regions on the guest for mirroring within the guest's VM space
*
* @param VirtualAddress The address we want to set to mirror a physical memory region
* @param PhysicalAddress The physical memory region we are mapping
* @param Size Size of the region to mirror
*
* @return true when successfully mapped. false if there was an error adding
*/
FEX_DEFAULT_VISIBILITY virtual bool AddVirtualMemoryMapping(uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size) = 0;
/**
* @brief Retrieves a feature struct indicating certain supported aspects from
* the hose.
*
* @param CTX A valid non-null context instance.
*/
FEX_DEFAULT_VISIBILITY virtual HostFeatures GetHostFeatures() const = 0;
FEX_DEFAULT_VISIBILITY virtual void HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) = 0;
FEX_DEFAULT_VISIBILITY virtual void RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) = 0;
[[noreturn]] FEX_DEFAULT_VISIBILITY virtual void HandleSignalHandlerReturn(bool RT) = 0;
FEX_DEFAULT_VISIBILITY virtual void RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) = 0;
FEX_DEFAULT_VISIBILITY virtual FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) = 0;
FEX_DEFAULT_VISIBILITY virtual void ExecutionThread(FEXCore::Core::InternalThreadState *Thread) = 0;
FEX_DEFAULT_VISIBILITY virtual void InitializeThread(FEXCore::Core::InternalThreadState *Thread) = 0;
FEX_DEFAULT_VISIBILITY virtual void RunThread(FEXCore::Core::InternalThreadState *Thread) = 0;
FEX_DEFAULT_VISIBILITY virtual void StopThread(FEXCore::Core::InternalThreadState *Thread) = 0;
FEX_DEFAULT_VISIBILITY virtual void DestroyThread(FEXCore::Core::InternalThreadState *Thread) = 0;
FEX_DEFAULT_VISIBILITY virtual void CleanupAfterFork(FEXCore::Core::InternalThreadState *Thread) = 0;
FEX_DEFAULT_VISIBILITY virtual void SetSignalDelegator(FEXCore::SignalDelegator *SignalDelegation) = 0;
FEX_DEFAULT_VISIBILITY virtual void SetSyscallHandler(FEXCore::HLE::SyscallHandler *Handler) = 0;
FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) = 0;
FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) = 0;
FEX_DEFAULT_VISIBILITY virtual FEXCore::IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(const std::string& Name) = 0;
FEX_DEFAULT_VISIBILITY virtual void UnloadAOTIRCacheEntry(FEXCore::IR::AOTIRCacheEntry *Entry) = 0;
FEX_DEFAULT_VISIBILITY virtual void SetAOTIRLoader(std::function<int(const std::string&)> CacheReader) = 0;
FEX_DEFAULT_VISIBILITY virtual void SetAOTIRWriter(std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) = 0;
FEX_DEFAULT_VISIBILITY virtual void SetAOTIRRenamer(std::function<void(const std::string&)> CacheRenamer) = 0;
FEX_DEFAULT_VISIBILITY virtual void FinalizeAOTIRCache() = 0;
FEX_DEFAULT_VISIBILITY virtual void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) = 0;
FEX_DEFAULT_VISIBILITY virtual void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length) = 0;
FEX_DEFAULT_VISIBILITY virtual void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> callback) = 0;
FEX_DEFAULT_VISIBILITY virtual void MarkMemoryShared() = 0;
FEX_DEFAULT_VISIBILITY virtual void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) = 0;
FEX_DEFAULT_VISIBILITY virtual CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr) = 0;
/**
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
*
* @param CTX A valid non-null context instance.
* @param Definitions A vector of thunk definitions that the frontend controls
*/
FEX_DEFAULT_VISIBILITY virtual void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) = 0;
FEX_DEFAULT_VISIBILITY virtual void SetVDSOSigReturn(const VDSOSigReturn &Pointers) = 0;
private:
};
/**
* @brief This initializes internal FEXCore state that is shared between contexts and requires overhead to setup
*/
FEX_DEFAULT_VISIBILITY void InitializeStaticTables(OperatingMode Mode = MODE_64BIT);
FEX_DEFAULT_VISIBILITY void ShutdownStaticTables();
/**
* @brief [[threadsafe]] Create a new FEXCore context object
*
* This is necessary to do when running threaded contexts
*
* @return a new context object
*/
FEX_DEFAULT_VISIBILITY FEXCore::Context::Context *CreateNewContext();
/**
* @brief Post creation context initialization
* Once configurations have been set, do the post-creation initialization with that configuration
*
* @param CTX The context that we created
*
* @return true if we managed to initialize correctly
*/
FEX_DEFAULT_VISIBILITY bool InitializeContext(FEXCore::Context::Context *CTX);
/**
* @brief Destroy the context object
*
* @param CTX
*/
FEX_DEFAULT_VISIBILITY void DestroyContext(FEXCore::Context::Context *CTX);
/**
* @brief Allows setting up in memory code and other things prior to launchign code execution
*
* @param CTX The context that we created
* @param Loader The loader that will be doing all the code loading
*
* @return true if we loaded code
*/
FEX_DEFAULT_VISIBILITY FEXCore::Core::InternalThreadState* InitCore(FEXCore::Context::Context *CTX, uint64_t InitialRIP, uint64_t StackPointer);
FEX_DEFAULT_VISIBILITY void SetExitHandler(FEXCore::Context::Context *CTX, ExitHandler handler);
FEX_DEFAULT_VISIBILITY ExitHandler GetExitHandler(const FEXCore::Context::Context *CTX);
/**
* @brief Pauses execution on the CPU core
*
* Blocks until all threads have paused.
*/
FEX_DEFAULT_VISIBILITY void Pause(FEXCore::Context::Context *CTX);
/**
* @brief Starts (or continues) the CPU core
*
* This function is async and returns immediately.
* Use RunUntilExit() for synchonous executions
*
*/
FEX_DEFAULT_VISIBILITY void Run(FEXCore::Context::Context *CTX);
/**
* @brief Runs the CPU core until it exits
*
* If an Exit handler has been registered, this function won't return until the core
* has shutdown.
*
* @param CTX The context that we created
*
* @return The ExitReason for the parentthread.
*/
FEX_DEFAULT_VISIBILITY ExitReason RunUntilExit(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
/**
* @brief Gets the program exit status
*
*
* @param CTX The context that we created
*
* @return The program exit status
*/
FEX_DEFAULT_VISIBILITY int GetProgramStatus(const FEXCore::Context::Context *CTX);
/**
* @brief Tells the core to shutdown
*
* Blocks until shutdown
*/
FEX_DEFAULT_VISIBILITY void Stop(FEXCore::Context::Context *CTX);
/**
* @brief Executes one instruction
*
* Returns once execution is complete.
*/
FEX_DEFAULT_VISIBILITY void Step(FEXCore::Context::Context *CTX);
/**
* @brief [[threadsafe]] Returns the ExitReason of the parent thread. Typically used for async result status
*
* @param CTX The context that we created
*
* @return The ExitReason for the parentthread
*/
FEX_DEFAULT_VISIBILITY ExitReason GetExitReason(const FEXCore::Context::Context *CTX);
/**
* @brief [[theadsafe]] Checks if the Context is either done working or paused(in the case of single stepping)
*
* Use this when the context is async running to determine if it is done
*
* @param CTX the context that we created
*
* @return true if the core is done or paused
*/
FEX_DEFAULT_VISIBILITY bool IsDone(const FEXCore::Context::Context *CTX);
/**
* @brief Gets a copy the CPUState of the parent thread
*
* @param CTX The context that we created
* @param State The state object to populate
*/
FEX_DEFAULT_VISIBILITY void GetCPUState(const FEXCore::Context::Context *CTX,
FEXCore::Core::CPUState *State);
/**
* @brief Copies the CPUState provided to the parent thread
*
* @param CTX The context that we created
* @param State The satate object to copy from
*/
FEX_DEFAULT_VISIBILITY void SetCPUState(FEXCore::Context::Context *CTX,
const FEXCore::Core::CPUState *State);
/**
* @brief Allows the frontend to pass in a custom CPUBackend creation factory
*
* This allows the frontend to have its own frontend. Typically for debugging
*
* @param CTX The context that we created
* @param Factory The factory that the context will call if the DefaultCore config ise set to CUSTOM
*/
FEX_DEFAULT_VISIBILITY void SetCustomCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory);
/**
* @brief Sets up memory regions on the guest for mirroring within the guest's VM space
*
* @param VirtualAddress The address we want to set to mirror a physical memory region
* @param PhysicalAddress The physical memory region we are mapping
* @param Size Size of the region to mirror
*
* @return true when successfully mapped. false if there was an error adding
*/
FEX_DEFAULT_VISIBILITY bool AddVirtualMemoryMapping(FEXCore::Context::Context *CTX, uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size);
/**
* @brief Allows the frontend to set a custom syscall handler
*
* Useful for debugging purposes. May not work if the syscall ID exceeds the maximum number of syscalls in the lookup table
*
* @param Syscall Which syscall ID to install a visitor to
* @param Visitor The Visitor to install
*/
FEX_DEFAULT_VISIBILITY void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, uint64_t Syscall, FEXCore::HLE::SyscallVisitor *Visitor);
/**
* @brief Retrieves a feature struct indicating certain supported aspects from
* the hose.
*
* @param CTX A valid non-null context instance.
*/
FEX_DEFAULT_VISIBILITY HostFeatures GetHostFeatures(const FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void HandleCallback(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
FEX_DEFAULT_VISIBILITY void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required);
FEX_DEFAULT_VISIBILITY void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required);
FEX_DEFAULT_VISIBILITY FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
FEX_DEFAULT_VISIBILITY void ExecutionThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void InitializeThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void RunThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void StopThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
FEX_DEFAULT_VISIBILITY void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation);
FEX_DEFAULT_VISIBILITY void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler);
FEX_DEFAULT_VISIBILITY FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf);
FEX_DEFAULT_VISIBILITY FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf, uint32_t CPU);
FEX_DEFAULT_VISIBILITY FEXCore::IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(FEXCore::Context::Context *CTX, const std::string& Name);
FEX_DEFAULT_VISIBILITY void UnloadAOTIRCacheEntry(FEXCore::Context::Context *CTX, FEXCore::IR::AOTIRCacheEntry *Entry);
FEX_DEFAULT_VISIBILITY void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader);
FEX_DEFAULT_VISIBILITY void SetAOTIRWriter(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter);
FEX_DEFAULT_VISIBILITY void SetAOTIRRenamer(FEXCore::Context::Context *CTX, std::function<void(const std::string&)> CacheRenamer);
FEX_DEFAULT_VISIBILITY void FinalizeAOTIRCache(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer);
FEX_DEFAULT_VISIBILITY void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length);
FEX_DEFAULT_VISIBILITY void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> callback);
FEX_DEFAULT_VISIBILITY void MarkMemoryShared(FEXCore::Context::Context *CTX);
FEX_DEFAULT_VISIBILITY void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
FEX_DEFAULT_VISIBILITY CustomIRResult AddCustomIREntrypoint(FEXCore::Context::Context *CTX, uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr);
/**
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
*
* @param CTX A valid non-null context instance.
* @param Definitions A vector of thunk definitions that the frontend controls
*/
FEX_DEFAULT_VISIBILITY void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
}
+1 -1
View File
@@ -37,7 +37,7 @@ namespace FEXCore::Core {
uint32_t es_cached, cs_cached, ss_cached, ds_cached;
uint64_t gs_cached;
uint64_t fs_cached;
uint64_t _pad2[1];
uint64_t InlineJITBlockHeader;
XMMRegs xmm;
uint8_t flags[48];
uint64_t mm[8][2];
+1 -1
View File
@@ -10,7 +10,7 @@ namespace FEXCore::Core {
}
namespace FEXCore::Context {
struct Context;
class Context;
namespace Debug {
@@ -18,7 +18,7 @@ namespace FEXCore {
}
namespace FEXCore::Context {
struct Context;
class Context;
}
namespace FEXCore::CPU {
+1 -1
View File
@@ -303,7 +303,7 @@ constexpr InstFlagType FLAGS_X87_FLAGS = (1ULL << 10);
constexpr InstFlagType FLAGS_SF_REX_IN_BYTE = (1ULL << 15);
// XMM subflags
constexpr InstFlagType FLAGS_SF_HIGH_XMM_REG = (1ULL << 11);
constexpr InstFlagType FLAGS_SF_UNUSED = (1ULL << 11); // No assigned behavior yet
constexpr InstFlagType FLAGS_SF_DST_GPR = (1ULL << 12);
constexpr InstFlagType FLAGS_SF_SRC_GPR = (1ULL << 13);
constexpr InstFlagType FLAGS_SF_MMX_DST = (1ULL << 14);
+1 -1
View File
@@ -15,7 +15,7 @@ namespace FEXCore::IR {
}
namespace FEXCore::Context {
struct Context;
class Context;
}
namespace FEXCore::Core {
+3 -3
View File
@@ -390,7 +390,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Bitfield") {
TEST_SINGLE(sxth(Size::i32Bit, Reg::r29, Reg::r28), "sxth w29, w28");
TEST_SINGLE(sxth(Size::i64Bit, Reg::r29, Reg::r28), "sxth x29, w28");
TEST_SINGLE(sxtw(XReg::x29, XReg::x28), "sxtw x29, w28");
TEST_SINGLE(sxtw(XReg::x29, WReg::w28), "sxtw x29, w28");
TEST_SINGLE(sbfx(Size::i32Bit, Reg::r29, Reg::r28, 4, 16), "sbfx w29, w28, #4, #16");
TEST_SINGLE(sbfx(Size::i64Bit, Reg::r29, Reg::r28, 4, 16), "sbfx x29, x28, #4, #16");
@@ -1504,8 +1504,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Rotate right into flags") {
TEST_SINGLE(rmif(XReg::x30, 63, 0b1111), "rmif x30, #63, #NZCV");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Evaluate into flags") {
TEST_SINGLE(setf8(XReg::x30), "setf8 w30");
TEST_SINGLE(setf16(XReg::x30), "setf16 w30");
TEST_SINGLE(setf8(WReg::w30), "setf8 w30");
TEST_SINGLE(setf16(WReg::w30), "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");
+15 -200
View File
@@ -40,18 +40,33 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD table lookup")
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q25), "tbx v30.16b, {v26.16b}, v25.16b");
TEST_SINGLE(tbx(DReg::d30, QReg::q26, DReg::d25), "tbx v30.8b, {v26.16b}, v25.8b");
TEST_SINGLE(tbl(QReg::q30, QReg::q31, QReg::q0, QReg::q25), "tbl v30.16b, {v31.16b, v0.16b}, v25.16b");
TEST_SINGLE(tbl(DReg::d30, QReg::q31, QReg::q0, DReg::d25), "tbl v30.8b, {v31.16b, v0.16b}, v25.8b");
TEST_SINGLE(tbl(QReg::q30, QReg::q26, QReg::q27, QReg::q25), "tbl v30.16b, {v26.16b, v27.16b}, v25.16b");
TEST_SINGLE(tbl(DReg::d30, QReg::q26, QReg::q27, DReg::d25), "tbl v30.8b, {v26.16b, v27.16b}, v25.8b");
TEST_SINGLE(tbx(QReg::q30, QReg::q31, QReg::q0, QReg::q25), "tbx v30.16b, {v31.16b, v0.16b}, v25.16b");
TEST_SINGLE(tbx(DReg::d30, QReg::q31, QReg::q0, DReg::d25), "tbx v30.8b, {v31.16b, v0.16b}, v25.8b");
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q27, QReg::q25), "tbx v30.16b, {v26.16b, v27.16b}, v25.16b");
TEST_SINGLE(tbx(DReg::d30, QReg::q26, QReg::q27, DReg::d25), "tbx v30.8b, {v26.16b, v27.16b}, v25.8b");
TEST_SINGLE(tbl(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q25), "tbl v30.16b, {v31.16b, v0.16b, v1.16b}, v25.16b");
TEST_SINGLE(tbl(DReg::d30, QReg::q31, QReg::q0, QReg::q1, DReg::d25), "tbl v30.8b, {v31.16b, v0.16b, v1.16b}, v25.8b");
TEST_SINGLE(tbl(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q25), "tbl v30.16b, {v26.16b, v27.16b, v28.16b}, v25.16b");
TEST_SINGLE(tbl(DReg::d30, QReg::q26, QReg::q27, QReg::q28, DReg::d25), "tbl v30.8b, {v26.16b, v27.16b, v28.16b}, v25.8b");
TEST_SINGLE(tbx(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q25), "tbx v30.16b, {v31.16b, v0.16b, v1.16b}, v25.16b");
TEST_SINGLE(tbx(DReg::d30, QReg::q31, QReg::q0, QReg::q1, DReg::d25), "tbx v30.8b, {v31.16b, v0.16b, v1.16b}, v25.8b");
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q25), "tbx v30.16b, {v26.16b, v27.16b, v28.16b}, v25.16b");
TEST_SINGLE(tbx(DReg::d30, QReg::q26, QReg::q27, QReg::q28, DReg::d25), "tbx v30.8b, {v26.16b, v27.16b, v28.16b}, v25.8b");
TEST_SINGLE(tbl(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, QReg::q25), "tbl v30.16b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.16b");
TEST_SINGLE(tbl(DReg::d30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, DReg::d25), "tbl v30.8b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.8b");
TEST_SINGLE(tbl(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, QReg::q25), "tbl v30.16b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.16b");
TEST_SINGLE(tbl(DReg::d30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, DReg::d25), "tbl v30.8b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.8b");
TEST_SINGLE(tbx(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, QReg::q25), "tbx v30.16b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.16b");
TEST_SINGLE(tbx(DReg::d30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, DReg::d25), "tbx v30.8b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.8b");
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, QReg::q25), "tbx v30.16b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.16b");
TEST_SINGLE(tbx(DReg::d30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, DReg::d25), "tbx v30.8b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.8b");
}
@@ -815,11 +830,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register m
TEST_SINGLE(sqxtun2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "sqxtun2 v30.4s, v29.2d");
//TEST_SINGLE(sqxtun2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "sqxtun2 v30.2d, v29.1d");
//TEST_SINGLE(shll(SubRegSize::i8Bit, QReg::q30, QReg::q29), "shll v30.16b, v29.16b, #0");
TEST_SINGLE(shll(SubRegSize::i16Bit, QReg::q30, QReg::q29), "shll v30.8h, v29.8b, #8");
TEST_SINGLE(shll(SubRegSize::i32Bit, QReg::q30, QReg::q29), "shll v30.4s, v29.4h, #16");
TEST_SINGLE(shll(SubRegSize::i64Bit, QReg::q30, QReg::q29), "shll v30.2d, v29.2s, #32");
//TEST_SINGLE(shll(SubRegSize::i8Bit, DReg::d30, DReg::d29), "shll v30.8b, v29.8b, #0");
TEST_SINGLE(shll(SubRegSize::i16Bit, DReg::d30, DReg::d29), "shll v30.8h, v29.8b, #8");
TEST_SINGLE(shll(SubRegSize::i32Bit, DReg::d30, DReg::d29), "shll v30.4s, v29.4h, #16");
@@ -830,11 +840,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register m
TEST_SINGLE(shll2(SubRegSize::i32Bit, QReg::q30, QReg::q29), "shll2 v30.4s, v29.8h, #16");
TEST_SINGLE(shll2(SubRegSize::i64Bit, QReg::q30, QReg::q29), "shll2 v30.2d, v29.4s, #32");
//TEST_SINGLE(shll2(SubRegSize::i8Bit, DReg::d30, DReg::d29), "shll2 v30.8b, v29.8b, #0");
TEST_SINGLE(shll2(SubRegSize::i16Bit, DReg::d30, DReg::d29), "shll2 v30.8h, v29.16b, #8");
TEST_SINGLE(shll2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "shll2 v30.4s, v29.8h, #16");
TEST_SINGLE(shll2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "shll2 v30.2d, v29.4s, #32");
TEST_SINGLE(uqxtn(SubRegSize::i8Bit, QReg::q30, QReg::q29), "uqxtn v30.8b, v29.8h");
TEST_SINGLE(uqxtn(SubRegSize::i16Bit, QReg::q30, QReg::q29), "uqxtn v30.4h, v29.4s");
TEST_SINGLE(uqxtn(SubRegSize::i32Bit, QReg::q30, QReg::q29), "uqxtn v30.2s, v29.2d");
@@ -2221,15 +2226,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(sqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqshl v30.1d, v29.1d, #1");
//TEST_SINGLE(sqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqshl v30.1d, v29.1d, #63");
TEST_SINGLE(shrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "shrn v30.8b, v29.8h, #1");
TEST_SINGLE(shrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "shrn v30.8b, v29.8h, #7");
TEST_SINGLE(shrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "shrn v30.4h, v29.4s, #1");
TEST_SINGLE(shrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "shrn v30.4h, v29.4s, #15");
TEST_SINGLE(shrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "shrn v30.2s, v29.2d, #1");
TEST_SINGLE(shrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "shrn v30.2s, v29.2d, #31");
//TEST_SINGLE(shrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "shrn v30.2d, v29.2d, #1");
//TEST_SINGLE(shrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "shrn v30.2d, v29.2d, #63");
TEST_SINGLE(shrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "shrn v30.8b, v29.8h, #1");
TEST_SINGLE(shrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "shrn v30.8b, v29.8h, #7");
TEST_SINGLE(shrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "shrn v30.4h, v29.4s, #1");
@@ -2248,24 +2244,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "shrn2 v30.2d, v29.2d, #1");
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "shrn2 v30.2d, v29.2d, #63");
TEST_SINGLE(shrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.16b, v29.8h, #1");
TEST_SINGLE(shrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "shrn2 v30.16b, v29.8h, #7");
TEST_SINGLE(shrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.8h, v29.4s, #1");
TEST_SINGLE(shrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "shrn2 v30.8h, v29.4s, #15");
TEST_SINGLE(shrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.4s, v29.2d, #1");
TEST_SINGLE(shrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "shrn2 v30.4s, v29.2d, #31");
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.1d, v29.1d, #1");
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "shrn2 v30.1d, v29.1d, #63");
TEST_SINGLE(rshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "rshrn v30.8b, v29.8h, #1");
TEST_SINGLE(rshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "rshrn v30.8b, v29.8h, #7");
TEST_SINGLE(rshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "rshrn v30.4h, v29.4s, #1");
TEST_SINGLE(rshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "rshrn v30.4h, v29.4s, #15");
TEST_SINGLE(rshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "rshrn v30.2s, v29.2d, #1");
TEST_SINGLE(rshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "rshrn v30.2s, v29.2d, #31");
//TEST_SINGLE(rshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "rshrn v30.2d, v29.2d, #1");
//TEST_SINGLE(rshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "rshrn v30.2d, v29.2d, #63");
TEST_SINGLE(rshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "rshrn v30.8b, v29.8h, #1");
TEST_SINGLE(rshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "rshrn v30.8b, v29.8h, #7");
TEST_SINGLE(rshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "rshrn v30.4h, v29.4s, #1");
@@ -2284,24 +2262,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "rshrn2 v30.2d, v29.2d, #1");
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "rshrn2 v30.2d, v29.2d, #63");
TEST_SINGLE(rshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.16b, v29.8h, #1");
TEST_SINGLE(rshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "rshrn2 v30.16b, v29.8h, #7");
TEST_SINGLE(rshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.8h, v29.4s, #1");
TEST_SINGLE(rshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "rshrn2 v30.8h, v29.4s, #15");
TEST_SINGLE(rshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.4s, v29.2d, #1");
TEST_SINGLE(rshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "rshrn2 v30.4s, v29.2d, #31");
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.1d, v29.1d, #1");
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "rshrn2 v30.1d, v29.1d, #63");
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.8b, v29.8h, #1");
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqshrn v30.8b, v29.8h, #7");
TEST_SINGLE(sqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.4h, v29.4s, #1");
TEST_SINGLE(sqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqshrn v30.4h, v29.4s, #15");
TEST_SINGLE(sqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.2s, v29.2d, #1");
TEST_SINGLE(sqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqshrn v30.2s, v29.2d, #31");
//TEST_SINGLE(sqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.2d, v29.2d, #1");
//TEST_SINGLE(sqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrn v30.2d, v29.2d, #63");
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrn v30.8b, v29.8h, #1");
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrn v30.8b, v29.8h, #7");
TEST_SINGLE(sqshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrn v30.4h, v29.4s, #1");
@@ -2320,24 +2280,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrn2 v30.2d, v29.2d, #1");
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrn2 v30.2d, v29.2d, #63");
TEST_SINGLE(sqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.16b, v29.8h, #1");
TEST_SINGLE(sqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrn2 v30.16b, v29.8h, #7");
TEST_SINGLE(sqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.8h, v29.4s, #1");
TEST_SINGLE(sqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqshrn2 v30.8h, v29.4s, #15");
TEST_SINGLE(sqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.4s, v29.2d, #1");
TEST_SINGLE(sqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqshrn2 v30.4s, v29.2d, #31");
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.1d, v29.1d, #1");
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqshrn2 v30.1d, v29.1d, #63");
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.8b, v29.8h, #1");
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqrshrn v30.8b, v29.8h, #7");
TEST_SINGLE(sqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.4h, v29.4s, #1");
TEST_SINGLE(sqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqrshrn v30.4h, v29.4s, #15");
TEST_SINGLE(sqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.2s, v29.2d, #1");
TEST_SINGLE(sqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqrshrn v30.2s, v29.2d, #31");
//TEST_SINGLE(sqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.2d, v29.2d, #1");
//TEST_SINGLE(sqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrn v30.2d, v29.2d, #63");
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrn v30.8b, v29.8h, #1");
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrn v30.8b, v29.8h, #7");
TEST_SINGLE(sqrshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrn v30.4h, v29.4s, #1");
@@ -2356,24 +2298,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrn2 v30.2d, v29.2d, #1");
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrn2 v30.2d, v29.2d, #63");
TEST_SINGLE(sqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.16b, v29.8h, #1");
TEST_SINGLE(sqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrn2 v30.16b, v29.8h, #7");
TEST_SINGLE(sqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.8h, v29.4s, #1");
TEST_SINGLE(sqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqrshrn2 v30.8h, v29.4s, #15");
TEST_SINGLE(sqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.4s, v29.2d, #1");
TEST_SINGLE(sqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqrshrn2 v30.4s, v29.2d, #31");
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.1d, v29.1d, #1");
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqrshrn2 v30.1d, v29.1d, #63");
//TEST_SINGLE(sshll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sshll v30.8b, v29.8h, #1");
//TEST_SINGLE(sshll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sshll v30.8b, v29.8h, #7");
TEST_SINGLE(sshll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sshll v30.8h, v29.8b, #1");
TEST_SINGLE(sshll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 7), "sshll v30.8h, v29.8b, #7");
TEST_SINGLE(sshll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sshll v30.4s, v29.4h, #1");
TEST_SINGLE(sshll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 15), "sshll v30.4s, v29.4h, #15");
TEST_SINGLE(sshll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sshll v30.2d, v29.2s, #1");
TEST_SINGLE(sshll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "sshll v30.2d, v29.2s, #31");
//TEST_SINGLE(sshll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sshll v30.8b, v29.8h, #1");
//TEST_SINGLE(sshll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sshll v30.8b, v29.8h, #7");
TEST_SINGLE(sshll(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sshll v30.8h, v29.8b, #1");
@@ -2392,15 +2316,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
TEST_SINGLE(sshll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sshll2 v30.2d, v29.4s, #1");
TEST_SINGLE(sshll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "sshll2 v30.2d, v29.4s, #31");
//TEST_SINGLE(sshll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.16b, v29.8h, #1");
//TEST_SINGLE(sshll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sshll2 v30.16b, v29.8h, #7");
TEST_SINGLE(sshll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.8h, v29.16b, #1");
TEST_SINGLE(sshll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 7), "sshll2 v30.8h, v29.16b, #7");
TEST_SINGLE(sshll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.4s, v29.8h, #1");
TEST_SINGLE(sshll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 15), "sshll2 v30.4s, v29.8h, #15");
TEST_SINGLE(sshll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.2d, v29.4s, #1");
TEST_SINGLE(sshll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 31), "sshll2 v30.2d, v29.4s, #31");
//TEST_SINGLE(sxtl(SubRegSize::i8Bit, QReg::q30, QReg::q29), "sxtl v30.8b, v29.8h");
TEST_SINGLE(sxtl(SubRegSize::i16Bit, QReg::q30, QReg::q29), "sxtl v30.8h, v29.8b");
TEST_SINGLE(sxtl(SubRegSize::i32Bit, QReg::q30, QReg::q29), "sxtl v30.4s, v29.4h");
@@ -2601,15 +2516,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(uqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "uqshl v30.1d, v29.1d, #1");
//TEST_SINGLE(uqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "uqshl v30.1d, v29.1d, #63");
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.8b, v29.8h, #1");
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqshrun v30.8b, v29.8h, #7");
TEST_SINGLE(sqshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.4h, v29.4s, #1");
TEST_SINGLE(sqshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqshrun v30.4h, v29.4s, #15");
TEST_SINGLE(sqshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.2s, v29.2d, #1");
TEST_SINGLE(sqshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqshrun v30.2s, v29.2d, #31");
//TEST_SINGLE(sqshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.2d, v29.2d, #1");
//TEST_SINGLE(sqshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrun v30.2d, v29.2d, #63");
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrun v30.8b, v29.8h, #1");
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrun v30.8b, v29.8h, #7");
TEST_SINGLE(sqshrun(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrun v30.4h, v29.4s, #1");
@@ -2628,24 +2534,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrun2 v30.2d, v29.2d, #1");
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrun2 v30.2d, v29.2d, #63");
TEST_SINGLE(sqshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.16b, v29.8h, #1");
TEST_SINGLE(sqshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrun2 v30.16b, v29.8h, #7");
TEST_SINGLE(sqshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.8h, v29.4s, #1");
TEST_SINGLE(sqshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqshrun2 v30.8h, v29.4s, #15");
TEST_SINGLE(sqshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.4s, v29.2d, #1");
TEST_SINGLE(sqshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqshrun2 v30.4s, v29.2d, #31");
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.1d, v29.1d, #1");
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqshrun2 v30.1d, v29.1d, #63");
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.8b, v29.8h, #1");
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqrshrun v30.8b, v29.8h, #7");
TEST_SINGLE(sqrshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.4h, v29.4s, #1");
TEST_SINGLE(sqrshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqrshrun v30.4h, v29.4s, #15");
TEST_SINGLE(sqrshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.2s, v29.2d, #1");
TEST_SINGLE(sqrshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqrshrun v30.2s, v29.2d, #31");
//TEST_SINGLE(sqrshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.2d, v29.2d, #1");
//TEST_SINGLE(sqrshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrun v30.2d, v29.2d, #63");
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrun v30.8b, v29.8h, #1");
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrun v30.8b, v29.8h, #7");
TEST_SINGLE(sqrshrun(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrun v30.4h, v29.4s, #1");
@@ -2664,24 +2552,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrun2 v30.2d, v29.2d, #1");
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrun2 v30.2d, v29.2d, #63");
TEST_SINGLE(sqrshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.16b, v29.8h, #1");
TEST_SINGLE(sqrshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrun2 v30.16b, v29.8h, #7");
TEST_SINGLE(sqrshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.8h, v29.4s, #1");
TEST_SINGLE(sqrshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqrshrun2 v30.8h, v29.4s, #15");
TEST_SINGLE(sqrshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.4s, v29.2d, #1");
TEST_SINGLE(sqrshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqrshrun2 v30.4s, v29.2d, #31");
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.1d, v29.1d, #1");
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqrshrun2 v30.1d, v29.1d, #63");
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.8b, v29.8h, #1");
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "uqshrn v30.8b, v29.8h, #7");
TEST_SINGLE(uqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.4h, v29.4s, #1");
TEST_SINGLE(uqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "uqshrn v30.4h, v29.4s, #15");
TEST_SINGLE(uqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.2s, v29.2d, #1");
TEST_SINGLE(uqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "uqshrn v30.2s, v29.2d, #31");
//TEST_SINGLE(uqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.2d, v29.2d, #1");
//TEST_SINGLE(uqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqshrn v30.2d, v29.2d, #63");
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqshrn v30.8b, v29.8h, #1");
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqshrn v30.8b, v29.8h, #7");
TEST_SINGLE(uqshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqshrn v30.4h, v29.4s, #1");
@@ -2700,24 +2570,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqshrn2 v30.2d, v29.2d, #1");
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqshrn2 v30.2d, v29.2d, #63");
TEST_SINGLE(uqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.16b, v29.8h, #1");
TEST_SINGLE(uqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqshrn2 v30.16b, v29.8h, #7");
TEST_SINGLE(uqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.8h, v29.4s, #1");
TEST_SINGLE(uqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "uqshrn2 v30.8h, v29.4s, #15");
TEST_SINGLE(uqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.4s, v29.2d, #1");
TEST_SINGLE(uqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "uqshrn2 v30.4s, v29.2d, #31");
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.1d, v29.1d, #1");
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "uqshrn2 v30.1d, v29.1d, #63");
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.8b, v29.8h, #1");
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "uqrshrn v30.8b, v29.8h, #7");
TEST_SINGLE(uqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.4h, v29.4s, #1");
TEST_SINGLE(uqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "uqrshrn v30.4h, v29.4s, #15");
TEST_SINGLE(uqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.2s, v29.2d, #1");
TEST_SINGLE(uqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "uqrshrn v30.2s, v29.2d, #31");
//TEST_SINGLE(uqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.2d, v29.2d, #1");
//TEST_SINGLE(uqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqrshrn v30.2d, v29.2d, #63");
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqrshrn v30.8b, v29.8h, #1");
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqrshrn v30.8b, v29.8h, #7");
TEST_SINGLE(uqrshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqrshrn v30.4h, v29.4s, #1");
@@ -2736,24 +2588,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqrshrn2 v30.2d, v29.2d, #1");
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqrshrn2 v30.2d, v29.2d, #63");
TEST_SINGLE(uqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.16b, v29.8h, #1");
TEST_SINGLE(uqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqrshrn2 v30.16b, v29.8h, #7");
TEST_SINGLE(uqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.8h, v29.4s, #1");
TEST_SINGLE(uqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "uqrshrn2 v30.8h, v29.4s, #15");
TEST_SINGLE(uqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.4s, v29.2d, #1");
TEST_SINGLE(uqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "uqrshrn2 v30.4s, v29.2d, #31");
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.1d, v29.1d, #1");
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "uqrshrn2 v30.1d, v29.1d, #63");
//TEST_SINGLE(ushll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "ushll v30.8b, v29.8h, #1");
//TEST_SINGLE(ushll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "ushll v30.8b, v29.8h, #7");
TEST_SINGLE(ushll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "ushll v30.8h, v29.8b, #1");
TEST_SINGLE(ushll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 7), "ushll v30.8h, v29.8b, #7");
TEST_SINGLE(ushll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "ushll v30.4s, v29.4h, #1");
TEST_SINGLE(ushll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 15), "ushll v30.4s, v29.4h, #15");
TEST_SINGLE(ushll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "ushll v30.2d, v29.2s, #1");
TEST_SINGLE(ushll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "ushll v30.2d, v29.2s, #31");
//TEST_SINGLE(ushll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "ushll v30.8b, v29.8h, #1");
//TEST_SINGLE(ushll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "ushll v30.8b, v29.8h, #7");
TEST_SINGLE(ushll(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "ushll v30.8h, v29.8b, #1");
@@ -2772,20 +2606,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
TEST_SINGLE(ushll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "ushll2 v30.2d, v29.4s, #1");
TEST_SINGLE(ushll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "ushll2 v30.2d, v29.4s, #31");
//TEST_SINGLE(ushll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.16b, v29.8h, #1");
//TEST_SINGLE(ushll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "ushll2 v30.16b, v29.8h, #7");
TEST_SINGLE(ushll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.8h, v29.16b, #1");
TEST_SINGLE(ushll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 7), "ushll2 v30.8h, v29.16b, #7");
TEST_SINGLE(ushll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.4s, v29.8h, #1");
TEST_SINGLE(ushll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 15), "ushll2 v30.4s, v29.8h, #15");
TEST_SINGLE(ushll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.2d, v29.4s, #1");
TEST_SINGLE(ushll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 31), "ushll2 v30.2d, v29.4s, #31");
//TEST_SINGLE(uxtl(SubRegSize::i8Bit, QReg::q30, QReg::q29), "uxtl v30.8b, v29.8h");
TEST_SINGLE(uxtl(SubRegSize::i16Bit, QReg::q30, QReg::q29), "uxtl v30.8h, v29.8b");
TEST_SINGLE(uxtl(SubRegSize::i32Bit, QReg::q30, QReg::q29), "uxtl v30.4s, v29.4h");
TEST_SINGLE(uxtl(SubRegSize::i64Bit, QReg::q30, QReg::q29), "uxtl v30.2d, v29.2s");
//TEST_SINGLE(uxtl(SubRegSize::i8Bit, DReg::d30, DReg::d29), "uxtl v30.8b, v29.8h");
TEST_SINGLE(uxtl(SubRegSize::i16Bit, DReg::d30, DReg::d29), "uxtl v30.8h, v29.8b");
TEST_SINGLE(uxtl(SubRegSize::i32Bit, DReg::d30, DReg::d29), "uxtl v30.4s, v29.4h");
@@ -2796,11 +2616,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
TEST_SINGLE(uxtl2(SubRegSize::i32Bit, QReg::q30, QReg::q29), "uxtl2 v30.4s, v29.8h");
TEST_SINGLE(uxtl2(SubRegSize::i64Bit, QReg::q30, QReg::q29), "uxtl2 v30.2d, v29.4s");
//TEST_SINGLE(uxtl2(SubRegSize::i8Bit, DReg::d30, DReg::d29), "uxtl2 v30.16b, v29.8h");
TEST_SINGLE(uxtl2(SubRegSize::i16Bit, DReg::d30, DReg::d29), "uxtl2 v30.8h, v29.16b");
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");
//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");
+210 -102
View File
@@ -31,6 +31,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, Reg::r30), "ld1 {v26.2d}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, Reg::r30), "ld1 {v26.1d}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "ld1 {v31.16b, v0.16b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "ld1 {v31.8b, v0.8b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "ld1 {v26.16b, v27.16b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "ld1 {v26.8b, v27.8b}, [x30]");
@@ -43,6 +45,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "ld1 {v26.2d, v27.2d}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "ld1 {v26.1d, v27.1d}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "ld1 {v31.16b, v0.16b, v1.16b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "ld1 {v31.8b, v0.8b, v1.8b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld1 {v26.16b, v27.16b, v28.16b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld1 {v26.8b, v27.8b, v28.8b}, [x30]");
@@ -55,6 +59,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld1 {v26.2d, v27.2d, v28.2d}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld1 {v26.1d, v27.1d, v28.1d}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "ld1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "ld1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
@@ -79,6 +85,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, Reg::r30), "st1 {v26.2d}, [x30]");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, Reg::r30), "st1 {v26.1d}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "st1 {v31.16b, v0.16b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "st1 {v31.8b, v0.8b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "st1 {v26.16b, v27.16b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "st1 {v26.8b, v27.8b}, [x30]");
@@ -91,6 +99,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "st1 {v26.2d, v27.2d}, [x30]");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "st1 {v26.1d, v27.1d}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "st1 {v31.16b, v0.16b, v1.16b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "st1 {v31.8b, v0.8b, v1.8b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st1 {v26.16b, v27.16b, v28.16b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "st1 {v26.8b, v27.8b, v28.8b}, [x30]");
@@ -103,6 +113,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st1 {v26.2d, v27.2d, v28.2d}, [x30]");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "st1 {v26.1d, v27.1d, v28.1d}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "st1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "st1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "st1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "st1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
@@ -115,6 +127,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "st1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30]");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "st1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "ld2 {v31.16b, v0.16b}, [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "ld2 {v31.8b, v0.8b}, [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2 {v26.16b, v27.16b}, [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2 {v26.8b, v27.8b}, [x30]");
@@ -127,6 +141,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2 {v26.2d, v27.2d}, [x30]");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "st2 {v31.16b, v0.16b}, [x30]");
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "st2 {v31.8b, v0.8b}, [x30]");
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "st2 {v26.16b, v27.16b}, [x30]");
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "st2 {v26.8b, v27.8b}, [x30]");
@@ -139,6 +155,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "st2 {v26.2d, v27.2d}, [x30]");
TEST_SINGLE(st2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "ld3 {v31.16b, v0.16b, v1.16b}, [x30]");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "ld3 {v31.8b, v0.8b, v1.8b}, [x30]");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3 {v26.16b, v27.16b, v28.16b}, [x30]");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3 {v26.8b, v27.8b, v28.8b}, [x30]");
@@ -151,6 +169,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3 {v26.2d, v27.2d, v28.2d}, [x30]");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "st3 {v31.16b, v0.16b, v1.16b}, [x30]");
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "st3 {v31.8b, v0.8b, v1.8b}, [x30]");
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st3 {v26.16b, v27.16b, v28.16b}, [x30]");
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "st3 {v26.8b, v27.8b, v28.8b}, [x30]");
@@ -163,6 +183,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st3 {v26.2d, v27.2d, v28.2d}, [x30]");
TEST_SINGLE(st3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "ld4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "ld4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
@@ -175,6 +197,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30]");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "st4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "st4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "st4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "st4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
@@ -213,6 +237,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, Reg::r30, 16), "ld1 {v26.2d}, [x30], #16");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, Reg::r30, 8), "ld1 {v26.1d}, [x30], #8");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "ld1 {v31.16b, v0.16b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "ld1 {v31.8b, v0.8b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld1 {v26.16b, v27.16b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld1 {v26.8b, v27.8b}, [x30], x29");
@@ -225,6 +251,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld1 {v26.2d, v27.2d}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld1 {v26.1d, v27.1d}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "ld1 {v31.16b, v0.16b}, [x30], #32");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "ld1 {v31.8b, v0.8b}, [x30], #16");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld1 {v26.16b, v27.16b}, [x30], #32");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld1 {v26.8b, v27.8b}, [x30], #16");
@@ -237,6 +265,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld1 {v26.2d, v27.2d}, [x30], #32");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld1 {v26.1d, v27.1d}, [x30], #16");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "ld1 {v31.16b, v0.16b, v1.16b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "ld1 {v31.8b, v0.8b, v1.8b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld1 {v26.16b, v27.16b, v28.16b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld1 {v26.8b, v27.8b, v28.8b}, [x30], x29");
@@ -249,6 +279,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld1 {v26.2d, v27.2d, v28.2d}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld1 {v26.1d, v27.1d, v28.1d}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "ld1 {v31.16b, v0.16b, v1.16b}, [x30], #48");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "ld1 {v31.8b, v0.8b, v1.8b}, [x30], #24");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld1 {v26.16b, v27.16b, v28.16b}, [x30], #48");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld1 {v26.8b, v27.8b, v28.8b}, [x30], #24");
@@ -261,6 +293,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld1 {v26.2d, v27.2d, v28.2d}, [x30], #48");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld1 {v26.1d, v27.1d, v28.1d}, [x30], #24");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "ld1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "ld1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
@@ -273,6 +307,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "ld1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "ld1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "ld1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "ld1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
@@ -309,6 +345,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, Reg::r30, 16), "st1 {v26.2d}, [x30], #16");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, Reg::r30, 8), "st1 {v26.1d}, [x30], #8");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "st1 {v31.16b, v0.16b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "st1 {v31.8b, v0.8b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st1 {v26.16b, v27.16b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "st1 {v26.8b, v27.8b}, [x30], x29");
@@ -321,6 +359,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st1 {v26.2d, v27.2d}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "st1 {v26.1d, v27.1d}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "st1 {v31.16b, v0.16b}, [x30], #32");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "st1 {v31.8b, v0.8b}, [x30], #16");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st1 {v26.16b, v27.16b}, [x30], #32");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "st1 {v26.8b, v27.8b}, [x30], #16");
@@ -333,6 +373,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st1 {v26.2d, v27.2d}, [x30], #32");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "st1 {v26.1d, v27.1d}, [x30], #16");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "st1 {v31.16b, v0.16b, v1.16b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "st1 {v31.8b, v0.8b, v1.8b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st1 {v26.16b, v27.16b, v28.16b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "st1 {v26.8b, v27.8b, v28.8b}, [x30], x29");
@@ -345,6 +387,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st1 {v26.2d, v27.2d, v28.2d}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "st1 {v26.1d, v27.1d, v28.1d}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "st1 {v31.16b, v0.16b, v1.16b}, [x30], #48");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "st1 {v31.8b, v0.8b, v1.8b}, [x30], #24");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st1 {v26.16b, v27.16b, v28.16b}, [x30], #48");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "st1 {v26.8b, v27.8b, v28.8b}, [x30], #24");
@@ -357,6 +401,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st1 {v26.2d, v27.2d, v28.2d}, [x30], #48");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "st1 {v26.1d, v27.1d, v28.1d}, [x30], #24");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "st1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "st1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "st1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
@@ -369,6 +415,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "st1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], x29");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "st1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "st1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "st1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "st1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
@@ -381,6 +429,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "st1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], #64");
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "st1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], #32");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "ld2 {v31.16b, v0.16b}, [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "ld2 {v31.8b, v0.8b}, [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2 {v26.16b, v27.16b}, [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2 {v26.8b, v27.8b}, [x30], x29");
@@ -393,6 +443,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2 {v26.2d, v27.2d}, [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "ld2 {v31.16b, v0.16b}, [x30], #32");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "ld2 {v31.8b, v0.8b}, [x30], #16");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld2 {v26.16b, v27.16b}, [x30], #32");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld2 {v26.8b, v27.8b}, [x30], #16");
@@ -405,6 +457,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld2 {v26.2d, v27.2d}, [x30], #32");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "st2 {v31.16b, v0.16b}, [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "st2 {v31.8b, v0.8b}, [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st2 {v26.16b, v27.16b}, [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "st2 {v26.8b, v27.8b}, [x30], x29");
@@ -417,6 +471,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st2 {v26.2d, v27.2d}, [x30], x29");
TEST_SINGLE(st2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "st2 {v31.16b, v0.16b}, [x30], #32");
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "st2 {v31.8b, v0.8b}, [x30], #16");
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st2 {v26.16b, v27.16b}, [x30], #32");
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "st2 {v26.8b, v27.8b}, [x30], #16");
@@ -429,6 +485,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st2 {v26.2d, v27.2d}, [x30], #32");
TEST_SINGLE(st2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "ld3 {v31.16b, v0.16b, v1.16b}, [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "ld3 {v31.8b, v0.8b, v1.8b}, [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3 {v26.16b, v27.16b, v28.16b}, [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3 {v26.8b, v27.8b, v28.8b}, [x30], x29");
@@ -441,6 +499,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3 {v26.2d, v27.2d, v28.2d}, [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "ld3 {v31.16b, v0.16b, v1.16b}, [x30], #48");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "ld3 {v31.8b, v0.8b, v1.8b}, [x30], #24");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld3 {v26.16b, v27.16b, v28.16b}, [x30], #48");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld3 {v26.8b, v27.8b, v28.8b}, [x30], #24");
@@ -453,6 +513,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld3 {v26.2d, v27.2d, v28.2d}, [x30], #48");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "st3 {v31.16b, v0.16b, v1.16b}, [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "st3 {v31.8b, v0.8b, v1.8b}, [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st3 {v26.16b, v27.16b, v28.16b}, [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "st3 {v26.8b, v27.8b, v28.8b}, [x30], x29");
@@ -465,6 +527,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st3 {v26.2d, v27.2d, v28.2d}, [x30], x29");
TEST_SINGLE(st3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "st3 {v31.16b, v0.16b, v1.16b}, [x30], #48");
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "st3 {v31.8b, v0.8b, v1.8b}, [x30], #24");
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st3 {v26.16b, v27.16b, v28.16b}, [x30], #48");
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "st3 {v26.8b, v27.8b, v28.8b}, [x30], #24");
@@ -477,6 +541,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st3 {v26.2d, v27.2d, v28.2d}, [x30], #48");
TEST_SINGLE(st3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "ld4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "ld4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
@@ -489,6 +555,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "ld4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "ld4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "ld4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "ld4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
@@ -501,6 +569,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "ld4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], #64");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "st4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "st4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "st4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
@@ -513,6 +583,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
TEST_SINGLE(st4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "st4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "st4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "st4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "st4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
@@ -531,7 +603,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: ASIMD loadstore single")
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30), "ld1 {v26.s}[0], [x30]");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30), "ld1 {v26.d}[0], [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "ld1 {v26.b}[15], [x30]");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "ld1 {v26.b}[15], [x30]");
TEST_SINGLE(ld1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30), "ld1 {v26.h}[7], [x30]");
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30), "ld1 {v26.s}[3], [x30]");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30), "ld1 {v26.d}[1], [x30]");
@@ -551,71 +623,80 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: ASIMD loadstore single")
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30), "st1 {v26.s}[0], [x30]");
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30), "st1 {v26.d}[0], [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "st1 {v26.b}[15], [x30]");
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "st1 {v26.b}[15], [x30]");
TEST_SINGLE(st1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30), "st1 {v26.h}[7], [x30]");
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30), "st1 {v26.s}[3], [x30]");
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30), "st1 {v26.d}[1], [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30), "ld2 {v31.b, v0.b}[0], [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.b, v27.b}[0], [x30]");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.h, v27.h}[0], [x30]");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.s, v27.s}[0], [x30]");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.d, v27.d}[0], [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "ld2 {v26.b, v27.b}[15], [x30]");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "ld2 {v26.b, v27.b}[15], [x30]");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30), "ld2 {v26.h, v27.h}[7], [x30]");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30), "ld2 {v26.s, v27.s}[3], [x30]");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30), "ld2 {v26.d, v27.d}[1], [x30]");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "ld2r {v31.8b, v0.8b}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.8b, v27.8b}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.4h, v27.4h}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.2s, v27.2s}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.1d, v27.1d}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "ld2r {v31.16b, v0.16b}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.16b, v27.16b}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.8h, v27.8h}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.4s, v27.4s}, [x30]");
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.2d, v27.2d}, [x30]");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30), "st2 {v31.b, v0.b}[0], [x30]");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.b, v27.b}[0], [x30]");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.h, v27.h}[0], [x30]");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.s, v27.s}[0], [x30]");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.d, v27.d}[0], [x30]");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "st2 {v26.b, v27.b}[15], [x30]");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "st2 {v26.b, v27.b}[15], [x30]");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30), "st2 {v26.h, v27.h}[7], [x30]");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30), "st2 {v26.s, v27.s}[3], [x30]");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30), "st2 {v26.d, v27.d}[1], [x30]");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30), "ld3 {v31.b, v0.b, v1.b}[0], [x30]");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.b, v27.b, v28.b}[0], [x30]");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.h, v27.h, v28.h}[0], [x30]");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.s, v27.s, v28.s}[0], [x30]");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.d, v27.d, v28.d}[0], [x30]");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "ld3 {v26.b, v27.b, v28.b}[15], [x30]");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "ld3 {v26.b, v27.b, v28.b}[15], [x30]");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30), "ld3 {v26.h, v27.h, v28.h}[7], [x30]");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30), "ld3 {v26.s, v27.s, v28.s}[3], [x30]");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30), "ld3 {v26.d, v27.d, v28.d}[1], [x30]");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "ld3r {v31.8b, v0.8b, v1.8b}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.8b, v27.8b, v28.8b}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.4h, v27.4h, v28.4h}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.2s, v27.2s, v28.2s}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.1d, v27.1d, v28.1d}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "ld3r {v31.16b, v0.16b, v1.16b}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.16b, v27.16b, v28.16b}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.8h, v27.8h, v28.8h}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.4s, v27.4s, v28.4s}, [x30]");
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.2d, v27.2d, v28.2d}, [x30]");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30), "st3 {v31.b, v0.b, v1.b}[0], [x30]");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.b, v27.b, v28.b}[0], [x30]");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.h, v27.h, v28.h}[0], [x30]");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.s, v27.s, v28.s}[0], [x30]");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.d, v27.d, v28.d}[0], [x30]");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "st3 {v26.b, v27.b, v28.b}[15], [x30]");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "st3 {v26.b, v27.b, v28.b}[15], [x30]");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30), "st3 {v26.h, v27.h, v28.h}[7], [x30]");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30), "st3 {v26.s, v27.s, v28.s}[3], [x30]");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30), "st3 {v26.d, v27.d, v28.d}[1], [x30]");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30), "ld4 {v31.b, v0.b, v1.b, v2.b}[0], [x30]");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30]");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30]");
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "ld4 {v26.s, v27.s, v28.s, v29.s}[0], [x30]");
@@ -626,22 +707,25 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: ASIMD loadstore single")
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30]");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30]");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "ld4r {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.2s, v27.2s, v28.2s, v29.2s}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.1d, v27.1d, v28.1d, v29.1d}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "ld4r {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.4s, v27.4s, v28.4s, v29.4s}, [x30]");
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.2d, v27.2d, v28.2d, v29.2d}, [x30]");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30), "st4 {v31.b, v0.b, v1.b, v2.b}[0], [x30]");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30]");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30]");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.s, v27.s, v28.s, v29.s}[0], [x30]");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.d, v27.d, v28.d, v29.d}[0], [x30]");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30]");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30]");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30]");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30]");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30]");
@@ -652,7 +736,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30, 4), "ld1 {v26.s}[0], [x30], #4");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30, 8), "ld1 {v26.d}[0], [x30], #8");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "ld1 {v26.b}[15], [x30], #1");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "ld1 {v26.b}[15], [x30], #1");
TEST_SINGLE(ld1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30, 2), "ld1 {v26.h}[7], [x30], #2");
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30, 4), "ld1 {v26.s}[3], [x30], #4");
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30, 8), "ld1 {v26.d}[1], [x30], #8");
@@ -672,100 +756,112 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30, 4), "st1 {v26.s}[0], [x30], #4");
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30, 8), "st1 {v26.d}[0], [x30], #8");
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "st1 {v26.b}[15], [x30], #1");
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "st1 {v26.b}[15], [x30], #1");
TEST_SINGLE(st1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30, 2), "st1 {v26.h}[7], [x30], #2");
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30, 4), "st1 {v26.s}[3], [x30], #4");
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30, 8), "st1 {v26.d}[1], [x30], #8");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "ld2 {v26.b, v27.b}[0], [x30], #2");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "ld2 {v26.h, v27.h}[0], [x30], #4");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "ld2 {v26.s, v27.s}[0], [x30], #8");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, 2), "ld2 {v31.b, v0.b}[0], [x30], #2");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "ld2 {v26.b, v27.b}[0], [x30], #2");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "ld2 {v26.h, v27.h}[0], [x30], #4");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "ld2 {v26.s, v27.s}[0], [x30], #8");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 16), "ld2 {v26.d, v27.d}[0], [x30], #16");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, 2), "ld2 {v26.b, v27.b}[15], [x30], #2");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "ld2 {v26.h, v27.h}[7], [x30], #4");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "ld2 {v26.s, v27.s}[3], [x30], #8");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "ld2 {v26.h, v27.h}[7], [x30], #4");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "ld2 {v26.s, v27.s}[3], [x30], #8");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, 16), "ld2 {v26.d, v27.d}[1], [x30], #16");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 2), "ld2r {v26.8b, v27.8b}, [x30], #2");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30, 4), "ld2r {v26.4h, v27.4h}, [x30], #4");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30, 8), "ld2r {v26.2s, v27.2s}, [x30], #8");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 2), "ld2r {v31.8b, v0.8b}, [x30], #2");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 2), "ld2r {v26.8b, v27.8b}, [x30], #2");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30, 4), "ld2r {v26.4h, v27.4h}, [x30], #4");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30, 8), "ld2r {v26.2s, v27.2s}, [x30], #8");
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld2r {v26.1d, v27.1d}, [x30], #16");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 2), "ld2r {v26.16b, v27.16b}, [x30], #2");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30, 4), "ld2r {v26.8h, v27.8h}, [x30], #4");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30, 8), "ld2r {v26.4s, v27.4s}, [x30], #8");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 2), "ld2r {v31.16b, v0.16b}, [x30], #2");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 2), "ld2r {v26.16b, v27.16b}, [x30], #2");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30, 4), "ld2r {v26.8h, v27.8h}, [x30], #4");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30, 8), "ld2r {v26.4s, v27.4s}, [x30], #8");
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 16), "ld2r {v26.2d, v27.2d}, [x30], #16");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "st2 {v26.b, v27.b}[0], [x30], #2");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "st2 {v26.h, v27.h}[0], [x30], #4");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "st2 {v26.s, v27.s}[0], [x30], #8");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, 2), "st2 {v31.b, v0.b}[0], [x30], #2");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "st2 {v26.b, v27.b}[0], [x30], #2");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "st2 {v26.h, v27.h}[0], [x30], #4");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "st2 {v26.s, v27.s}[0], [x30], #8");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 16), "st2 {v26.d, v27.d}[0], [x30], #16");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, 2), "st2 {v26.b, v27.b}[15], [x30], #2");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "st2 {v26.h, v27.h}[7], [x30], #4");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "st2 {v26.s, v27.s}[3], [x30], #8");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, 16), "st2 {v26.d, v27.d}[1], [x30], #16");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, 2), "st2 {v26.b, v27.b}[15], [x30], #2");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "st2 {v26.h, v27.h}[7], [x30], #4");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "st2 {v26.s, v27.s}[3], [x30], #8");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, 16), "st2 {v26.d, v27.d}[1], [x30], #16");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "ld3 {v26.b, v27.b, v28.b}[0], [x30], #3");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "ld3 {v26.h, v27.h, v28.h}[0], [x30], #6");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, 3), "ld3 {v31.b, v0.b, v1.b}[0], [x30], #3");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "ld3 {v26.b, v27.b, v28.b}[0], [x30], #3");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "ld3 {v26.h, v27.h, v28.h}[0], [x30], #6");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 12), "ld3 {v26.s, v27.s, v28.s}[0], [x30], #12");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 24), "ld3 {v26.d, v27.d, v28.d}[0], [x30], #24");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 1), "ld3 {v26.b, v27.b, v28.b}[15], [x30], #3");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 2), "ld3 {v26.h, v27.h, v28.h}[7], [x30], #6");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 4), "ld3 {v26.s, v27.s, v28.s}[3], [x30], #12");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 8), "ld3 {v26.d, v27.d, v28.d}[1], [x30], #24");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 1), "ld3 {v26.b, v27.b, v28.b}[15], [x30], #3");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 2), "ld3 {v26.h, v27.h, v28.h}[7], [x30], #6");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 4), "ld3 {v26.s, v27.s, v28.s}[3], [x30], #12");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 8), "ld3 {v26.d, v27.d, v28.d}[1], [x30], #24");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 3), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], #3");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 6), "ld3r {v26.4h, v27.4h, v28.4h}, [x30], #6");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 3), "ld3r {v31.8b, v0.8b, v1.8b}, [x30], #3");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 3), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], #3");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 6), "ld3r {v26.4h, v27.4h, v28.4h}, [x30], #6");
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 12), "ld3r {v26.2s, v27.2s, v28.2s}, [x30], #12");
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld3r {v26.1d, v27.1d, v28.1d}, [x30], #24");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 3), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], #3");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 6), "ld3r {v26.8h, v27.8h, v28.8h}, [x30], #6");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 3), "ld3r {v31.16b, v0.16b, v1.16b}, [x30], #3");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 3), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], #3");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 6), "ld3r {v26.8h, v27.8h, v28.8h}, [x30], #6");
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 12), "ld3r {v26.4s, v27.4s, v28.4s}, [x30], #12");
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 24), "ld3r {v26.2d, v27.2d, v28.2d}, [x30], #24");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[0], [x30], #3");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[0], [x30], #6");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, 3), "st3 {v31.b, v0.b, v1.b}[0], [x30], #3");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[0], [x30], #3");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[0], [x30], #6");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 12), "st3 {v26.s, v27.s, v28.s}[0], [x30], #12");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 24), "st3 {v26.d, v27.d, v28.d}[0], [x30], #24");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[15], [x30], #3");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[7], [x30], #6");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 12), "st3 {v26.s, v27.s, v28.s}[3], [x30], #12");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 24), "st3 {v26.d, v27.d, v28.d}[1], [x30], #24");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[15], [x30], #3");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[7], [x30], #6");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 12), "st3 {v26.s, v27.s, v28.s}[3], [x30], #12");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 24), "st3 {v26.d, v27.d, v28.d}[1], [x30], #24");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, 4), "ld4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], #4");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 16), "ld4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], #16");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 32), "ld4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], #32");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 4), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #4");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 8), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30], #8");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 4), "ld4r {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #4");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 4), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #4");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 8), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30], #8");
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 16), "ld4r {v26.2s, v27.2s, v28.2s, v29.2s}, [x30], #16");
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "ld4r {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], #32");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 4), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #4");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 8), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30], #8");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 4), "ld4r {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #4");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 4), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #4");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 8), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30], #8");
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 16), "ld4r {v26.4s, v27.4s, v28.4s, v29.4s}, [x30], #16");
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 32), "ld4r {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], #32");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, 4), "st4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], #4");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 16), "st4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], #16");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 32), "st4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], #32");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 0, Reg::r30, Reg::r29), "ld1 {v26.b}[0], [x30], x29");
TEST_SINGLE(ld1<SubRegSize::i16Bit>(VReg::v26, 0, Reg::r30, Reg::r29), "ld1 {v26.h}[0], [x30], x29");
@@ -797,95 +893,107 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30, Reg::r29), "st1 {v26.s}[3], [x30], x29");
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30, Reg::r29), "st1 {v26.d}[1], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[0], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, Reg::r29), "ld2 {v31.b, v0.b}[0], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[0], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.h, v27.h}[0], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.s, v27.s}[0], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.d, v27.d}[0], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[15], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "ld2 {v26.h, v27.h}[7], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "ld2 {v26.s, v27.s}[3], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "ld2 {v26.d, v27.d}[1], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[15], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "ld2 {v26.h, v27.h}[7], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "ld2 {v26.s, v27.s}[3], [x30], x29");
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "ld2 {v26.d, v27.d}[1], [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.8b, v27.8b}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "ld2r {v31.8b, v0.8b}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.8b, v27.8b}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.4h, v27.4h}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.2s, v27.2s}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.1d, v27.1d}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.16b, v27.16b}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "ld2r {v31.16b, v0.16b}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.16b, v27.16b}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.8h, v27.8h}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.4s, v27.4s}, [x30], x29");
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.2d, v27.2d}, [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[0], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, Reg::r29), "st2 {v31.b, v0.b}[0], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[0], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.h, v27.h}[0], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.s, v27.s}[0], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.d, v27.d}[0], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[15], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "st2 {v26.h, v27.h}[7], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "st2 {v26.s, v27.s}[3], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "st2 {v26.d, v27.d}[1], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[15], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "st2 {v26.h, v27.h}[7], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "st2 {v26.s, v27.s}[3], [x30], x29");
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "st2 {v26.d, v27.d}[1], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[0], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, Reg::r29), "ld3 {v31.b, v0.b, v1.b}[0], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[0], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.h, v27.h, v28.h}[0], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.s, v27.s, v28.s}[0], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.d, v27.d, v28.d}[0], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[15], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "ld3 {v26.h, v27.h, v28.h}[7], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "ld3 {v26.s, v27.s, v28.s}[3], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "ld3 {v26.d, v27.d, v28.d}[1], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[15], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "ld3 {v26.h, v27.h, v28.h}[7], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "ld3 {v26.s, v27.s, v28.s}[3], [x30], x29");
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "ld3 {v26.d, v27.d, v28.d}[1], [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "ld3r {v31.8b, v0.8b, v1.8b}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.4h, v27.4h, v28.4h}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.2s, v27.2s, v28.2s}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.1d, v27.1d, v28.1d}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "ld3r {v31.16b, v0.16b, v1.16b}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.8h, v27.8h, v28.8h}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.4s, v27.4s, v28.4s}, [x30], x29");
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.2d, v27.2d, v28.2d}, [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[0], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, Reg::r29), "st3 {v31.b, v0.b, v1.b}[0], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[0], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.h, v27.h, v28.h}[0], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.s, v27.s, v28.s}[0], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.d, v27.d, v28.d}[0], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[15], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "st3 {v26.h, v27.h, v28.h}[7], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "st3 {v26.s, v27.s, v28.s}[3], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "st3 {v26.d, v27.d, v28.d}[1], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[15], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "st3 {v26.h, v27.h, v28.h}[7], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "st3 {v26.s, v27.s, v28.s}[3], [x30], x29");
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "st3 {v26.d, v27.d, v28.d}[1], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, Reg::r29), "ld4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "ld4r {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.2s, v27.2s, v28.2s, v29.2s}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "ld4r {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.4s, v27.4s, v28.4s, v29.4s}, [x30], x29");
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, Reg::r29), "st4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
}
TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore exclusive pair") {
TEST_SINGLE(stxp(Size::i32Bit, Reg::r28, Reg::r29, Reg::r30, Reg::r28), "stxp w28, w29, w30, [x28]");
@@ -920,7 +1028,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore exclusive regi
TEST_SINGLE(ldaxr(WReg::w30, Reg::r29), "ldaxr w30, [x29]");
TEST_SINGLE(stxr(XReg::x30, XReg::x29, Reg::r28), "stxr w30, x29, [x28]");
TEST_SINGLE(stlxr(XReg::x30, XReg::x29, Reg::r28), "stlxr w30, x29, [x28]");
TEST_SINGLE(stlxr(WReg::w30, XReg::x29, Reg::r28), "stlxr w30, x29, [x28]");
TEST_SINGLE(ldxr(XReg::x30, Reg::r29), "ldxr x30, [x29]");
TEST_SINGLE(ldaxr(XReg::x30, Reg::r29), "ldaxr x30, [x29]");
@@ -1211,8 +1319,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore register pair
TEST_SINGLE(ldp<IndexType::POST>(WReg::w30, WReg::w28, Reg::r29, -256), "ldp w30, w28, [x29], #-256");
TEST_SINGLE(ldp<IndexType::POST>(WReg::w30, WReg::w28, Reg::r29, 252), "ldp w30, w28, [x29], #252");
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, WReg::w28, Reg::r29, -256), "ldpsw x30, x28, [x29], #-256");
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, WReg::w28, Reg::r29, 252), "ldpsw x30, x28, [x29], #252");
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, -256), "ldpsw x30, x28, [x29], #-256");
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, 252), "ldpsw x30, x28, [x29], #252");
TEST_SINGLE(stp<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, -512), "stp x30, x28, [x29], #-512");
TEST_SINGLE(stp<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, 504), "stp x30, x28, [x29], #504");
@@ -1245,8 +1353,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore register pair
TEST_SINGLE(ldp<IndexType::OFFSET>(WReg::w30, WReg::w28, Reg::r29, -256), "ldp w30, w28, [x29, #-256]");
TEST_SINGLE(ldp<IndexType::OFFSET>(WReg::w30, WReg::w28, Reg::r29, 252), "ldp w30, w28, [x29, #252]");
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, WReg::w28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]");
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, WReg::w28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]");
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]");
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]");
TEST_SINGLE(stp<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, -512), "stp x30, x28, [x29, #-512]");
TEST_SINGLE(stp<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, 504), "stp x30, x28, [x29, #504]");
@@ -1279,8 +1387,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore register pair
TEST_SINGLE(ldp<IndexType::PRE>(WReg::w30, WReg::w28, Reg::r29, -256), "ldp w30, w28, [x29, #-256]!");
TEST_SINGLE(ldp<IndexType::PRE>(WReg::w30, WReg::w28, Reg::r29, 252), "ldp w30, w28, [x29, #252]!");
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, WReg::w28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]!");
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, WReg::w28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]!");
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]!");
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]!");
TEST_SINGLE(stp<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, -512), "stp x30, x28, [x29, #-512]!");
TEST_SINGLE(stp<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, 504), "stp x30, x28, [x29, #504]!");
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -658,7 +658,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Scalar: Floating-point data-process
TEST_SINGLE(fcvt(SReg::s30, DReg::d29), "fcvt s30, d29");
if (false) {
// vixl doesn't support this instruction.
TEST_SINGLE(bfcvt(HReg::h30, DReg::d29), "bfcvt h30, d29");
TEST_SINGLE(bfcvt(HReg::h30, SReg::s29), "bfcvt h30, s29");
}
TEST_SINGLE(fcvt(HReg::h30, DReg::d29), "fcvt h30, d29");
TEST_SINGLE(frintn(DReg::d30, DReg::d29), "frintn d30, d29");
+3
View File
@@ -66,6 +66,9 @@ DefinitionRenameDict = {
"shmctl": "_shmctl",
"shmat": "_shmat",
"shmdt": "_shmdt",
# musl/Alpine Linux defines `fstatat64` as a define that points to `fstatat`.
# Rename it to avoid global define conflicts.
"fstatat64": "fstatat_64",
}
Definitions_x64 = []
+9
View File
@@ -15,7 +15,16 @@ BigCoreIDs = {
tuple([0x41, 0xd0b]): "cortex-a76",
tuple([0x41, 0xd0d]): "cortex-a77",
tuple([0x41, 0xd41]): "cortex-a78",
tuple([0x41, 0xd41]):
[ ["cortex-a78", "0.0"],
["cortex-x1c", "14.0"], # Claim to be x1c as an alternative if available.
["cortex-a78c", "9999.0"], # Doesn't exist in clang as of version 15.0
],
tuple([0x41, 0xd44]): "cortex-x1",
tuple([0x41, 0xd4c]):
[ ["cortex-x1", "0.0"],
["cortex-x1c", "14.0"],
],
tuple([0x41, 0xd47]):
[ ["cortex-a78", "0.0"],
["cortex-a710", "14.0"],
Loaded 100 of 177 files, more files were not shown because too many files have changed in this diff. Show more