Compare commits

..
1668 Commits
Author SHA1 Message Date
Ryan Houdek faed139c34 Docs: Update for release FEX-2312.1 2023-12-11 13:48:42 -08:00
Ryan Houdek 80927bf0e1 FEXLoader: Temporarily disable CLONE_CLEAR_SIGHAND
This flag breaks FEX heavily for now.
glibc 2.38 started using this flag as an optimization for posix_spawn.
It will fall back to a "non-optimized" implementation if the clone
syscall returns EINVAL. For now do this while we investigate a more
proper implementation.

Should be backported to 2312.1.
2023-12-11 13:47:40 -08:00
Ryan Houdek 64276dbd0c Docs: Update for release FEX-2312 2023-12-03 17:59:59 -08:00
Alyssa Rosenzweig b619f381b3 Merge pull request #3306 from Sonicadvance1/move_remaining_nzcv
IR: Moves remaining NZCV operations to use DestSize
2023-12-03 21:06:22 -04:00
Ryan Houdek 3b0aff5fb9 IR: Moves remaining NZCV operations to use DestSize
Just like in #3305 but wasn't causing any known issues
2023-12-02 15:40:51 -08:00
Alyssa Rosenzweig a8ab8bbe8e Merge pull request #3305 from Sonicadvance1/fix_crash2
JIT: Fixes crash in TestNZ
2023-12-02 19:35:10 -04:00
Ryan Houdek 3e2ba6d835 unittests/ASM: Adds test for TestNZ bug
Would trigger the bug before it was fixed.
2023-12-02 14:57:57 -08:00
Ryan Houdek c6497fe32b JIT: Fixes crash in TestNZ
In some situations TestNZ is generated with a constant that is using a
constant that can't fit inside of the tst instruction.

This was found in libGLX with virgl, crashing invalid instruction
generation and crashing steamwebhelper
2023-12-02 14:57:57 -08:00
Ryan Houdek c8ef77c15f Merge pull request #3297 from Sonicadvance1/remove_getprogramstatus
FEXCore: Removes GetProgramStatus
2023-11-30 16:29:37 -08:00
Ryan Houdek b35fadf7e3 Merge pull request #3298 from Sonicadvance1/remove_initializecontext
FEXCore: Removes InitializeContext API
2023-11-30 16:29:30 -08:00
Ryan Houdek 250ffb6d23 Merge pull request #3299 from alyssarosenzweig/opt/bt
Optimize BT/BTC/BTS/BTR
2023-11-30 10:50:29 -08:00
Alyssa Rosenzweig f6b1434d63 Merge pull request #3304 from Sonicadvance1/fix_corruption
Dispatcher: Fixes corruption when spilling SRA registers
2023-11-30 08:51:56 -04:00
Ryan Houdek 6bbae69c75 Dispatcher: Fixes corruption when spilling SRA registers
These functions only want the GPRs returned for SRA. This is because the
signal handler needs this map to relation between x86 GPRs and AArch64
GPRs.

When we added AF and PF to the SRA array we accidentally started
returning two more GPRs to the frontend. This caused the signal
delegator to start corrupting the members after GPRs in FEX's CoreState.

Corrupting 16-bytes after the gregs[] array.
This included corrupting:
   - es_idx, cs_idx, ss_idx, ds_idx, gs_idx, fs_idx, _pad[]
2023-11-29 16:17:17 -08:00
Alyssa Rosenzweig d0f54bcb23 Merge pull request #3278 from Sonicadvance1/fix_oversights
FEXCore: Fixes passing arguments to ABI helpers
2023-11-29 17:17:39 -04:00
Alyssa Rosenzweig 470615b896 Merge pull request #3301 from Sonicadvance1/fix_sonicmania_32bit
InstCountCI: Moves Sonic Mania code to 32-bit file
2023-11-29 15:36:45 -04:00
Ryan Houdek b02ab8ee19 InstCountCI: Moves Sonic Mania code to 32-bit file
Sonic Mania is a 32-bit game, it shouldn't be running its assembly as
64-bit.
2023-11-29 11:25:32 -08:00
Alyssa Rosenzweig 5f6046be4c InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 14:35:32 -04:00
Alyssa Rosenzweig e923e83efb OpcodeDispatcher: fix nzcvdirty
lets us use flagm in cmpxchg.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 14:35:25 -04:00
Alyssa Rosenzweig e836e4212d InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 14:23:41 -04:00
Alyssa Rosenzweig 9417c93110 OpcodeDispatcher: remove outdated comment
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 14:23:41 -04:00
Alyssa Rosenzweig 068599b1ec OpcodeDispatcher: use size-appropriate alu in bt*
saves zero-extending move for 32-bit ops.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 14:22:21 -04:00
Alyssa Rosenzweig 7216415bfc OpcodeDispatcher: reorder flag calcs in bt*
saves big moves.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 14:22:21 -04:00
Alyssa Rosenzweig 3020626506 OpcodeDispatcher: unify bt/btc/bts/btr impls
they're all copypastes of each other, unify into one general "bit test & perform
action" template. this means most of the wins from the previous commits now
apply for bt* without more copypaste.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 14:22:21 -04:00
Alyssa Rosenzweig 0a79fa8d5d OpcodeDispatcher: remove masking for 32/64-bit bt
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 13:40:18 -04:00
Alyssa Rosenzweig f8380b9adb OpcodeDispatcher: use smaller shifts for BT
if the shift is < N, and we grab bit 0 after, we only need to consider <=N
bits of the source. this lets us use 32-bit lsr for 32-bit bt, which will
reduce masking in the next commit.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 13:40:18 -04:00
Alyssa Rosenzweig d898028bc3 OpcodeDispatcher: optimize BT with constant
use the rmif properly.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 13:35:23 -04:00
Ryan Houdek f090700184 FEXCore: Removes InitializeContext API
This isn't necessary anymore, just initialize everything on context
creation immediately. All use cases just called this immediately
afterwards.
2023-11-29 09:33:32 -08:00
Ryan Houdek 01d29dffb9 FEXCore: Removes GetProgramStatus
Split from #3284 without changing ownership semantics while I reduce the
debugging surface here.

Removes one usage of ParentThread from FEXCore. Which can be done since
it is no longer an opaque structure, we can read the StatusCode
directly.

No functional change.
2023-11-29 09:23:47 -08:00
Alyssa Rosenzweig 14ba64a22d OpcodeDispatcher: use rmif masking for bt
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 13:20:22 -04:00
Alyssa Rosenzweig 6716077cb6 OpcodeDispatcher: don't mask bt source
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-29 13:16:55 -04:00
Alyssa Rosenzweig 8892580c41 Merge pull request #3295 from Sonicadvance1/fix_missing_cond_arg
IRDumper: Fixes missing conditional name
2023-11-29 12:57:49 -04:00
Ryan Houdek 1b41304fc1 IRDumper: Fixes missing conditional name
When COND_AL was added it wasn't added to this helper. Since there is a
gap between the last condition, just early check the value.

Fixes reading beyond the end of the array
2023-11-29 08:39:35 -08:00
Ryan Houdek 7de66ac3a4 Merge pull request #3286 from Sonicadvance1/remove_getcpustate
FEXCore: Removes Get/SetCPUState
2023-11-28 18:54:29 -08:00
Alyssa Rosenzweig 85a1c1ff25 Merge pull request #3281 from Sonicadvance1/optimize_constantdirection_memsetcpy
FEXCore: Optimize memcpy and memset when direction is compile time constant
2023-11-28 19:11:27 -04:00
Alyssa Rosenzweig 8e892ece59 Merge pull request #3277 from Sonicadvance1/remove_warnings_17
FEXCore: Removes FEX_PACKED from CPUState
2023-11-28 19:11:18 -04:00
Ryan Houdek aa1344aadd Merge pull request #3293 from Sonicadvance1/erofs_extract
FEXRootFSFetcher: Supports extracting erofs images
2023-11-28 11:04:49 -08:00
Ryan Houdek 3f02d7c665 Merge pull request #3289 from Sonicadvance1/move_debug_names
FEXCore: Moves debug strings to gdbserver
2023-11-28 11:04:15 -08:00
Ryan Houdek f328fca880 Merge pull request #3283 from Sonicadvance1/thread_frontend_pt1
FEXCore: Start changing how thread creation works
2023-11-28 11:03:52 -08:00
Ryan Houdek 47d79978ef FEXRootFSFetcher: Fixes curl downloading with spaces in the path
Since we're invoking curl directly, we don't need to wrap it in `sh -c`
with this function.

Fixes an issue where curl downloads to a non-escaped path weren't
working. Now they do.
2023-11-28 01:19:46 -08:00
Ryan Houdek 2e24f34a3f Merge pull request #3291 from Sonicadvance1/remove_tmpfile
EmulatedFiles: Stop relying on O_TMPFILE
2023-11-27 13:48:31 -08:00
Ryan Houdek 6e8af295c5 Merge pull request #3290 from Sonicadvance1/move_signaldelegator
FEXCore: Moves more SignalDelegator functions to the frontend
2023-11-27 13:46:11 -08:00
Ryan Houdek bba156a3c1 FEXRootFSFetcher: Supports extracting erofs images
As I was poking around erofs-utils documentation, I found out that
fsck.erofs actually provides an option for extracting erofs images
without using fuse.

This finally puts the erofs handling on feature parity with squashfs.
2023-11-27 13:26:56 -08:00
Ryan Houdek 8015ce2099 Merge pull request #3292 from Sonicadvance1/remove_debug_x86_table
FEXCore: Removes x86 DebugInfo table
2023-11-27 13:13:18 -08:00
Ryan Houdek 1153c1a538 EmulatedFiles: Stop relying on O_TMPFILE
O_TMPFILE has a few minor problems that I have been thinking about for a
while. I just recently got reminded about this and remembered that most
problems get resolved by using memfd_create.

- O_TMPFILE is only supported on some filesystems.
   - Supported filesystem must be the one mounted to the pathname being
     opened.
   - Only a minor inconvenience as tmpfs and all related filesystems
     support this.
   - An inode is actually created on whatever filesystem is backing the
     folder.
- `/tmp/` must exist as a directory
   - If this folder happened to not be mounted then these temporary
     files wouldn't have been created.
   - memfd_create doesn't have a folder that needs to exist.
- We were leaving the files open as read/write
   - While we were rewinding the file offset, an misbehaving application
     could have wrote garbage to the temp file.
   - memfd sealing allows us to open the FD as RW and then seal its
     capabilities, making it a read-only FD.
- We were leaking FDs opened with O_CLOEXEC
   - We could have just opened the O_TMPFILE with O_CLOEXEC
   - memfd also just supports this flag, so use it.
   - No real issues, just nice to be sanitary here.

Overall this doesn't really change any behaviour, but it is nice to
cleanup some of the edges there.
2023-11-27 13:10:48 -08:00
Ryan Houdek a47b3cccb8 FEXCore: Moves debug strings to gdbserver
These are only used by gdbserver for filling out its XML data structures
so just remove them from FEXCore.

Also fixes the ordering on RegNames to match the definition of the enum
class definition in CoreState. This has been out of correct order since
we reordered registers months ago.
2023-11-27 13:08:37 -08:00
Ryan Houdek 2070056d16 FEXCore: Moves more SignalDelegator functions to the frontend
As we are moving more and more OS specific code to the frontend, this is
another set of functions that can be moved to FEXLoader from FEXCore.

No functional change here, only code moved from protected to private and
to FEXLoader's SignalDelegator.

Once more thread handling is moved to the frontend we can move even more
out of FEXCore. As follows:
- CheckXIDHandler can get moved.
  - First pthread FEX makes would just call this.
- Register/UnregisterTLSState
  - This can happen in the clone/thread handler once the frontend
    handles it.

This leaves very little in the backend and is mostly an interface for
passing signal data to the frontend that it needs once a signal has
occured.
It additionally also is used for `SignalThread`.
2023-11-27 12:59:46 -08:00
Ryan Houdek e227f1343f FEXCore: Start changing how thread creation works
The frontend needs to be in control of how threads are created. This is
inherent to the fact that OS threads are OS specific. We currently have
this weird split that when initializing the FEXCore context, we create a
parent thread at all times.

This does some initial cleanup that gets the core initialization nearly
decoupled.
2023-11-27 12:49:41 -08:00
Ryan Houdek 3c7335713d Merge pull request #3287 from Sonicadvance1/remove_config_threads
Config: Removes Threads option
2023-11-27 12:46:14 -08:00
Ryan Houdek bdf4089264 Merge pull request #3285 from Sonicadvance1/remove_getexitreason
FEXCore: Removes GetExitReason
2023-11-27 12:45:55 -08:00
Ryan Houdek b027113998 Merge pull request #3280 from Sonicadvance1/rpres_afp_requirement
FEXCore: Disables RPRES until AFP is audited and enabled
2023-11-27 12:45:39 -08:00
Ryan Houdek 389c6b11dd Merge pull request #3288 from Sonicadvance1/fix_dungeon_defenders
FEXCore: Fixes imul returning garbage data
2023-11-26 08:08:28 -08:00
Ryan Houdek fa5d9dc3b7 FEXCore/X86Tables: Shifts register down to offset zero
Flag position zero wasn't being used. Fill the hole.
Keeps the two overlay flags next to each other, moves the pop flag back
towards the end.
2023-11-25 17:10:23 -08:00
Ryan Houdek cb56728e57 FEXCore/X86Tables: Removes unused supports REP flag
This flag was being set in the tables but was actually unused.
2023-11-25 16:54:51 -08:00
Ryan Houdek b89c3a4573 FEXCore: Removes x86 DebugInfo table
This has long since been unused. Originally implemented for some fuzzing
tests but has been abandoned and that should likely be implemented some
other way.
2023-11-25 16:50:24 -08:00
Ryan Houdek 0cc11108ba InstcountCI: Update for changed codegen. 2023-11-24 14:15:06 -08:00
Ryan Houdek a7caf83022 FEXCore: Fixes imul returning garbage data
When a 32-bit imul was being executed it had a chance of returning
garbage data in the upper 32-bits of the 64-bit result.
While this didn't typically cause problems, this gets exacerbated from
32-bit applications executing multiplies for address calculations.

A combination of commits 7146691360 and
d01b457727 exposed this problem where
previously there would be multiple moves between the calculation and
data use which would have zero'd the upper bits for us previously.

Now that we are no longer doing that, we need to make sure the opcode
dispatcher doesn't generate broken code instead.

Fixes Dungeon Defenders, which hasn't worked since FEX-2308.

Adds an ASM test that ensures we don't break it again.
2023-11-24 14:04:05 -08:00
Ryan Houdek 053452c40c Config: Removes Threads option
This is an option that has been long overdue for removal. It's original
intention was primarily to lie to the guest application about the number
of cores in the system. This allowed us to say that the system was only
single threaded which worked around some threading bugs that we had
early on.

This is no longer the case and now it is a confusing remnant of the past
that people think they need to set. Incorrectly assuming that "0" by
default means that FEX is doing some sort of disabling of threading and
forcing all emulation down one CPU core. This is not the case and has
never been the case, so removing the option makes that idea go away.
2023-11-24 07:03:00 -08:00
Ryan Houdek d4361c87ae CPUID: Query and use physical cores upfront
Instead of using the config option, just query and use the result.
2023-11-24 07:02:30 -08:00
Ryan Houdek 6469eb7a0e Linux/EmulatedFiles: Stop using Threads config option
Now no longer potentially lying to the application about the number of
cores.
2023-11-24 07:01:56 -08:00
Ryan Houdek 13fbd0e802 Linux: Removes usage of CPU core config option
Stop lying to the application about getcpu, sched_getaffinity, and
sched_setaffinity.

- getcpu would wrap the cpu result modulo the count of cores
- sched_setaffinity wouldn't work at all
- sched_getaffinity lied and always reported full affinity of config
  option
2023-11-24 07:01:33 -08:00
Ryan Houdek e555a8f817 FEXCore: Removes Get/SetCPUState
Split off from #3282 to reduce burden.
We can read the data member directly now since it isn't opaque. In fact
we already do in the signal handlers. Removes these redundant helpers.

Removes one usage of ParentThread in FEXCore.
2023-11-23 07:55:06 -08:00
Ryan Houdek f9fb61cf1a FEXCore: Removes GetExitReason
Split off from #3282 to reduce burden.
We read the member directly now. Removes one usage of ParentThread
in FEXCore.
2023-11-23 07:50:23 -08:00
Ryan Houdek 43cf2e4e2c Merge pull request #3276 from bylaws/f1
Improvements for WOW64 compat with newer wine
2023-11-22 05:53:27 -08:00
Ryan Houdek 98f9a65202 Merge pull request #3275 from bylaws/x87fix
FEXCore: Work around broken preserve_all support in Windows clang
2023-11-22 05:53:14 -08:00
Ryan Houdek 8726c8fb73 Merge pull request #2691 from neobrain/refactor_scoped_signal_mask
ScopedSignalMask: Clean up API and use std::unique_lock/shared_lock
2023-11-19 04:53:05 -08:00
Ryan Houdek 27f3cb336f InstCountCI: Adds compile time constant memcpy/memset
To showcase the code.
2023-11-19 04:21:50 -08:00
Ryan Houdek aa3bacd938 FEXCore: Optimize memcpy and memset when direction is compile time constant
This usually happens on backwards memcpy where we know the direction of
the copy because the code will typically do as follows:

```
std
rep movsb
cld
```

This is because the direction flag is part of the ABI and needs to be
set back to the forward direction if it was modified.
This typically doesn't get picked up on forward copies because we won't
have visibility of a cld instruction in the block.

This optimization allows us to only emit half of the code for the memcpy
if it is a compile time constant.

There's definitely some future task that could assume forward direction
if unknown and recompile the code if the assumption has failed, but not
doing that here.
2023-11-19 04:17:10 -08:00
Ryan Houdek c71492ef32 FEXCore: Disables RPRES util AFP is audited and enabled
While auditing our JIT to see if we have any AFP issues I noticed this.

RPRES has a hard dependency that AFP exists in order to be used, we were
hitting a case where RPRES would be enabled, but AFP is disabled by
default.

RPRES only changes behaviour when FPCR.AH is set which requires AFP.
While this doesn't affect any hardware today, it likely will in the
future.
2023-11-19 03:24:19 -08:00
Ryan Houdek 70191f2d28 FEXCore: Fixes passing arguments to ABI helpers
Forgot to pass a few arguments through. Doesn't change behaviour since
we were always passing in TMP1 and FPRs = true but this is correct.
2023-11-19 02:15:45 -08:00
Ryan Houdek 93db8b7ca7 FEXCore: Removes FEX_PACKED from CPUState
Removes new warning from clang:
```
FEXCore/include/FEXCore/Core/CoreState.h:119:35: warning: not packing field 'DeferredSignalRefCount' as it is non-POD for the purposes of layout [-Wpacked-non-pod]
  119 |     NonAtomicRefCounter<uint64_t> DeferredSignalRefCount;
      |                                   ^
```

We already ensure sane data layout that this is unnecessary anyway.
2023-11-19 01:25:33 -08:00
Tony Wasserka d33b0cb9e3 SignalScopeGuards: Improve code gen for GuardSignalDeferringSectionWithFallback 2023-11-18 12:10:02 +01:00
Billy Laws 0806d4ec25 WOW64: Handle target threads where the JIT is uninitialised
Suspend may be called on a thread before it has finished WOW64 initialisation,
keep track of all initialized threads and fallback to direct
NtSuspendThread when this is the case.
2023-11-18 00:54:07 +00:00
Ryan Houdek 9b646746b2 Merge pull request #3274 from alyssarosenzweig/opt/pf
Arm64Emitter: Dedicate registers for PF/AF
2023-11-17 16:45:38 -08:00
Ryan Houdek 11993daec4 FEXCore: Hides eflags reconstruction information in the core
The frontend shouldn't need to know any information about how to
reconstruct eflags. Just give us the information we need and it'll work
out.
There are still some inherit limitations of this and some edge cases
that might give invalid data, but it is roughly as close as it was
before.

Just provide if the PC was in the JIT, the host GPRs, and the PState object from the signal
information and FEXCore does the rest.

We don't need to change the signature for `SetFlagsFromCompactedEFLAGS`
because during reloading of register state automatically does this for
us.
2023-11-17 20:38:42 -04:00
Billy Laws a78ffeeaba WOW64: Call Wow64ProcessPendingCrossProcessItems on syscalls
This is needed to handle code invalidation requests sent by external
processes.
2023-11-18 00:16:44 +00:00
Billy Laws 05b78339f6 FEXCore: Work around broken preserve_all support in Windows clang
While clang side code to support preserve_all on Windows is in place
(and thus there are no errors for using it), there are still some parts
missing on the LLVM side. [1]

[1] https://github.com/llvm/llvm-project/blob/2402b14046b9628b584c07789830d7ed481f7d74/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp#L88
2023-11-17 22:13:39 +00:00
Alyssa Rosenzweig 365c221029 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig f60608a9c0 OpcodeDispatcher: allow garbage for 32bit inc/dec
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 153d871be2 OpcodeDispatcher: allow garbage for 32-bit cmp
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig b69f2d7773 InstCountCI: Start collecting hot blocks
Goal is to improve FEX performance with real world games. Let's start with
Sonic Mania. Two hot blocks from #2681.

I'm only bothering to track flagm for this because I'm super biased.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 09ffe7ef6b InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 3dfb94b524 OpcodeDispatcher: use axflag for x87-f64 fcmp
only 1 instr saved but meh.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig d1e43d94e9 OpcodeDispatcher: use axflag for fcmp faster
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 1b490e0e53 CodeEmitter: add ax/xaflag
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 094146d630 IR: add axflag
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 23c2a53683 OpcodeDispatcher: move fcmp flag fixup to dispatcher
simpler *and* much faster

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 82b7689ca4 OpcodeDispatcher: remove fcmp deferral
no longer load bearing, delete the abstraction.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 149f3e6f6d OpcodeDispatcher: rm flagsOp unused since select rework
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig cea551c2ac InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Alyssa Rosenzweig 2dcae23776 Arm64Emitter: Dedicate registers for PF/AF
Many flag-generating instructions like cmp need to save calculations for
deferred PF and AF flag calculation. Currently, they require a store per flag,
which is prohibitively expensive for hot instructions like cmp. By instead
pinning PF/AF temporary results to registers (x26/x27 by convention here), we
eliminate many stores altogether and turn the rest into zero-cycle moves (on
64-bit at least, this isn't optimal for 32-bit emulation due to CTX->GetGPRSize
shenanigans, need to check if this requirement can be lifted..).

To implement, we model as SRA and then the existing SRA code is able to generate
good code with little manual tuning. (Future work will get us to excellent code
with more tuning ;) ).

The tradeoff is reducing the working dynamic GPR set by 2 registers, which might
increase spilling in some cases. I think it's worth it in practice, though.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-17 17:37:24 -04:00
Tony Wasserka 92e4e75217 Merge DeferredSignalMutex.h and ScopedSignalMask.h into a single file
Using a single file makes sense now that the individual files are much
shorter and share common utility classes.
2023-11-17 10:56:34 +01:00
Tony Wasserka 5ca35bf77c ForkableMutex: Simplify WIN32 implementation 2023-11-17 10:56:34 +01:00
Tony Wasserka c956b82d27 ScopedSignalMask/DeferredSignalMutex: Clean up API and use std::unique_lock/shared_lock 2023-11-17 10:56:34 +01:00
Ryan Houdek 1c115096c4 Merge pull request #3268 from Sonicadvance1/more_gdbserver
More GdbServer improvements
2023-11-15 21:15:14 -08:00
Ryan Houdek c1d5fae018 Merge pull request #3273 from alyssarosenzweig/opt/shifts
Optimize shifts/rotates
2023-11-14 14:13:56 -08:00
Ryan Houdek 17d49fc00f Review comments 2023-11-14 12:54:54 -08:00
Alyssa Rosenzweig 0e1e4c16b1 Merge pull request #3271 from Sonicadvance1/optimize_sha
OpcodeDispatcher: Optimize three sha instructions
2023-11-14 16:48:46 -04:00
Alyssa Rosenzweig bec8e27b4f InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-14 08:40:41 -04:00
Alyssa Rosenzweig 56841f0e50 OpcodeDispatcher: avoid moves with 64bit imul
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-14 08:40:41 -04:00
Alyssa Rosenzweig 723146050b OpcodeDispatcher: allow garbage with multiplies
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-14 08:40:41 -04:00
Alyssa Rosenzweig 85b1aa4c2d OpcodeDispatcher: optimize mul flags
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-14 08:40:41 -04:00
Ryan Houdek 0506369519 InstCountCI: Update for sha changes
Moves crypto instructions to files where crypto is enabled.
2023-11-13 18:38:02 -08:00
Ryan Houdek ba1632974e InstCountCI: Actually allow disabling crypto
Forcing it was required to work around initial simulator quirks
2023-11-13 18:38:02 -08:00
Ryan Houdek c69082b1a4 OpcodeDispatcher: Optimize three sha instructions
- sha1nexte
   - Takes advantage of sha1h if supported
   - Does the operation in a vector otherwise
- sha1msg2
   - Instead of dumping everything to GPRs, we can do this with vectors
   - Mostly matches ARM's sha1su1 instruction, but it is /just/
     different enough to be annoying.
- sha256msg1
   - Directly matches sha256u0
   - Leaves the previous implementation alone
2023-11-13 18:38:02 -08:00
Ryan Houdek 74b2548982 IR: Implement support for VUSHRAI IR op
This matches Arm64 usra semantics. This instruction is useful for
implementing vector element rotate.
2023-11-13 18:22:45 -08:00
Ryan Houdek f31656ec65 IR: Support sha1h and sha256u0
These match our needs so wire them up
2023-11-13 18:22:45 -08:00
Ryan Houdek e91420c405 HostFeatures: fixup SHA checks
- Simulator doesn't support SHA
- Use DisableCrypto option to disable sha as well
- Only enable SHA if ARM cpu supports both SHA1 and SHA2
2023-11-13 18:22:45 -08:00
Ryan Houdek 25df59a65d ArmEmitter: Fixes sha256u1 emitter
Noticed this was actually emitting sha256h2
2023-11-13 18:22:45 -08:00
Alyssa Rosenzweig 83fdd5720f IR: Add ccmn
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 22:05:02 -04:00
Ryan Houdek db63241fd4 Merge pull request #3270 from Sonicadvance1/x11_thunk_lock
Thunks/libX11: Change lock functions to nullptr by default
2023-11-13 17:29:43 -08:00
Alyssa Rosenzweig 89b00c89aa OpcodeDispatcher: optimize rcl 1-bit
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig d38917b5f0 OpcodeDispatcher: rm pointless constant
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 910e0242c1 OpcodeDispatcher: optimize rcr 1-bit
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 651b7bb75d OpcodeDispatcher: optimize RCL
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig c9f13ae1dd OpcodeDispatcher: optimize RCR the usual ways
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 862e575100 OpcodeDispatcher: avoid some ubfx for rcr with flagm
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 4669c4541c OpcodeDispatcher: don't zero for flagm ror
missed earlier in the PR, would be annoying to rebase in.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 769a8c41c4 OpcodeDispatcher: Branch over shift=0 flags
Not supposed to touch flags at all, so don't! instead of making a terrible mess
of csels. a lot less instructions, and probably faster because the branch should
be predicted correctly in practice in hot loops.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig bec9dba2b1 OpcodeDispatcher: use shifted xor + rmif for rotates
eliminates lots of Bfe on flagm.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 205ba2ea13 IR: add shifted xor
for rotates.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 2073f6d287 OpcodeDispatcher: don't zero nzcv for flagm shifts
Faster for flagm. would be slower for !flagm because bfi slowness...

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 0f25a960ee OpcodeDispatcher: remove bfe for small shl imm
We allow the garbage in flags calculation, it's ignored.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 282ed3e309 OpcodeDispatcher: optimize bsf/bsr
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig cd031a7d38 OpcodeDispatcher: avoid some ubfx for flagm
Do the masking as part of the rmif, for free.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig e1885ed0bd OpcodeDispatcher: Use 64-bit ubfx
for larger shifts.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:21:01 -04:00
Alyssa Rosenzweig 4a31b619fa Merge pull request #3272 from alyssarosenzweig/instcountci/del-optimal
InstructionCountCI: Remove Optimal flags
2023-11-13 21:20:07 -04:00
Alyssa Rosenzweig bd4464bd5e InstructionCountCI: Remove Optimal flags
Instruction count CI has transformed the way we work on FEX… I love the system
and want to make it better. there’s one part of instruction count CI that isn’t
so lovable: the problematic “optimal” flag on instructions.

There are several issues with this flag, both philosophical and practical.

– it is tedious to update the optimal flag when making an implementation
optimal. The effect of that is discouraging people from making instructions,
optimal, or encouraging people to fail to update the flag, and dilute the value
of it. Either way, since we care far more about optimal implementations, then we
do about updating the flag, clearly we should prioritize the implementation and
not the flag. This issue was not obvious at the outset, when instruction count,
CI was introduced, and still quite small. The problem magnified when we started
duplicating instructions in bulk for different combinations of CPU features
(flagm, AFP, etc.) that intern multiplies the manual work required to update the
flags by the corresponding constant factor. if it comes down to a choice between
removing this extra coverage and removing the flag, I think we all agree that
removing the flag is the lesser evil.

– The definition of “optimal” is fundamentally problematic. I have often
improved the instruction count of an instruction that was already “optimal”.
This is all kinds of silly, and calls into question whether there’s any value
whatsoever in the existing classifications of the flag. Furthermore, it is often
unknowable, whether an implementation really is optimal. Is it possible to
implement BZHI (with flag calculations) in fewer than eight instructions? We
don’t know, and it’s silly to pretend that we do.

– as a consequence of the problematic definitions , there are so many errors in
both directions that I don’t think there’s much value in preserving the existing
classification at the expense of +progress. Being able to say “32% of
instructions are translated optimally” is neat, but it really doesn’t tell us
anything whatsoever when you dig a little deeper.

So, as the flag is misleading at best and perhaps harmful at worst, let’s remove
it and make the instruction count CI, more useful overall. let’s let the
expected count and the assembly speak for themselves, and cut away the chaff. if
we want a meaningless number to report to management, we can instead calculate
the average blowup factor ;-)

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-13 21:14:05 -04:00
Ryan Houdek d907a7dc9f gdbServer: Adds some lldb specific query commands
Fairly small.
2023-11-13 15:18:38 -08:00
Ryan Houdek 732070f750 GdbServer: Try binding twice
If a bind fails then this is usually because PID reuse happened and the
unix domain socket path still existed on the filesystem. Alternatively
the process did an execve which left a dangling unix domain file.

Unlink the file and try again in these cases. It will almost certainly
work the second time.
2023-11-13 15:11:47 -08:00
Ryan Houdek 48442b6b03 GdbServer: Poll listen socket for connection
Instead of relying on sleeping in accept, use poll. This allows us to
early shutdown instead of leaving a thread hanging stuck in accept.

Doesn't change behaviour with FEX gdbserver waiting for attach, but in
the future when we all full process trees to have gdbserver running this
will be more important.
2023-11-13 15:11:47 -08:00
Ryan Houdek 7fdbe547a3 GDBServer: Set thread name
Makes it easier to pick it out of all the threads.
2023-11-13 15:11:47 -08:00
Ryan Houdek 59565b828d FEXServerClient: Expose the temp folder directly
Using the server mount folder works most of the time, but when running
under pressure-vessel this stacks directories in a weird way because the
mount folder has some tricks applied to it.

Expose the temp folder being used directly instead.
2023-11-13 15:11:47 -08:00
Alyssa Rosenzweig 1e2d059890 Merge pull request #3266 from alyssarosenzweig/opt/selects-and-more
Optimize selects & branches & more nzcv goodies
2023-11-13 14:20:42 -04:00
Ryan Houdek 0aa41908a2 Thunks/libX11: Change lock functions to nullptr by default
These are setup to be nullptr by default. Instead of providing no-op
lock instructions just have them be nullptr.

It's already part of the API that they need to be nullptr checked before
calling and this matches behaviour of the real libX11 library. Removes
some spam that is unnecessary.
2023-11-12 16:03:43 -08:00
Alyssa Rosenzweig b27ce3f79c InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:37:09 -04:00
Alyssa Rosenzweig 238e52f74a OpcodeDispatcher: Don't mask 32-bit bzhi either
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:36:46 -04:00
Alyssa Rosenzweig 9398b931fb Arm64Emitter: Handle 32-bit negatives
Noticed in the area.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:36:42 -04:00
Alyssa Rosenzweig b2a9785959 OpcodeDispatcher: optimize bzhi
Trickery to save an instruction :')

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 224a1f19a3 OpcodeDispatcher: improve bzhi flag gen
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 109c53f22b unittests: Add BZHI sign test
fails on main.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig e25849b2cb OpcodeDispatcher: fix BZHI flag calculation
needs SF.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 57978accc1 OpcodeDispatcher: Add heuristic to prefer rmif for InsertNZCV
Decent instcountci win.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig ff37177f4d IR: Remove AND(N)Z conditions
Unused since TestNZ+NZCVSelect accomplishes the same and good riddance. Might
bring them back later for tbz/tbnz, but certainly not in this Selectful form.

(I added them when I thought we were going to RA the flags. With the more
effective static approach, we don't need this for that.)

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 472d143021 OpcodeDispatcher: Use TestNZ directly for SelectBit
Lets us drop the bitwise select variants, this was the last use.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 157f95b08f IR: Extend TestNZ to two sources
Unlock the power of AND.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 6bf7ab0778 IR: Remove complex branches
Performance footgun and now unused. Don't bring it back.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 0de958be2a IR: Remove Abs
Now unused. If we bring it back, it should be brought back as CSSC only. On
non-CSSC platforms, an explicit cmp + predicated neg can be better.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig ef544fecf2 OpcodeDispatcher: Use predicated neg for x87 fild
Saves an instruction on non-CSSC platforms by deleting a redundant cmp.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 5eea68d6c6 IR: Support predicated Neg
i.e. cneg. will be used for x87 hell op

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 5471367db1 OpcodeDispatcher: Optimize branches
For native cases. Big perf win.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig b8265b1067 OpcodeDispatcher: Rework SelectCC for branches
Separate out the NZCV bits from the more complex stuff so we can specially
optimize the branches.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Alyssa Rosenzweig 099c683a5a IR: Add FromNZCV mode to CondJump
In this mode, rather than the branch comparing its arguments and then jumping
based on the result, the branch simply jumps by the native comparison based on
the NZCV value. This allows us to map x86 branches to arm64 branches 1:1.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-12 17:32:24 -04:00
Ryan Houdek 0357bb23e8 Merge pull request #3265 from Sonicadvance1/gdb_unix_socket
GdbServer: Switch over to a unix domain socket
2023-11-11 13:20:40 -08:00
Ryan Houdek c7193b52fb GdbServer: Switch over to a unix domain socket
Resolving the issue that we can only ever have one gdbserver process
running consuming port 8086 (even if the port number is cute).

Doesn't give us anything yet but in the future will allow us to have
whole process trees running gdbservers that we can attach to.
2023-11-11 13:14:09 -08:00
Alyssa Rosenzweig ec14a65e23 OpcodeDispatcher: optimize LOOP invert
need to reorder since the select clobbers nzcv. before/after diff on the loopne
unit test:

> 4308: [INFO] cset w20, ne
40c41
< 4308: [INFO] mrs x20, nzcv
---
> 4308: [INFO] mrs x21, nzcv
42,49c43,48
< 4308: [INFO] cset x21, ne
< 4308: [INFO] ubfx w22, w20, #30, #1
< 4308: [INFO] eor x22, x22, #0x1
< 4308: [INFO] and x21, x21, x22
< 4308: [INFO] msr nzcv, x20
< 4308: [INFO] cbnz x21, #+0x8 (addr 0xfffed66f8094)
< 4308: [INFO] b #+0x1c (addr 0xfffed66f80ac)
< 4308: [INFO] ldr x0, pc+8 (addr 0xfffed66f809c)
---
> 4308: [INFO] cset x22, ne
> 4308: [INFO] and x20, x22, x20
> 4308: [INFO] msr nzcv, x21
> 4308: [INFO] cbnz x20, #+0x8 (addr 0xfffec94e8090)
> 4308: [INFO] b #+0x1c (addr 0xfffec94e80a8)
> 4308: [INFO] ldr x0, pc+8 (addr 0xfffec94e8098)

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 21:03:58 -04:00
Alyssa Rosenzweig 0d70c6a0d0 OpcodeDispatcher: Use cset for getting nzcv flags
Usually better in practice... some rotates are slightly regressed by this but
they were already terrible.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 21:03:58 -04:00
Alyssa Rosenzweig bfa069c4d5 OpcodeDispatcher: Dirty NZCV on new blocks
This worked only by accident before.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 21:03:58 -04:00
Alyssa Rosenzweig 61bdf64e15 OpcodeDispatcher: Cleanup PF select
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 21:03:58 -04:00
Alyssa Rosenzweig 482b35c283 OpcodeDispatcher: Optimize JA/JNA selects
Chain two csel instructions together, which should be optimal.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 10:46:59 -04:00
Alyssa Rosenzweig b74d886017 OpcodeDispatcher: Cleanup selectcc
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 10:46:59 -04:00
Alyssa Rosenzweig 1667abad7e OpcodeDispatcher: Don't fold flags in SelectCC
Not beneficial in the new approach.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 10:46:59 -04:00
Alyssa Rosenzweig b187a853e7 OpcodeDispatcher: Use NZCVSelect for SelectCC
Massively better codegen.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 10:46:58 -04:00
Alyssa Rosenzweig 228c7d142e IR: Allow inline 0 on NZCVSelect
via the 0 reg. We really need a more generalized approach to taking advantage of
wzr, but this optimizes the special case I care about for seta (saving a move to
make the impl optimal).

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 09:42:26 -04:00
Alyssa Rosenzweig 041199644c IR: Add NZCVSelect op
This will replace Select soon, as it lets us take advantage of
NZCV-generating instructions and it doesn't clobber NZCV.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-10 09:04:42 -04:00
Alyssa Rosenzweig 3767f3633d Merge pull request #3263 from alyssarosenzweig/opt/not-garbage
OpcodeDispatcher: Make "not" not garbage
2023-11-09 19:58:02 -04:00
Ryan Houdek af3253947e Merge pull request #3162 from alyssarosenzweig/opt/nzcv-native
Keep guest SF/ZF/CF/OF flags resident in host NZCV
2023-11-09 15:10:45 -08:00
Ryan Houdek efc5eb2933 Merge pull request #3250 from Sonicadvance1/gdbserver_frontend_move
FEXLoader: Wire up gdbserver in the frontend
2023-11-09 14:48:59 -08:00
Ryan Houdek b4eeb96375 Merge pull request #3261 from Sonicadvance1/tuning_options
FEX: Only pass CPU tunables to FEXCore and FEXLoader
2023-11-09 14:48:42 -08:00
Alyssa Rosenzweig c9832e3d34 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 15:22:25 -04:00
Alyssa Rosenzweig da3e3fc7a3 OpcodeDispatcher: Optimize some selects
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 15:21:10 -04:00
Alyssa Rosenzweig b5c83f0628 JIT: Optimize 8/16-bit TestNZ
This is cursed. I blame the darling.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 14:50:36 -04:00
Alyssa Rosenzweig 03087a55ba InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 12:02:20 -04:00
Alyssa Rosenzweig 1ce3c16b30 OpcodeDispatcher: Make "not" not garbage
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 12:02:20 -04:00
Alyssa Rosenzweig bf702850a9 InstCountCI: Add not bh case
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 11:32:45 -04:00
Alyssa Rosenzweig 584c4cc05e OpcodeDispatcher: Mask with rmif sometimes
For CF/OF calculation, this saves an instruction on flagm platforms.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 10:05:51 -04:00
Alyssa Rosenzweig 279afd88bb OpcodeDispatcher: Generalize rmif trick
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 10:05:51 -04:00
Alyssa Rosenzweig c0a6d82025 OpcodeDispatcher: Use rmif for NZCV inserts
Optimizes piles of s/w flag generation on flagm.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 3a03e1c93c OpcodeDispatcher: rework InsertNZCV
in prep for rmif.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig bdaa70405f OpcodeDispatcher: simplify flag control ops
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 1281145982 OpcodeDispatcher: Optimize popf
mostly for easier debugging tbh

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 87cac09477 OpcodeDispatcher: optimize cmc
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 5336129b58 IR: Optimize sub/sbb
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 72fc2b522d IR: Optimize add/adc
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig b6f6c84790 IR: Optimize tests
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 11e9be13b1 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig afdb8753ba IR: Remove some implicit flag clobbers
Do it explicitly for sve-256 and punt on optimizing, so we avoid regressing code
gen otherwise.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig f6a2e6739d IR: VCMPEQ doesn't clobber nzcv
cmeq not cmpeq!

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig d6569d510d Arm64: Keep host flags resident in NZCV
Rather than the context. Effectively a static register allocation scheme for
flags. This will let us optimize out a LOT of flag handling code, keeping things
in NZCV rather than needing to copy between NZCV and memory all the time.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 04e4993d9b OpcodeDispatcher: Add a kludge to save NZCV less
Some opcodes only clobber NZCV under certain circumstances, we don't yet have
a good way of encoding that. In the mean time this hot fixes some would-be
instcountci regressions.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 783e09d67d ConstProp: Remove select code motion
Problematic in the new approach and not sure what it's trying to accomplish tbh.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 314f478225 ConstProp: remove select+branch fusion
Not beneficial in the new approach to flags.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig c1dbc28aa2 OpcodeDispatcher: Implement SaveNZCV
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig 8f7e393ffb Arm64: Don't clobber NZCV in CondJump
Again we need to handle this one specially because the dispatcher can't insert
restore code after the branch. It should be optimized in the near future, don't
worry.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig b3055523b4 IR: Switch to dedicated NZCV load/store
Semantics differ markedly from the non-NZCV flags, splitting this out makes it a
lot easier to do things correctly imho. Gets the dest/src size correct
(important for spilling), as well as makes our existing opt passes skip this
which is needed for correctness at the moment anyway.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Alyssa Rosenzweig cf6b21564c InstCountCI: disable flagm explicitly more
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-09 09:40:51 -04:00
Mai 996a4c023c Merge pull request #3260 from Sonicadvance1/ender_lilies_nzcv_unittest
unittests/ASM: Adds unittest found from Ender Lilies that crashed with NZCV
2023-11-08 20:24:09 +01:00
Ryan Houdek 0dcbdcc0e2 FEX: Only pass CPU tunables to FEXCore and FEXLoader
This fixes an issue where CPU tunables were ending up in the thunk
generator which means if your CPU doesn't support all the features on
the *Builder* then it would crash with SIGILL. This was happening with
Canonical's runners because they typically only support ARMv8.2 but we
are compiling packages to run on ARMv8.4 devices.

cc: FEX-2311.1
2023-11-08 05:50:33 -08:00
Ryan Houdek 5bdd422db6 unittests/ASM: Adds unittest found from Ender Lilies that crashed with NZCV
SHA instructions are very large right now and cause register spilling
due to their codegen. Ender Lilies has a really large block in a
function called `sha1_block_data_order` that was causing FEX to spill
NZCV flags incorrectly. The assumption which held true before NZCV
optimizations were a thing was that all flags were either 1-bit in an
8-bit container, or just 8-bit (x87 TOP flag).

NZCV host flags broke this assumption by making its flags 32-bit which
ended up breaking when encounting spilling situations.
2023-11-08 04:52:22 -08:00
Alyssa Rosenzweig bf147f47b5 Merge pull request #3258 from Sonicadvance1/remove_warnings_16
Arm64Emitter: Fixes warning
2023-11-08 07:05:26 -04:00
Alyssa Rosenzweig 3f1f7faf34 Merge pull request #3257 from alyssarosenzweig/helper/derive-op
Add helper for deriving ops by opcode
2023-11-08 06:53:45 -04:00
Ryan Houdek 1fc6725826 Arm64Emitter: Fixes warning 2023-11-08 01:27:27 -08:00
Ryan Houdek fa8c35feba Docs: Update for release FEX-2311 2023-11-07 09:42:16 -08:00
Alyssa Rosenzweig 73958b9163 OpcodeDispatcher: Use DeriveOp
Replace every instance of the Op overwrite pattern, and ban that anti-pattern
from the codebase in the future. This will prevent piles of NZCV related
regressions.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-07 12:05:00 -04:00
Alyssa Rosenzweig 9b81a83894 OpcodeDispatcher: Add DeriveOp helper
The "create op with wrong opcode, then change the opcode" pattern is REALLY
dangerous. This does not address that. But when we start doing NZCV trickery, it
will get /more/ dangerous, and so it's time to add a helper and make the
convenient thing the safe(r) thing. This helper correctly saves NZCV /before/
the instruction like the real builders would. It also provides a spot for future
safety asserts if someone is motivated.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-07 12:05:00 -04:00
Alyssa Rosenzweig 65b7d4007e Merge pull request #3256 from Sonicadvance1/unittest_for_3254
unittests/ASM: Adds unittest for implicit flag clobber for #3254
2023-11-07 10:02:07 -04:00
Ryan Houdek 2c0444e846 unittests/ASM: Adds unittest for implicit flag clobber for #3254
It looks like currently FEX has a bug around implicit flag clobbering
with this pull request where an IR operation that implicitly clobbers
flags isn't correctly saving the NZCV flags before doing the operation.

Adds a unit test that specifically captures this issue. RAX will be 1 or
0 depending on if the flags are clobbered incorrectly or not.
2023-11-07 02:04:11 -08:00
Ryan Houdek b92e716d0c Merge pull request #3255 from Sonicadvance1/unittest_flags_signals
FEXLinuxTests: Adds a unittest for eflags and signals around a inlined syscall
2023-11-06 05:43:44 -08:00
Ryan Houdek a7a1365cf7 FEXLinuxTests: Adds a unittest for eflags and signals around a inlined syscall
When attempting to debug #3162 I had noticed spurious behaviour around
what I assumed to be eflags getting corrupt around inlined syscalls.
This turned out to be a red herring but to ensure we are still testing
this, create a fully fleshed out unit test.

This test ensures a couple of things.
1) A flag that is set or unset before a syscall doesn't have its data
   corrupt
2) An inline syscall doesn't corrupt the eflags, checking the eflag
   result after returning from the syscall.
3) A signal occuring while in an inline syscall returns the correct
   eflags information in the signal handler information

This test gets accomplished by setting or unsetting a particular flag
and then calling the futex syscall in a way that is guaranteed to be
inlined and also wait forever. Then the parent thread will signal with a
SIGTERM and read back the signal information. It does this multiple
times for each flag we care about.
2023-11-06 05:33:03 -08:00
Alyssa Rosenzweig 8ee5b5cf50 Merge pull request #3253 from Sonicadvance1/fix_double_munmap
JITArm64: Fixes double munmap issue that was causing crashes
2023-11-06 07:57:01 -04:00
Alyssa Rosenzweig b45023bedf Merge pull request #3251 from Sonicadvance1/fexlinuxtests_use_intel
FEXLinuxTests: Compile tests with masm=intel
2023-11-06 07:56:29 -04:00
Alyssa Rosenzweig 3702e513f5 Merge pull request #3252 from Sonicadvance1/fix_fillstaticregisters
ARMEmitter: Fix GPR fill mask in `FillStaticRegs`
2023-11-06 07:50:58 -04:00
Ryan Houdek 829384e488 JITArm64: Fixes double munmap issue that was causing crashes
While tracking issues in #3162, I had encountered a random crash that I
started hunting. It was very quickly apparent that this crash was
unrelated to that PR. I just happened to be running a unittest that was
creating and tearing down a bunch of threads that exacerbated the
problem.

See as follows with the strace output:
```
[pid 269497] munmap(0x7fffde1ff000, 16777216) = 0
[pid 269497] munmap(0x7fffde1ff000, 16777216 <unfinished ...>
[pid 268982] mmap(NULL, 16777216, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fffde1ff000
[pid 269497] <... munmap resumed>)      = 0
```

One thread is freeing some memory with munmap, another one then does a mmap and gets the same address back.
Nothing too crazy at initial glance, but taking a closelier look, we can
see that there are two strange oddities:
1) We are double unmapping the same address range through munmap
2) The second munmap is interrupted and returns AFTER the mmap.

This has the unfortunate side-effect that the mmap that just returned
the same address has actually just been unmapped! This was resulting in
spurious crashes around thread creation that was SUPER hard to nail
down.

The problem comes down to how code buffer objects are managed, in
particular how the Arm64Emitter and Dispatcher handled its buffers.

Arm64Emitter is inherited by two classes; Dispatcher, and Arm64JITCore.
On class destruction the emitter would free its internal tracking
buffer. Additionally on destruction, the Arm64JITCore would walk through
all of its CodeBuffers and free them. The problem ends up being that in
the Arm64JITCore, it would free its code buffers which also ended up
being the current active buffer bound to the Arm64Emitter. Thus causing
the Arm64Emitter to come back around and try to free the same buffer
again.

This is a double-free problem! and was only visible on thread exiting!
Can't track double frees with mmap and munmap with current tooling!

This problem typically didn't occur because of how fast the destruction
usually takes and jemalloc inbetween also typically means the problem
doesn't occur. Initially thinking this was a threaded pool allocator bug
because typically the new allocation would end up in there once a new
thread was spinning up.

Now we change behaviour, Arm64Emitter doesn't do any buffer management
itself, instead just passing an initial buffer on to its internal buffer
tracking if given one up front.

This leaves the Dispatcher and the Arm64JITCore to do their buffer
management and ensuring there is no double free.

The day is saved!
2023-11-05 19:18:40 -08:00
Ryan Houdek ed23fbe932 InstCountCI: Update for SRA spill changes. 2023-11-05 02:59:19 -08:00
Ryan Houdek 0af0427efd ARMEmitter: Fix GPR fill mask in FillStaticRegs
This mask was being used incorrectly, it's a GPR spill mask for host
GPRs not an index in to the SRA array. Search the array of SRA registers
for the first one in the mask first to use as a temporary.

Fixes an issue with 32-bit inline syscalls where the first register
being spilled was r8, which was beyond the size of SRA registers on
32-bit processes. This would cause FEX to read the value just after
x32::SRA which is x32::RA. This would mean it would use r20 as a
temporary, corrupting the register in the process.

I noticed this while poking at #3162, but also when I was looking at a
memory buffer ownership problem.
2023-11-05 02:54:48 -08:00
Ryan Houdek a499272d81 FEXLinuxTests: Compile tests with masm=intel
ATT is maddening and a unittest I'm writing was going to be a nightmare.
2023-11-04 22:00:45 -07:00
Ryan Houdek 5dee921300 FEXLoader: Wire up gdbserver in the frontend
Requires #3249 to be merged first

Library alerting has been disabled for now, and storing IR while
gdbserver is running is removed.

Otherwise no functional change.
2023-11-03 20:23:51 -07:00
Ryan Houdek c0dcf8925a Move gdbserver to frontend 2023-11-03 20:23:51 -07:00
Ryan Houdek e91c5ff906 Merge pull request #3249 from Sonicadvance1/gdbserver_frontend_prep
GDBServer: Preparation work to get this moved to the frontend
2023-11-03 20:21:28 -07:00
Ryan Houdek 190f7c27e0 Merge pull request #3243 from Sonicadvance1/loadfile_unsized
FEXCore/FileLoading: Updates helper to load file that is backed by memory
2023-11-03 20:21:12 -07:00
Ryan Houdek b15f0b5d36 FEXCore/FileLoading: Updates helper to load file that is backed by memory
When attempting to read files that aren't backed by a filesystem then
our current read file helpers fail since they query the file size
upfront.

Change the helper so that it doesn't query the size and just reads the file if it
can be opened. This lets us read `/proc/self/maps` using helpers.
2023-11-03 07:01:39 -07:00
Ryan Houdek e2c65189ff GDBServer: Preparation work to get this moved to the frontend
GDBServer is inherently OS specific which is why all this code is
removed when compiling for mingw/win32. This should get moved to the
frontend before we start landing more work to clean this interface up.

Not really any functional change.

Changes:

FEXCore/Context: Adds new public interfaces, these were previously
private.
- WaitForIdle
   - If `Pause` was called or the process is shutting down then this
     will wait until all threads have paused or exited.
- WaitForThreadsToRun
   - If `Pause` was previously called and then `Run` was called to get
     them running again, this waits until all the threads have come out
     of idle to avoid races.
- GetThreads
   - Returns the `InternalThreadData` for all the current threads.
   - GDBServer needs to know all the internal thread data state when the
     threads are paused which is what this gives it.

GDBServer:
- Removes usages of internal data structures where possible.
   - This gets it clean enough that moving it out of FEXCore is now
     possible.
2023-11-02 20:11:01 -07:00
Ryan Houdek 03f63f99a8 FEXCore: Moves StringUtils to FEXCore headers
Once gdbserver gets moved to the frontend this will need to be in the
includes.
2023-11-02 20:09:12 -07:00
Ryan Houdek e305a9a0d5 Merge pull request #3248 from neobrain/fix_thunks_async_callback_error
Thunks: Print error if guest-provided callbacks are called asynchronously
2023-11-02 12:03:22 -07:00
Tony Wasserka 3a90dbbb35 Thunks: Print error if guest-provided callbacks are called asynchronously from the host 2023-11-02 19:50:58 +01:00
Ryan Houdek 5103f2d92b Merge pull request #3247 from alyssarosenzweig/refactor/nzcv-prereq
Preparatory patches for nzcv
2023-11-01 14:07:14 -07:00
Alyssa Rosenzweig d4a6b031ea Merge pull request #3245 from Sonicadvance1/remove_gdbpausecheck
FEXCore: Removes gdb pause check handler
2023-11-01 15:46:40 -04:00
Alyssa Rosenzweig 319cf4bf3d Arm64: Preserve flags in ExitFunction
Mostly harmless (except for fusion on cortexes), prepares us for nzcv work.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-01 15:44:36 -04:00
Alyssa Rosenzweig d75c0f2c50 IR: Add missing flag clobbers
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-01 15:44:36 -04:00
Alyssa Rosenzweig a586d3823d IR: VFCMPEQ does not clobber flag
Oversight.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-01 15:44:36 -04:00
Alyssa Rosenzweig 5522c6db9c OpcodeDispatcher: Use jump wrappers
Mostly automated replacement + renaming for build fixing.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-01 15:44:36 -04:00
Alyssa Rosenzweig 367e1658ad OpcodeDispatcher: Add jump wrappers
These should always be used in the dispatcher rather than the raw jumps they
translate to, as they ensure that flags are flushed. Eliminates a class of bugs
that will become a lot easier to hit with the new nzcv work.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-01 15:44:36 -04:00
Alyssa Rosenzweig bbad06f81a ArchHelpers: Add cfinv()
FlagM goodness.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-11-01 15:44:35 -04:00
Mai 9612b2fe4b Merge pull request #3240 from Sonicadvance1/optimize_palignr_zero
OpcodeDispatcher: Optimize palignr with zero immediate
2023-11-01 05:55:52 +01:00
Mai ef5503f0b7 Merge pull request #3239 from Sonicadvance1/optimize_blendw
OpcodeDispatcher: Optimize pblendw
2023-11-01 05:54:31 +01:00
Ryan Houdek 26bf67ca76 InstCountCI: Update for pblendw 2023-10-31 21:20:05 -07:00
Ryan Houdek e5df636efd OpcodeDispatcher: Optimize pblendw
Requires #3238 to be merged first since this uses the tbx IR operation.

Worst case is now a three instruction sequence of ldr+ldr+tbx.
Some operations are special-cased, which definitely doesn't cover all
possible cases we could use without tbx, but as a worst case improvement
this is a significant improvement.
2023-10-31 21:18:02 -07:00
Ryan Houdek f34f4a0227 unittests/ASM: Extend pblendw test for all swizzles
We can't sanely test all 256 swizzle masks, so walk the array of random
data for each swizzle and CRC the results each step along the way.
This will give us a final result rather than each individual step.
2023-10-31 21:16:19 -07:00
Mai f45722d2cd Merge pull request #3238 from Sonicadvance1/optimize_blendps
OpcodeDispatcher: Optimize blendps
2023-11-01 04:32:33 +01:00
Ryan Houdek db7ef0e4b0 InstCountCI: Update for blendps
All sixteen swizzles are now optimal.
2023-10-31 20:14:56 -07:00
Ryan Houdek de10cbad98 OpcodeDispatcher: Optimize blendps
A bunch of blendps swizzles weren't optimal. This optimizes all swizzles
to be optimal.

Two instructions can be more optimal without a tbx but the rest required
tbx to be optimal since they don't match ARM's swizzle mechanics.
2023-10-31 20:06:45 -07:00
Ryan Houdek e4d9c264d8 IR: Adds support for tbx 2023-10-31 20:06:45 -07:00
Ryan Houdek 97b3efa90a unittests/ASM: Update blendps to test all swizzles
There are sixteen swizzles so it can fit in a single test.
2023-10-31 20:06:45 -07:00
Mai 18065199e3 Merge pull request #3242 from Sonicadvance1/gdbserver_threadnames
GdbServer: Fixes returning thread names
2023-11-01 04:02:14 +01:00
Mai 77d92872bc Merge pull request #3212 from Sonicadvance1/dpp_opt
OpcodeDispatcher: Optimize 128-bit DPPS and DPPD
2023-11-01 04:01:05 +01:00
Ryan Houdek 460f13be71 FEXCore: Removes gdb pause check handler
gdbserver is currently entirely broken so this doesn't change behaviour.
The gdb pause check that we originally had an excessive amount of
overhead.
Instead use the pending interrupt fault check that was wired
up for wine.
This makes the check very lightweight and makes it more reasonable to
implement a way to have gdbserver support attaching to a process.
2023-10-31 18:40:00 -07:00
Ryan Houdek 47c9463217 GdbServer: Fixes returning thread names
gdb gets angry if we return text with `<No Name>` in an xml text field.
Instead only return a name if we have one and gdb will take care of the
rest.

Additionally change the formatting of the return packet, it doesn't need
the xml version header.
2023-10-29 17:16:46 -07:00
Ryan Houdek 15c825f362 Merge pull request #3241 from neobrain/refactor_thunks_drop_async
Thunks/xcb: Drop unused and incomplete support for asynchronous callbacks
2023-10-29 13:27:29 -07:00
Alyssa Rosenzweig bbd20b47ba Merge pull request #3236 from Sonicadvance1/fix_strenum
Config: Fixes string enum parser with multiple arguments
2023-10-29 07:32:57 -04:00
Alyssa Rosenzweig 5b70209728 Merge pull request #3233 from Sonicadvance1/print_vixl
JIT: Implements Print support for vixl sim
2023-10-29 07:32:32 -04:00
Alyssa Rosenzweig a287f2a189 Merge pull request #3237 from Sonicadvance1/round_classification
InstCountCI: Update rounds{s,d} classification
2023-10-29 07:31:22 -04:00
Tony Wasserka 3a240e3b61 Thunks/xcb: Drop unused and incomplete support for asynchronous callbacks 2023-10-29 11:05:24 +01:00
Ryan Houdek de1e593ec2 InstCountCI: Update for palignr zero immediate 2023-10-27 15:11:07 -07:00
Ryan Houdek 13cd8b33a2 OpcodeDispatcher: Optimize palignr with zero immediate
These turns in to moves
2023-10-27 15:09:31 -07:00
Ryan Houdek 0f26bc20a3 unittests/ASM: Adds palignr tests for zero immediate
These effectively turn in to moves.
2023-10-27 15:07:52 -07:00
Ryan Houdek eacab3cc22 InstCountCI: Update rounds{s,d} classification
This is optimal without AFP. We already test these with AFP in a
different file.
2023-10-27 12:38:02 -07:00
Ryan Houdek 09e3371a0d Config: Fixes string enum parser with multiple arguments
Messed up when originally implementing this, substr's second argument is
requested substring length, not the ending position.

Noticed this while trying to parse multiple FEX_HOSTFEATURES options.
2023-10-27 12:04:04 -07:00
Alyssa Rosenzweig ff3f7345b6 Merge pull request #3235 from Sonicadvance1/cpu_names
CPUID: Adds some missing cpu core names
2023-10-27 08:00:43 -04:00
Alyssa Rosenzweig 8181e53727 Merge pull request #3234 from Sonicadvance1/minor_bfxil
Arm64: Minor optimization to bfxil and bfi
2023-10-27 08:00:28 -04:00
Alyssa Rosenzweig 5431aa5a28 Merge pull request #3232 from Sonicadvance1/assert_code
IR: Print assert code for IR EmitValidation
2023-10-27 07:58:51 -04:00
Ryan Houdek 1a293cc542 CPUID: Adds some missing cpu core names
EZ-PZ
2023-10-26 21:03:32 -07:00
Ryan Houdek b1e78934ad InstCountCI: Improvement for bfi/bfxil 2023-10-26 20:26:48 -07:00
Ryan Houdek 61f22911c7 Arm64: Minor optimization to bfxil and bfi
When the destination doesn't alias the source, we can remove a final mov
from both of these operations.

Does some minor code improvement.
2023-10-26 20:25:44 -07:00
Ryan Houdek fe8778bb96 JIT: Implements Print support for vixl sim
Everytime I want to quickly output a value for testing I tend to use
Print which didn't work under the simulator.
Give this a quick fix to wire up the jump to the vixl sim.
2023-10-26 18:34:47 -07:00
Ryan Houdek 14e5ea1e22 Merge pull request #3230 from Sonicadvance1/optimize_atomic_fetch
IR: Optimize unused result atomic fetch mop to just atomic mop
2023-10-26 15:57:20 -07:00
Ryan Houdek 74f1205f33 IR: Print assert code for IR EmitValidation
Currently we don't get why an IR emit failed in the assert message. Put
the code in to the message so it is easier to see.
This also resolved the issue that when in RelWithDebInfo the assert line
would typically be the end of the IR emission function, so you couldn't
see which assert actually triggered. Now since the message is printed
this is easier

Before:
```
[ASSERT]
```

After:
```
[ASSERT] Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit
```

This is a lot easier and better data than what #3227 proposed.
2023-10-26 15:51:56 -07:00
Ryan Houdek 4045bfd187 Merge pull request #3229 from Sonicadvance1/instcountci_3dnow
OpcodeDispatcher: Optimize a few 3DNow! operations
2023-10-26 15:42:50 -07:00
Ryan Houdek 9db93a43dd IR: Adds comments that atomic op layout must match
If the fetch and nonfetch versions mismatched then the DCE optimization
which changes the IR operation would break handily.

Add a comment as a reminder so if anyone touches this they will
understand.
2023-10-26 15:40:55 -07:00
Ryan Houdek a379d50729 Merge pull request #3226 from neobrain/fix_thunks_incomplete_but_compatible
Thunks: Skip data layout analysis for types that are always assumed compatible
2023-10-26 13:44:31 -07:00
Alyssa Rosenzweig f5822f83b0 Merge pull request #3231 from Sonicadvance1/alu_ops_more_garbage
OpcodeDispatcher: Allow garbage in upper bits for more ALU ops
2023-10-26 06:23:14 -04:00
Ryan Houdek 5028434292 IR: Optimize unused result atomic fetch mop to just atomic mop
When the result of an atomic fetch operation is unused then we can
safely convert it to a non-fetching version of the operation.

This happens hundreds of times per process as far as I can tell. No idea
if this actually helps any hardware, but theoretically it can allow CPUs
to not stall waiting for writeback of the atomic operation.
Couldn't detect any performance improvements in the various little
things I was poking at least. Very trivial to support so add it.

Unaligned variants are already handled in our unaligned fault handler
since the only difference is the acquire semantic is dropped and the
destination register is the zero register.
2023-10-25 21:00:04 -07:00
Ryan Houdek 8f0461cac8 IR: Adds support for non-fetch Atomci CLR and NEG 2023-10-25 21:00:04 -07:00
Ryan Houdek 0b4cb23411 InstCountCI: Update for more garbage data 2023-10-25 20:46:31 -07:00
Ryan Houdek bab96b9441 OpcodeDispatcher: Allow garbage in upper bits for more ALU ops
Secondary ALU operations were missed and when the operation is 4-bytes
in size we can also allow garbage upper bits since the JIT will emit a
32-bit operation for this instruction which is safe.

Optimizes some bad codegen around 32-bit ALU operations.
2023-10-25 20:44:36 -07:00
Ryan Houdek 11e2f14185 InstCountCI: Update for 3DNow! changes 2023-10-25 19:30:13 -07:00
Ryan Houdek 6177290e9d OpcodeDispatcher: Optimize a few 3DNow! operations
Instead of using VInsElement in pi2fw and pf2iw, just use uzp1 to ensure
we don't unintentionally add to RA pressure.

Additionally we can generate the constant needed for pmulhrw directly
using the movi instruction. Converts two instructions in to one.

Under FEX's current constraints this makes all 3DNow! instructions
optimal.
2023-10-25 19:27:54 -07:00
Ryan Houdek 20a54913bd IR: Support shifted imms in VectorImm
We will want to use this in some cases.
2023-10-25 19:27:00 -07:00
Tony Wasserka dd9ed89a7a Thunks/X11: Drop unneeded type annotation 2023-10-25 20:38:57 +02:00
Tony Wasserka a4e1e0a1fb Thunks/gen: Skip data layout analysis for types that are always assumed compatible 2023-10-25 19:35:02 +02:00
Tony Wasserka 5e9f69001d Thunks/gen: Clarify pointer parameter handling
One of the subconditions was always true, so it can safely be removed.
2023-10-25 19:35:02 +02:00
Ryan Houdek 39c5ab1c81 Merge pull request #3225 from neobrain/fix_32bit_funcptrs
Thunks: Fix function pointer support on 32-bit
2023-10-25 07:07:18 -07:00
Ryan Houdek 5bf790324c Merge pull request #3224 from neobrain/feature_thunk_strict_everywhere
Thunks: Annotate pointer parameters throughout all thunked libraries
2023-10-25 07:00:42 -07:00
Tony Wasserka adcdb32d49 Thunks: Fix function pointer support on 32-bit 2023-10-25 14:37:22 +02:00
Tony Wasserka f264578f12 Thunks: Unconditionally enable strict processing mode 2023-10-25 12:39:57 +02:00
Tony Wasserka 7149da387a Thunks: Annotate pointer parameters throughout all thunked libraries 2023-10-25 12:39:57 +02:00
Alyssa Rosenzweig 0f3d14e7c0 Merge pull request #3223 from Sonicadvance1/testharness_page_size
TestHarnessRunner: Don't hardcode stack allocation to 4096 bytes
2023-10-24 10:52:35 -04:00
Ryan Houdek aad5080224 TestHarnessRunner: Don't hardcode stack allocation to 4096 bytes
Just allocate a single page that we query at runtime.
2023-10-24 07:36:13 -07:00
Ryan Houdek c77a3d673c Merge pull request #3222 from Sonicadvance1/nzcv_opt_bug
unittests: Adds test for bug from #3162
2023-10-23 18:19:16 -07:00
Ryan Houdek 0ff2e6e1e3 unittests: Adds test for bug from #3162
This PR has a bug around flags calculation and REP LODS{B,W,D,Q}.
This currently passes on main but fails on #3162.

Bug only occurs in 32-bit instead of 64-bit with the same test. Should
help diagnose the bugs in #3162.
2023-10-23 16:55:56 -07:00
Ryan Houdek 8538f5bac4 Merge pull request #3221 from Sonicadvance1/instcountci_missing_insts
InstCountCI: Adds two missing variants of movd/movq
2023-10-23 15:26:58 -07:00
Ryan Houdek a305baf6e5 InstCountCI: Fixes some mislabeled instructions
These are optimal according to our standards.
2023-10-23 15:15:03 -07:00
Ryan Houdek 807619aa02 InstCountCI: Adds two missing variants of movd/movq
We only had the move to memory destination version, ensure we test to
GPR as well.
2023-10-23 15:01:18 -07:00
Ryan Houdek 6db2125b41 Merge pull request #3220 from Sonicadvance1/override_flagm
InstCountCI: Support disabling flagm extensions
2023-10-23 14:45:45 -07:00
Ryan Houdek 9f6d80fe5d InstCountCI: Duplicate tests that change behaviour based on flagm
Necessary for #3162 to have consistent behaviour in CI
2023-10-23 14:03:19 -07:00
Ryan Houdek 423ce12001 InstCountCI: Disable flagm and Flagm2 on tests
Most of these will get duplicated in the next commit
2023-10-23 14:02:50 -07:00
Ryan Houdek 4edd72fc33 InstCountCI: Support disabling flagm extensions
This is necessary so #3162 can give consistent results
2023-10-23 14:02:24 -07:00
Ryan Houdek 978f607dd9 Merge pull request #3177 from neobrain/feature_thunk_pointer_annotations
Thunks: Add new pointer annotations to assist data layout analysis
2023-10-23 13:00:27 -07:00
Ryan Houdek 9ba78c9771 Merge pull request #3219 from Sonicadvance1/enable_vixlsim_instcountci
github: Enables Vixl simulator on x86 host for instcountci
2023-10-23 12:49:33 -07:00
Ryan Houdek e2144345c0 github: Enables Vixl simulator on x86 host for instcountci
Otherwise features get filled out weirdly.
2023-10-23 12:42:40 -07:00
Ryan Houdek 63e4c3682d Merge pull request #3218 from Sonicadvance1/opt_df
OpcodeDispatcher: Optimize DF pointer offset calculation
2023-10-23 11:41:22 -07:00
Ryan Houdek 2956e84ead InstCountCI: Update for constant prop improvement 2023-10-23 10:39:29 -07:00
Ryan Houdek 4466c50c2b ConstProp: Optimize SubShift and Add with negative
When SubShift (LSL) occurs with both sources constant then optimize away
the calculation.

Additionally if add is found to have one immediate constant where the
inverse of the constant fits in to ImmAddSub range, then invert the
constant and change it in to a sub.

This optimizes the cases when direction flag is known upfront in an
instruction.
2023-10-23 10:36:33 -07:00
Ryan Houdek dd5ca1d349 InstCountCI: Update for DF pointer optimization. 2023-10-23 10:14:51 -07:00
Ryan Houdek 95c756b466 OpcodeDispatcher: Optimize DF pointer offset calculation
Previously this moved two constant, did a compare and a csel. Four
instructions in total. It also corrupts NZCV which we want to use for
other things.

This new codegen emits one constant and one subtract instruction, two
instructions total and doesn't touch NZCV.

More optimal!
2023-10-23 09:27:41 -07:00
Ryan Houdek 99465faf63 IR: Implements support for subtract with shifted register
Will be used soon.
2023-10-23 09:27:41 -07:00
Ryan Houdek e018917f76 Merge pull request #3216 from alyssarosenzweig/opt/nzcv-infra
Prep commits for NZCV modelling
2023-10-23 07:39:47 -07:00
Ryan Houdek a261d9909e Merge pull request #3214 from Sonicadvance1/fix_bug
FEXCore: Fixes bug in vector `ZextAndMaskingElimination` pass
2023-10-23 07:29:43 -07:00
Alyssa Rosenzweig 6de8bc6848 IR: Annotate instructions with implicit flag clobber
Audit the code base and mark any instruction that implicitly clobbers flags so
it can get special handling in the dispatcher to spill NZCV ahead of emitting.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 10:21:47 -04:00
Alyssa Rosenzweig d87155e4ee IR: Add infrastructure for modelling flag clobbers
Lots of instructions clobber NZCV inadvertently but are not intended to write to
the host flags from the IR point-of-view. As an example, Abs logically has no
side effects but physically clobbers NZCV due to its cmp/csneg impl on non-CSSC
hw. Add infrastructure to model this in the IR so we can deal with it when we
start using NZCV for things.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 10:21:47 -04:00
Alyssa Rosenzweig 7484cacaf9 InstCountCI: Update for VInsertElement change
Only SVE256 codepath affected.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 10:21:47 -04:00
Alyssa Rosenzweig 42259974c4 Arm64: Preserve NZCV in VInsertElement
So we don't need to mark VInsertElement as implicit clobber in the common case.
Only afects sve256 which doesn't exist yet.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 10:21:34 -04:00
Alyssa Rosenzweig e455996dbd OpcodeDispatcher: Remove silly shift branching
The flag generation code does this internally and more efficiently.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 10:16:38 -04:00
Alyssa Rosenzweig b5dd1d05e9 Dispatcher: Preserve NZCV
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 10:16:38 -04:00
Alyssa Rosenzweig bbaf70da15 Dispatcher: Yeet pointless subs
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 10:16:29 -04:00
Ryan Houdek 65e8d094ef Merge pull request #3215 from alyssarosenzweig/hack/16k-units
FEXLoader: Query runtime page size
2023-10-23 06:43:43 -07:00
Alyssa Rosenzweig 4c801d594a FEXLoader: Query runtime page size
This lets most of the ASM tests run on 16K Linux hosts which is good because I
have a Mac and I'm bad at computer.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-10-23 09:35:22 -04:00
Ryan Houdek d4403edea9 OpcodeDispatcher: Updates COMIS to eliminate scalar moves
This was one of the few things that managed to hit the previously
removed optimization. Just fix the OpcodeDispatcher instead.
2023-10-21 21:33:07 -07:00
Ryan Houdek 06ef012fb2 FEXCore: Fixes bug in vector ZextAndMaskingElimination pass
With the previous RCLSE pass optimization that fixes store->load
forwarding, this pass started optimizing harder.

This hit a bug with this vmov removal that previously didn't get hit.
In particular this would eliminate vmov IR operations even if they were
zero extending a vector.
Since we have dramatically cleaned up the amount of vmov IR operations
we are generating, remove this optimization entirely. In the games I
tested, the only game that hit this "optimization" was Ender Lilies and
it started generating broken code for the single block of instructions
that did.

Adds a unit test for this case just in-case it comes back in the future
for some reason.

Fixes an issue where Ender Lilies would flash the screen to black every
time an enemy hit the player character.
2023-10-21 21:21:14 -07:00
Mai 8f8f37684a Merge pull request #3213 from Sonicadvance1/fix_repres
JITArm64: Fixes bug in rpres scalar operations
2023-10-22 05:05:14 +02:00
Ryan Houdek 7140b8d901 InstCountCI: Update for RPRES fix 2023-10-21 15:29:11 -07:00
Ryan Houdek d5beba9423 JITArm64: Fixes bug in rpres scalar operations
Noticed this during code investigation, these two operations were
swapped.

Would have caused issues if anything supported RPRES today.
2023-10-21 15:24:43 -07:00
Ryan Houdek 826e15aea9 unittests/ASM: Adds dpps/dppd broadcast mask tests
Ensures that the optimization around the broadcast mask is correct.
2023-10-20 18:15:43 +02:00
Ryan Houdek 14e80ce228 InstCountCI: Update for DPPS/DPPD
Adds some new destination broadcast masks to ensure we handle most of
them.
2023-10-20 18:15:43 +02:00
Ryan Houdek 165d3d3d4d Arm64JIT: Fixes VDupElement so it respects 64-bit vector duping
In some cases when we want the upper bits to be zero, this is the
desired behaviour
2023-10-20 18:15:43 +02:00
Ryan Houdek 887200e571 OpcodeDispatcher: Optimize 128-bit DPPS and DPPD
These instructions aren't super amazing due to the fact that they have
both a source mask and a destination duplication mask.

Setup a case where we can generate more optimal code in /most/ cases.

There are a few that still fall down a "bad" path for the result
broadcast but in most cases they are optimal. Still to be seen what
games typically use the broadcast mask as.

AVX in its infinite wisdom expanded DPPS to 256-bit, while leaving DPPD
to only support 128-bit still. This leaves the original implementation
alone for 256-bit DPPS since I don't want to break it.

This is another instruction that gets a free optimization when
SVE-128bit is supported!
2023-10-20 18:02:27 +02:00
Ryan Houdek 2c0bc0654d IR: Adds new VFAddV operation
SVE added this instruction natively, we can take advantage of it on
SVE-128bit systems which is quite nice.

Will be used soon.
2023-10-19 16:38:11 +02:00
Ryan Houdek b3d76bd2f1 IR: Adds DPPS and DPPD source masks
This will get used for these instructions soon
2023-10-19 16:36:19 +02:00
Ryan Houdek 2e694412f4 Merge pull request #3211 from lioncash/ext
VectorOps: Handle SVE VExtr a little better
2023-10-19 16:04:02 +02:00
Lioncache d84577c36c VectorOps: Handle SVE VExtr a little better
If the source registers don't alias the destination, then we can
safely move the lower bits over to it without using a temporary.
2023-10-19 15:11:23 +02:00
Ryan Houdek cf9c2aa72c Merge pull request #3206 from Sonicadvance1/fix_syscall
Linux: Fixes issue with *at syscalls with absolute paths not working
2023-10-19 15:05:34 +02:00
Ryan Houdek 1cb8e4891c Merge pull request #3210 from lioncash/fcadd
VectorOps: Handle SVE VFCADD a little better
2023-10-19 15:05:15 +02:00
Lioncache 24f2796141 VectorOps: Handle SVE VFCADD a little better
If no registers alias, then we can move the first source directly into the
destination and then perform the FCADD operation as opposed to using a
temporary.
2023-10-19 14:48:46 +02:00
Tony Wasserka cb215b5f21 FEXLinuxTests/thunks: Add assume_compatible_data_layout tests 2023-10-19 12:49:00 +02:00
Tony Wasserka 0cf2695772 FEXLinuxTests/thunks: Add tests for opaque types 2023-10-19 12:49:00 +02:00
Tony Wasserka 6a6886305e Thunks/gen: Add assume_compatible/is_opaque annotations
These annotations allow for a given type or parameter to be treated as
"compatible" even if data layout analysis can't infer this automatically.

assume_compatible_data_layout is more powerful than is_opaque, since it
allows for structs containing members of a certain type to be automatically
inferred as "compatible".

Conversely however, is_opaque enforces that the underlying data is never
accessed directly, since non-pointer uses of the type would still be
detected as "incompatible".
2023-10-19 12:49:00 +02:00
Tony Wasserka 5ef7537e61 unittests/thunks: Add ptr_passthrough tests 2023-10-19 12:49:00 +02:00
Tony Wasserka 167fe85cc3 Thunks: Implement ptr_passthrough annotation
This annotation can be used for data types that can't be repacked
automatically even with custom repack annotations. With ptr_passthrough,
the types are wrapped in guest_layout and passed to the host like that.
2023-10-19 12:49:00 +02:00
Tony Wasserka cf65747667 Thunks: Introduce an intermediate guest_layout wrapper to unpack callback arguments
This will be used later to aid automatic struct repacking.
2023-10-19 12:48:59 +02:00
Tony Wasserka 27bb28b47f Thunks: Carry annotations in callback wrappers of host functions
Previously, two functions with the same signature would always be wrapped
in the same logic. This change allows customizing one function with
annotations while leaving the other one unchanged.
2023-10-19 12:48:59 +02:00
Tony Wasserka a00da800e7 Thunks: Rename funcptr_types to thunked_funcptrs
This reflects its purpose slightly better, particularly since future patches
will add more information to this object.
2023-10-19 12:48:59 +02:00
Tony Wasserka bf835e80ac Thunks: Bump compiler requirements to C++20 2023-10-19 12:48:59 +02:00
Tony Wasserka 8f246b206b Merge pull request #3209 from neobrain/refactor_revert_vulkan_reorder 2023-10-19 12:45:14 +02:00
Ryan Houdek 3c5c23bf36 Merge pull request #3208 from lioncash/avg
VectorOps: Handle SVE VURAvg a little better
2023-10-19 12:38:56 +02:00
Tony Wasserka 5bcfaf4b9f Thunks/vulkan: Revert reordering changes from 180d16af7a
These interfere heavily with ongoing work. Let's reapply the reordering
once the dust has settled instead.
2023-10-19 12:31:33 +02:00
Lioncache 1f6c6345d9 VectorOps: Handle SVE VURAvg a little better
We can perform less moves by checking for scenarios where aliasing
occurs. Since addition is commutative (usually, general-case anyway),
order of inputs doesn't strictly matter here.
2023-10-19 12:14:12 +02:00
Ryan Houdek 93792577eb Merge pull request #3207 from lioncash/div
VectorOps: Handle SVE VFDiv a little better
2023-10-19 11:53:45 +02:00
Lioncache 3d23cd5765 VectorOps: Handle SVE VFDiv a little better
In the event no source vectors alias the destination,
we can just move the first source vector into it and
then perform the divide without needing to move afterword.
2023-10-19 11:45:35 +02:00
Ryan Houdek fcc239552c Linux: Fixes issue with *at syscalls with absolute paths not working
When a syscall from the *at series is provided an FD but the path is
absolute then dirfd should be ignored. We weren't correctly doing this.
Now if the path is absolute, but set the argument to the special
AT_FDCWD..
Fixes #3204
2023-10-19 09:48:50 +02:00
Ryan Houdek 8238de024f Merge pull request #3205 from lioncash/max
VectorOps: Handle SVE VSMax/VSMin and VUMax/VUMin paths a little better
2023-10-18 19:24:35 +02:00
Lioncache 39e658f02a VectorOps: Handle more VUMin SVE cases better
We can avoid needing to use movprfx here by moving
directly into the destination when possible and just
doing the UMIN directly
2023-10-18 18:48:13 +02:00
Lioncache e89dd27f2a VectorOps: Handle more VSMin SVE cases better
We can avoid needing to use movprfx here by moving
directly into the destination when possible and just
doing the SMIN directly.
2023-10-18 18:48:13 +02:00
Lioncache f85fae0041 VectorOps: Handle more VUMax SVE cases better
We can avoid needing to use movprfx here by moving
directly into the destination when possible and just
doing the UMAX directly.

Also expands the unsigned max tests to test values with
the sign bit set to ensure all behavior is caught.
2023-10-18 18:48:12 +02:00
Lioncache 65eec673fc VectorOps: Handle more VSMax SVE cases better
Since SMAX performs a comparison and returns the max value regardless
of how the operands are provided, we can check for when the second
input aliases the destination.
2023-10-18 18:48:03 +02:00
Ryan Houdek 5c93a085d2 Merge pull request #3203 from lioncash/movs
OpcodeDispatcher: Handle SSE vector moves into themselves a little better
2023-10-18 16:28:45 +02:00
Lioncache 4b356a7c2c OpcodeDispatcher: Have MOVNTSD go down the non-temporal path
For some reason this was using the regular unaligned path.
2023-10-18 14:59:02 +02:00
Lioncache 2b67f87054 OpcodeDispatcher: Handle SSE vector moves into themselves a little better
Obviously, it's silly to do this, but we should still be generating
optimal code for this case (which is none at all).
2023-10-18 14:58:57 +02:00
Ryan Houdek 1ea40ae676 Merge pull request #3201 from neobrain/fix_flt_thunks_64bit_only
FEXLinuxTests: Temporarily limit thunk test execution to 64-bit guests
2023-10-18 12:40:29 +02:00
Ryan Houdek e0ef32e0bf Merge pull request #3202 from Sonicadvance1/oopsies_vulkan
Thunks: Oops deleted an entry point
2023-10-18 12:38:05 +02:00
Ryan Houdek a2b53c8eb0 Thunks: Oops deleted an entry point
Moving some entries around I managed to delete one.
Fixes Vulkan thunks.
2023-10-18 12:21:28 +02:00
Tony Wasserka 21b6cccb4e FEXLinuxTests: Temporarily limit thunk test execution to 64-bit guests
Thunking isn't fully functional on 32-bit guests currently, so non-trivial
tests would currently hang in that context.
2023-10-18 12:09:10 +02:00
Tony Wasserka d539829251 FEXLinuxTests: Drop .32/.64 suffixes from test names 2023-10-18 12:09:10 +02:00
Ryan Houdek ef321e4bf8 Merge pull request #3200 from lioncash/mov
OpcodeDispatcher: Remove unnecessary 128-bit truncating moves from StoreResult
2023-10-17 12:12:48 +02:00
Lioncache 47a0f14537 OpcodeDispatcher: Remove unnecessary 128-bit truncating moves from StoreResult
Removes the truncating move that we perform inside the StoreResult
function and instead delegates the responsibility to the instruction
implementations themselves.

This removes a lot of redundant moves that occur on 128-bit variants
of AVX instructions.

Also fixes a weird case where we were handling 128-bit SVE
in VBroadcastFromMem when we already have AdvSIMD instructions
that will perfom the zero-extension behavior for us.
2023-10-17 11:07:04 +02:00
Ryan Houdek 6d39f369b0 Merge pull request #3199 from lioncash/loadops
OpcodeDispatcher: Put extra LoadSource options in a struct
2023-10-16 09:42:27 +02:00
Lioncache 2304cfc530 OpcodeDispatcher: Remove prefixing from MemoryAccessType enum
Since this is an enum class, we don't need to add a prefix.
2023-10-16 03:10:33 +02:00
Lioncache 1a39de4509 OpcodeDispatcher: Put extra LoadSource options in a struct
Allows for easier expansion without needing to expand the function definitons.

Also makes a few usages significantly less verbose and makes specifying
options a little more declarative, rather than having to memorize what
each argument is specifying.
2023-10-15 21:18:00 +02:00
Ryan Houdek efb479f88f Merge pull request #3198 from lioncash/rorx
OpcodeDispatcher: Remove redundant moves from rorx
2023-10-15 17:13:00 +02:00
Lioncache b27bf43901 OpcodeDispatcher: Remove redundant moves from rorx
By allowing junk in the upper bits, we can avoid an unnecessary move,
since we'll be ignoring them in the following ROR instruction anyway.
2023-10-15 17:05:15 +02:00
Ryan Houdek c612fa8f2f Merge pull request #3196 from lioncash/fmt 2023-10-13 10:44:43 -07:00
Lioncache 4ccc40f697 Externals: Update fmt from 10.1.0 to 10.1.1
Notably this bugfix version also introduces support for formatting
std::atomic types and std::atomic_flag.

Also, of course keeps our tracked external up to date.
2023-10-13 13:31:49 -04:00
Ryan Houdek cb53a704ba Merge pull request #3195 from Sonicadvance1/fix_missing_vulkan
Thunks: Fixes missing vulkan definitions
2023-10-11 18:51:52 -07:00
Alyssa Rosenzweig 483423674a Merge pull request #3194 from Sonicadvance1/instcountci_multiinst_tests
InstCountCI: Adds some multi instruction tests
2023-10-11 05:06:55 -04:00
Ryan Houdek 180d16af7a Thunks: Fixes missing vulkan definitions
A couple of games were hitting these. Not sure how they were missed in
PR #3159 but adds the missing one.

Small rearrangement to make this easier as well. Hopefully thunk stuff
lands sooner rather than later to automate this for Vulkan.

Maybe `-isystem` instead of `-I` needs to be used unlike what #2076,
might depend on what is installed on the host system.
2023-10-11 01:49:42 -07:00
Ryan Houdek 1acc038826 InstCountCI: Adds some multi instruction tests
Some simple tests to showcase instructions that we can optimize.
- Back to back pushes could be optimized
- Back to back scalar vector operations can be optimized
- Show with AFP that back to back scalar is already optimal
   - Also ensures we don't break this stage.
2023-10-10 16:45:39 -07:00
Alyssa Rosenzweig cc558fd5dc Merge pull request #3192 from Sonicadvance1/optimize_small_push
OpcodeDispatcher: Optimizes < 32-bit register push
2023-10-10 17:10:34 -04:00
Ryan Houdek 8f04223193 InstCountCI: Adds push changed results
Removes an accidental Test.json file that I use locally.
2023-10-10 12:06:36 -07:00
Ryan Houdek 4be649c44e OpcodeDispatcher: Optimizes < 32-bit register push
We don't need zero in the upper bits for a push.
Makes a couple variants optimal.

Adds missing tests to the 32-bit file, since only 32-bit can push a
32-bit register.
2023-10-10 12:04:41 -07:00
Ryan Houdek 6253f4f708 Merge pull request #3186 from Sonicadvance1/afp_support
IR: Adds scalar vector insert operations
2023-10-10 11:28:22 -07:00
Ryan Houdek cc2eef619c InstCountCI: Update for AFP optimizations
A bunch of random instructions have converted to be optimal in a vacuum.
2023-10-10 03:44:59 -07:00
Ryan Houdek 3bff42e6a7 OpcodeDispatcher: Wire up support for the new scalar insert operations 2023-10-10 03:44:58 -07:00
Ryan Houdek 2671246fef IR: Adds scalar vector insert operations
These IR operations are required to support AFP's NEP mode which does
vector insert in to the destination register. Additionally it gives us
tracking information to allow optimizing out redundant inserts on
devices that don't support AFP natively.

In order to match x86 semantics we need to support binary and unary
scalar operations that do a final insert in to a vector. With optional
zeroing of the top 128-bits for AVX variants.

A tricky thing is that in binary operations this means that the
destination and first source have an intrinsically linked property
depending on if it is SSE or AVX.

SSE example:
- addss xmm0, xmm1
   - xmm0 is both the destination and the first source.
   - This means xmm0[31:0] = xmm0[31:0] + xmm1[31:0]
   - Bits [127:32] are UNMODIFIED.

FEX's JIT jumps through some hoops so that if the destination register
equals the first source register, then it hits the optimal path the
AFP.NEP will insert in to the result. AVX throws a small wrench in to
this due to changed behaviour

AVX example:
- vaddss xmm0, xmm1, xmm2
  - xmm0 is ONLY the destination, xmm1 and xmm2 are the sources
  - This operation copies the bits above the scalar result from the
    first source (xmm1).
  - Additionally this will zero bits above the original 128-bit xmm
    register.
  - xmm0[31:0] = xmm1[31:0] + xmm2[31:0]
  - xmm0[127:32] = xmm1[127:32]
  - ymm0[255:127] = 0

This causes these instructions to support a fairly large table depending
on if the instruction is an SSE or AVX instruction, plus if the host CPU
supports AFP or not.

So while fairly complex, it's handling all the edge cases and gives us
optimization opportunities as we move forward. Currently on non-AFP
supporting devices this has a minor benefit that these IR operations
remove one temporary register, lowering the Register Allocation
overhead.

In the coming weeks I am likely to introduce an optimization pass that
removes redundant inserts because FEX currently does /really/ badly with
scalar code loops.

Needs #3184 merged first.
2023-10-10 03:17:19 -07:00
Ryan Houdek 8cb8f090dd Arm64Emitter: enable/disable AFP on Fill/Spill
When FEX is in the JIT we need to make sure to enable NEP and AH and
then disable when leaving.

Explicitly disabled when the vixl simulator is used since even
attempting to set the bits will cause it to fault out. Ensures
InstCountCI keeps working.
2023-10-10 03:17:18 -07:00
Ryan Houdek a37d89a7d5 HostFeatures: Disable AFP until verified that it is working
Need to audit scalar instruction usage to ensure all uses are okay with
garbage in the upper bits.
2023-10-10 03:17:18 -07:00
Ryan Houdek 252d7712ea Arm64: Save if the host supports AFP 2023-10-10 03:17:18 -07:00
Ryan Houdek c548625fbe InstCountCI: Update tests for disabling AFP
Doesn't change behaviour yet, just prep work.
2023-10-10 03:17:18 -07:00
Ryan Houdek f036a0b84f Merge pull request #3191 from Sonicadvance1/instcountci_multiple
InstCountCI: Support multiple instructions in the tests
2023-10-10 02:53:14 -07:00
Ryan Houdek 2e1389b25e Merge pull request #3184 from Sonicadvance1/armemitter_sized_scalars
ArmEmitter: Adds sized Scalar 1 source and 2 source helpers
2023-10-10 02:53:06 -07:00
Alyssa Rosenzweig a5f82a57fa Merge pull request #3190 from Sonicadvance1/atomic_instcountci
InstCountCI: Adds missing atomic tests
2023-10-10 05:22:36 -04:00
Ryan Houdek cd83d3eb24 InstCountCI: Support multiple instructions in the tests
There are some cases where we want to test multiple instructions where
we can do optimizations that would overwise be hard to see.

eg:
```asm
; Can be optimized to a single stp
push eax
push ebx

; Can remove half of the copy since we know the direction
cld
rep movsb

; Can remove a redundant insert
addss xmm0, xmm1
addss xmm0, xmm2
```

This lets us have arbitrary sized code in instruction count CI, with the
original json key becoming only a label if the instruction array is
provided.

There are still some major limitations to this, instructions that
generate side-effects might have "garbage" after the end of the block
that isn't correctly accounted for. So care must be taken.

Example in the json
```json
"push ax, bx": {
  "ExpectedInstructionCount": 4,
  "Optimal": "No",
  "Comment": "0x50",
  "x86Insts": [
    "push ax",
    "push bx"
  ],
  "ExpectedArm64ASM": [
    "uxth w20, w4",
    "strh w20, [x8, #-2]!",
    "uxth w20, w7",
    "strh w20, [x8, #-2]!"
  ]
}
```
2023-10-09 21:49:53 -07:00
Ryan Houdek 93ab8ab23c InstCountCI: Adds missing atomic tests
This adds all the missing atomic tests in to their own tests files.
This includes all of them except a few choice ones that are in their
original files.

- BTC, BTR, BTS  are in their Secondary/SecondaryGroup files
- CMPXCHG, CMPXCHG8B, CMPXCHG16B are in their Secondary/SecondaryGroup
  files
   - These always imply lock semantics even without the prefix.
2023-10-09 21:18:08 -07:00
Ryan Houdek 462fff2c67 Merge pull request #3189 from Sonicadvance1/remove_warnings_15
FEXCore: Removes a warning about assume discarding side-effects
2023-10-09 17:26:14 -07:00
Alyssa Rosenzweig 8dab35cbf8 Merge pull request #3188 from Sonicadvance1/reconstruct_flags_naming
FEXCore: Renames raw FLAGS location names to signify they can't be used directly
2023-10-09 19:41:09 -04:00
Ryan Houdek a1a479e69f FEXCore: Removes a warning about assume discarding side-effects 2023-10-09 16:04:57 -07:00
Ryan Houdek 6403290019 FEXCore: Renames raw FLAGS location names to signify they can't be used directly
Six of the EFLAGS can't be used directly in a bitmask because they are
either contained in a different flags location or has multiple bits
stored in it.

SF, ZF, CF, OF are stored in ARM's NZCV format in offset 24.
PF calculation is deferred but stored in the regular offset.
AF is also deferred in relation to the PF but stored in the regular
offset.

These /need/ to be reconstructed using the `ReconstructCompactedEFLAGS`
function when wanting to read the EFLAGS.

When setting these flags they /need/ to be set using
`SetFlagsFromCompactedEFLAGS`.

If either of these functions are not used when managing EFLAGs then the
internal representation will get mangled and the state will be
corrupted.

Having a little `_RAW` on these to signify that these aren't just
regular single bit representations like the other flags in EFLAGS should
make us puzzle about this issue before writing more broken code that
tries accessing it directly.
2023-10-08 11:51:11 -07:00
Ryan Houdek 580bd50a00 unittests/ASM: Removes eflags comparison option
This was not used and is also broken.
2023-10-08 11:51:11 -07:00
Ryan Houdek b2a8b0ca12 Merge pull request #3187 from Sonicadvance1/implement_rpres
FEXCore: Implements support for RPRES
2023-10-08 09:57:27 -07:00
Ryan Houdek f78bdf0852 unittests/Emitter: Adds sized scalar unittests. 2023-10-08 09:48:37 -07:00
Ryan Houdek 5652eb4c5d ARMEmitter: Removes templated ptrue/ptrues
Non-templated version exists and templated version gets us nothing.
2023-10-07 23:51:32 -07:00
Ryan Houdek a52bb47551 unittests: Update for rpres optimization 2023-10-07 23:22:51 -07:00
Ryan Houdek 22590dde77 FEXCore: Implements support for RPRES
This allows us to use reciprocal instructions which matches precision of
what x86 expects rather than converting everything to float divides.

Currently no hardware supports this, and even the upcoming X4/A720/A520
won't support it, but it was trivial to implement so wire it up.
2023-10-07 23:13:47 -07:00
Ryan Houdek 6543a80ff9 Merge pull request #3185 from Sonicadvance1/ir_dispatcher_emit
FEXCore/IR: Changes over to automated IR dispatch generation
2023-10-07 21:21:44 -07:00
Ryan Houdek 9c36d1061b Merge pull request #3182 from Sonicadvance1/instcountci_stacking_test_names
InstCountCI: Fixes recursive tests with same filename
2023-10-07 21:21:10 -07:00
Alyssa Rosenzweig 5a3cc7b469 Merge pull request #3183 from Sonicadvance1/instcountci_support_afp_override
InstCountCI: Support overriding AFP features
2023-10-07 19:57:33 -04:00
Ryan Houdek 4cff3e5f1f FEXCore/IR: Changes over to automated IR dispatch generation
Suggested by Alyssa. Adding an IR operation can be a little tedious
since you need to add the definition to JIT.cpp for the dispatch switch,
JITClass.h for the function declared, and then actually defining the
implementation in the correct file.

Instead support the common case where an IR operation just gets
dispatched through to the regular handler. This lets the developer just
put the function definition in to the json and the relevent cpp file and
it just gets picked up.

Some minor things:
- Needs to support dynamic dispatch for {Load,Store}Register and
  {Load,Store}Mem
   - This is just a bool in the json
- It needs to not output JIT dispatch for some IR operations
   - SSE4.2 string instructions and x87 operations
   - These go down the "Unhandled" path
- Needs to support a Dispatcher function override
   - This is just for handling NoOp IR operations that get used for
     other reasons.
- Finally removes VSMul and VUMul, consolidating to VMul
   - Unlike V{U,S}Mull, signed or unsigned doesn't change behaviour here
- Fixed a couple random handler names not matching the IR operation
  name.
2023-10-07 15:01:47 -07:00
Ryan Houdek a1eb571630 ArmEmitter: Adds sized Scalar 1 source and 2 source helpers
Removes the need for an annoying switch statement with scalar operations
for the most part.
2023-10-07 11:51:32 -07:00
Ryan Houdek 559cf6491a InstCountCI: Support overriding AFP features
Also disable AFP under the vixl simulator by default since it doesn't support it.
2023-10-07 11:48:42 -07:00
Ryan Houdek 4bdda1eeb5 InstCountCI: Fixes recursive tests with same filename
This will be used to move AFP tests to a sub-directory
2023-10-07 11:47:16 -07:00
Mai fc70fc3506 Merge pull request #3179 from Sonicadvance1/support_hostfeature_crypto
FEXCore: Support crypto extensions in HostFeatures override
2023-10-06 16:01:59 -04:00
Mai 26ee63cc24 Merge pull request #3181 from Sonicadvance1/remove_spurious_license
External: Remove a spurious license
2023-10-06 15:59:41 -04:00
Ryan Houdek 0092ea7c0b External: Remove a spurious license
This doesn't exist anymore
2023-10-06 09:37:17 -07:00
Ryan Houdek 439a3b9c3a HostFeatures: Use a define 2023-10-06 09:33:41 -07:00
Alyssa Rosenzweig b4ddf36582 Merge pull request #3180 from Sonicadvance1/remove_warnings_14
Linux: Fixes warning in 32-bit clock_settime
2023-10-06 08:13:52 -04:00
Ryan Houdek 12c44f26e5 Linux: Fixes warning in 32-bit clock_settime
This syscall requires a valid pointer otherwise it returns EFAULT.
When going through the glibc helper it can crash before reaching the raw
syscall even.
2023-10-05 17:44:33 -07:00
Ryan Houdek 5b7ba06d5c FEXCore: Support crypto extensions in HostFeatures override
Enables in InstCountCI so Pi users can run InstCountCI can run the tests
without breaking on crypto operations.

When crypto is enabled or disabled just wholesale change AES, CRC32, and
PMULL 128-bit in one step. We don't really care about partial support
here.
2023-10-05 17:41:08 -07:00
Ryan Houdek ee0c1457d8 Docs: Update for release FEX-2310 2023-10-05 14:39:10 -07:00
Alyssa Rosenzweig 3413eb3d98 Merge pull request #3169 from Sonicadvance1/remove_constant_indirection
FEXCore: Support CpuState relative vector named constants
2023-10-05 08:22:48 -04:00
Ryan Houdek 2e0753a244 InstCountCI: Update for named vector constant optimization 2023-10-04 20:57:09 -07:00
Ryan Houdek 8a51bb7a61 FEXCore: Support CpuState relative vector named constants
The motivation towards just having a pointer array in CpuState was that
initialization was fairly cheap and that we have limited space inside
the encoding depending on what we want to do.

Initialization cost is still a concern but doing a memcpy of 128-bytes
isn't that big of a deal.

Limited space in CpuState, while a concern isn't a significant one.
   - Needs to currently be less than 1 page in size
   - Needs to be under the architectural offset limitations of loadstore
     scaled offsets. Which is 65KB for 128-bit vectors

Still keeps the pointer array around for cases when we would need
synthesize an address offset and it's just easier to load the
process-wide table.

The performance improvement here is removing the dependency in the
ldr+ldr chain. In microbenchmarks this has shown to have an improvement
of ~4% by removing this dependency chain on Cortex-X1C.
2023-10-04 20:56:29 -07:00
Ryan Houdek ee6debe8fd FEXCore: Adds DividePow2 helper 2023-10-04 20:56:29 -07:00
Mai 3ba1c7912c Merge pull request #3178 from Sonicadvance1/fix_avx_alias_precolour
Minor AVX optimizations
2023-10-04 21:31:20 -04:00
Ryan Houdek a408afaeb0 InstCountCI: Update for optimized AVX 2023-10-04 10:05:09 -07:00
Ryan Houdek fba7c4bedc IR/RA: Fixes register aliasing and pre-colouring for AVX
This is the cause of a bunch of redundant moves that shows up in
InstCountCI. Fixing this aliasing and pre-colouring issue causes a ton
of 256-bit operations to become optimal.
2023-10-04 10:04:06 -07:00
Ryan Houdek c52753e9c8 OpcodeDispatcher: Minor optimization in vzeroall
Using the cached zero value is less efficient than loading it in to the
register for all these cases.

Lets us use rename hardware more efficiently and removes a dependency
chain on a single register.

Original:
```
movi v2.2d, #0x0
mov z16.d, p7/m, z2.d
<... 16 more times>
mov z31.d, p7/m, z2.d
```

Result:
```
movi v16.2d, #0x0
<... 16 more times>
movi v31.2d, #0x0
```
2023-10-04 10:01:13 -07:00
Ryan Houdek e39634d314 Arm64: Fixes assert in VSQSHL/VSQSHR with SVE
When Dst != Vector then we need to pass Dst in to both Zd and Zdn.
Would have worked fine in a release build but assert build managed to
capture it.
2023-10-04 09:59:59 -07:00
Ryan Houdek 507cf82dad Merge pull request #3176 from neobrain/fix_thunks_unused_artifacts
Thunks: Only build guest target for libfex_thunk_test if FEXLinuxTests are enabled
2023-10-04 07:07:18 -07:00
Ryan Houdek 48fa4f1121 Merge pull request #3156 from neobrain/feature_thunk_data_layout_analysis
Thunks: Analyze data layout to detect platform differences
2023-10-04 07:06:49 -07:00
Tony Wasserka e06d609bf0 Thunks: Drop unused STRUCT_VERIFIER define from CMake 2023-10-03 11:43:29 +02:00
Tony Wasserka 0a09e04e33 Thunks: Only build guest target for libfex_thunk_test if FEXLinuxTests are enabled 2023-10-03 11:43:27 +02:00
Ryan Houdek a1a709f948 Merge pull request #3170 from Sonicadvance1/vixl_sim_instcountci
InstCountCI: Enable running on x86 hosts
2023-10-02 16:38:25 -07:00
Ryan Houdek 5925eef213 Github/InstCountCI: Enables x86 runner
To ensure we don't break this path for developers.
2023-10-02 16:26:14 -07:00
Ryan Houdek df369bd6a0 InstCountCI: Enable running on x86 hosts
This is a quality of life improvement for people that want to tinker
with the InstCountCI but they may not necessarily have an Arm64 device
available immediately for poking.

As long as the vixl disassembler is enabled then the InstCountCI tests
can run and get bit-accurate encodings just like on an Arm64 device.

This also ensures that behaviour is consistent with or without the vixl
simulator enabled which is very important when running on x86 hosts.
2023-10-02 16:26:14 -07:00
Ryan Houdek 978489fce1 InstCountCI: Explicitly disable SVE256 for one test group
These instructions are specifically testing the SVE128 implementations,
don't want SVE256 mucking up the instructions.
2023-10-02 16:26:14 -07:00
Ryan Houdek d5a4d9b17f InstCountCI: Adds option to disable cssc for tests
One x87 instruction was using CSSC abs
2023-10-02 16:26:14 -07:00
Ryan Houdek 9933ef07ea Tools: Enable indirect vixl runtime calls if simulator is used
So tests can still run.
2023-10-02 16:26:14 -07:00
Ryan Houdek 6964e65660 HostFeatures: Hardcode icache and dcache line size on x86
64-byte is effectively part of x86's ABI anyway. No need to query it for
our uses.
2023-10-02 16:26:14 -07:00
Ryan Houdek 11db8e7506 FEXCore: Wire up the new option to disable vixl indirect runtimes
Also so it compiles without the vixl simulator enabled.
2023-10-02 16:26:12 -07:00
Ryan Houdek b6b5e93dbb Config: Adds an option to disable vixl sim indirect runtime calls 2023-10-02 16:23:11 -07:00
Ryan Houdek 935b3a313a Merge pull request #3171 from Sonicadvance1/merge_dispatcher
FEXCore: Merge Arm64Dispatcher in to Dispatcher
2023-10-02 16:22:36 -07:00
Tony Wasserka fe681ab335 unittests/ThunkLibs: Specify clang resource directory when compiling test code 2023-10-02 22:18:23 +02:00
Tony Wasserka 2d9e816ff5 unittests/ThunkLibs: Add various tests for structs repacking and for void parameters 2023-10-02 22:18:23 +02:00
Tony Wasserka b04b0549a9 unittests/ThunkLibs: Add data layout tests 2023-10-02 22:18:22 +02:00
Tony Wasserka 2b472cb962 Thunks/gen: Enforce type compatibility for function parameters 2023-10-02 22:18:22 +02:00
Tony Wasserka 7f931b5623 Thunks/gen: Add detection logic for data layout differences
This runs the data layout analysis pass added in the previous change twice:
Once for the host architecture and once for the guest architecture. This
allows the new DataLayoutCompareAction to query architecture differences for
each type, which can then be used to instruct code generation accordingly.

Currently, type compatibility is classified into 3 categories:
* Fully compatible (same size/alignment for the type itself and any members)
* Repackable (incompatibility can be resolved with emission of automatable
  repacking code, e.g. when struct members are located at differing offsets
  due to padding bytes)
* Incompatible
2023-10-02 22:18:22 +02:00
Tony Wasserka 070fa9f924 Thunks/gen: Add data layout analysis
This adds a ComputeDataLayout function that maps a set of clang::Types
to an internal representation of their data layout (size, member list, ...).
2023-10-02 22:18:22 +02:00
Tony Wasserka 371bf50c76 Thunks/gen: Track data types passed across architecture boundaries
The set of these types is tracked in AnalysisAction, to which extensive
verification logic is added to detect potential incompatibilities and to
enforce use of annotatations where needed.
2023-10-02 22:18:22 +02:00
Tony Wasserka d65d29903b Thunks/gen: Rename EmitOutput to OnAnalysisComplete 2023-10-02 22:03:10 +02:00
Tony Wasserka 7791e0090d Thunks: Disable 32-bit host thunks
These are not supported yet.
2023-10-02 22:03:10 +02:00
Alyssa Rosenzweig 02da6d6ce7 Merge pull request #3174 from Sonicadvance1/remove_steam_appconfig
AppConfig: Removes Steam config
2023-10-01 18:48:30 -04:00
Ryan Houdek a478cbb694 AppConfig: Removes Steam config
This was only required on x86 devices trying to escape the emulation.
Since x86 is now remove, this is entirely unnecessary.

When Steam launches applications with `/bin/sh`, this will remain under
the emulation and not escape these days.
2023-10-01 08:46:53 -07:00
Ryan Houdek 3a25dd6d2b Merge pull request #3173 from CallumDev/x87f64-fabs
X87F64: Implement FABS with vector instruction
2023-10-01 01:54:11 -07:00
CallumDev 9c25db83d9 JIT: VectorOps remove extraneous element size logs 2023-10-01 15:03:21 +10:30
CallumDev 7346476546 Update InstCountCI 2023-10-01 14:41:13 +10:30
CallumDev c42b581378 X87F64: Implement FABS with vector instruction 2023-10-01 14:39:55 +10:30
Ryan Houdek ccfd770d9d Merge pull request #3172 from CallumDev/x87f64-opts
X87F64: Use Bfe for rounding mode, FCHS use float instruction
2023-09-30 18:41:29 -07:00
CallumDev d4a623a3fb InstCountCI Update 2023-10-01 11:22:18 +10:30
CallumDev c09c25005e X87F64: Use Bfe for rounding mode, FCHS use float instruction 2023-10-01 11:11:33 +10:30
Ryan Houdek 90570fd5f4 FEXCore: Merge Arm64Dispatcher in to Dispatcher
With the removal of the x86 JIT, there is no need to have these be
independent classes.

Merges the Arm64Dispatcher in to the base Dispatcher class.
No functional change, just moving code.
2023-09-30 09:31:55 -07:00
Mai ab4642af38 Merge pull request #3167 from Sonicadvance1/gatherqdps
unittests/ASM: Implements tests for vpgatherqd/vgatherqps
2023-09-29 12:16:43 -04:00
Mai d94e5ce7f4 Merge pull request #3168 from Sonicadvance1/gatherqqpd
unittests/ASM: Implements tests for vpgatherqq/vgatherqpd
2023-09-29 12:16:12 -04:00
Mai dad7086fd0 Merge pull request #3166 from Sonicadvance1/gatherdqpd
unittests/ASM: Implements tests for vpgatherdq/vgatherpq
2023-09-29 12:15:39 -04:00
Ryan Houdek a21def7d74 unittests/ASM: Implements tests for vpgatherqq/vgatherqpd
Similar to previous tests, vpgatherqq and vgatherqpd are equivalent
instructions. So the tests are the same with the mnemonic changed.

This adds tests for an additional two sets of instructions. Getting us
full coverage of all eight instructions if we include the tests from
PR #3167 and #3166

Tests the same things as described in #3165

In addition, since these tests use 64-bit indices for address
calculation, we can easily generate and indice vector that tests
overflow. So every test at every displacement ALSO gains an additional
overflow test to ensure correct behaviour around pointer overflow
calculation.
2023-09-29 08:04:47 -07:00
Ryan Houdek 0d8d5444a4 unittests/ASM: Implements tests for vpgatherqd/vgatherqps
Similar to previous tests, vgatherqd and vgatherqps are equivalent
instructions. So the tests are the same with the mnemonic changed.

This adds tests for an additional two sets of instructions, Getting us
up to six total over the eight if we include the tests from #3166.

Tests the same things as described in #3165

In addition, since these tests use 64-bit indices for address
calculation, we can easily generate and indice vector that tests
overflow. So every test at every displacement ALSO gains and additional
overflow test to ensure correct behaviour around pointer overflow
calculation.
2023-09-29 07:20:07 -07:00
Ryan Houdek eedfad5036 unittests/ASM: Implements tests for vpgatherdq/vgatherpq
Just like the previous tests, vpgatherdq and vgatherpq are equivalent
instructions. So the tests are the same except for the instruction
mnemonic again.

This adds unittests for two more of the eight gather instructions.
Getting us up to testing four in total.
Specifically this adds tests for 32-bit indices while loading 64-bit
element instructions.

Same thing as PR #3165 for what it tests versus doesn't.
2023-09-28 22:49:03 -07:00
Ryan Houdek 85da0f0640 Merge pull request #3165 from Sonicadvance1/gatherddps
unittests/ASM: Implements tests for vpgatherdd/vgatherps
2023-09-28 22:44:38 -07:00
Ryan Houdek 9a01b440e3 unittests/ASM: Implements tests for vpgatherdd/vgatherps
vpgatherdd and vgatherps are effectively the same instructions, so the
tests are the same except for the instruction mnemonic.

This adds unit tests for two of the eight gather instructions.
Specifically this adds tests for the 32-bit indices loading 32-bit
elements instructions.

What it tests:
- Tests all displacement scales
- Tests multiple mask arrangements
- Ensures the mask register is zero'd after the instruction

What it doesn't test:
- Doesn't test address size calculation overflow
   - Only would happen on 32-bit with 32-bit indices, or /really/ high
     base addresses
   - The instruction should behave as a mask to the address size
   - Effectively behaves like `(uint64_t)(base + index << ilog2(scale))`
   - Better idea is to just not expose AVX to 32-bit applications
- Doesn't test VSIB immediate displacement
   - This just ends up being base_addr + imm so it isn't too interesting
   - We can add more tests in the future if we think we messed that up
- Doesn't test partial fault behaviour
   - Because that's a nightmare.

Specifically keeps each instruction test small and isolated so if a
single register fails it is very easily to nail down which operation did
it.
I know some of our ASM tests do a chunk of work and spit out a result at
the end which can be difficult to debug in some cases. Didn't want to do
that which is why the tests are spread out across 16 files for these
single class of instructions.
2023-09-28 19:58:34 -07:00
Ryan Houdek 228ee7fa47 TestHarnessRunner: Support AVX2 flag detection 2023-09-28 19:58:34 -07:00
Ryan Houdek 98789a8039 FEXCore: Implement support for AVX2 feature detection 2023-09-28 19:57:08 -07:00
Ryan Houdek 14398742c3 Merge pull request #3164 from neobrain/fix_thunks_asan
Thunks: Fix AddressSanitizer build
2023-09-28 12:05:55 -07:00
Tony Wasserka 5a7e3192da Thunks: Fix AddressSanitizer build 2023-09-28 15:13:03 +02:00
Ryan Houdek 6b4ff4ae81 Merge pull request #3163 from alyssarosenzweig/opt/ascii-flags
Optimize ASCII flags
2023-09-27 10:42:47 -07:00
Ryan Houdek d1d3de80d1 Merge pull request #3157 from alyssarosenzweig/opt/unmask-in
OpcodeDispatcher: Don't mask logic op inputs
2023-09-27 10:38:12 -07:00
Alyssa Rosenzweig 2e32e1367d InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-27 10:55:57 -04:00
Alyssa Rosenzweig 711583aa76 OpcodeDispatcher: Optimize PTEST flags
Zero NZCV first to avoid RMW.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-27 10:55:57 -04:00
Alyssa Rosenzweig 3efac9646c OpcodeDispatcher: Optimize ASCII flags
Make the zeroing of undefined NZCV more obvious. Mitigates regressions from
future work.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-27 10:31:31 -04:00
Alyssa Rosenzweig 095a362046 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 20:30:09 -04:00
Alyssa Rosenzweig 3bb64c64e3 OpcodeDispatcher: Don't mask for TEST
Like AND.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 20:30:02 -04:00
Alyssa Rosenzweig a4de164944 OpcodeDispatcher: Use lshr for ah/bh with AllowUpperGarbage
If we ever get around to fusing ops with shifts in the ConstProp optimizer (may
or may not be worthwhile), this will delete an instruction from things like "or
al, bh".

Even though lsr is the same speed as bfe on Firestorm, I feel if you ask for
garbage you should get garbage C:

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 20:28:01 -04:00
Alyssa Rosenzweig 45a645fbbc OpcodeDispatcher: Don't mask logic op inputs
Pointless, upper bits ignored anyway. Deletes piles of uxt and even some 32-bit
instruction moves.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 19:12:22 -04:00
Alyssa Rosenzweig 92211bf8c6 OpcodeDispatcher: Add AllowUpperGarbage option
To load 8-bit sources without bfe'ing for al/bl/cl if the caller knows it
doesn't need masking behaviour, but without lying about the size so the extract
for ah/bh/ch will still work properly.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 19:08:20 -04:00
Alyssa Rosenzweig 728d3f8ac7 InstCountCI: Add a case with a hi 8-bit reg
Noticeably different code pattern.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 18:33:55 -04:00
Ryan Houdek ca87d8688d Merge pull request #3153 from alyssarosenzweig/opt/adcs
Use adcs
2023-09-26 09:57:01 -07:00
Ryan Houdek e32601f49d Merge pull request #3161 from neobrain/fix_ctest_silent_failures
unittests: Instruct CTest to print output from tests on failure
2023-09-26 08:26:15 -07:00
Tony Wasserka f4dd456c80 unittests: Instruct CTest to print output from tests on failure 2023-09-26 17:16:28 +02:00
Alyssa Rosenzweig 7b22dbfe24 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 10:05:59 -04:00
Alyssa Rosenzweig 7a06cc9727 IR: Use adcs/sbcs
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-26 09:06:46 -04:00
Ryan Houdek 8b3881b5db Merge pull request #3154 from alyssarosenzweig/opt/smol-carry
Optimize 8/16-bit CF calculation
2023-09-26 05:49:07 -07:00
Ryan Houdek 76d4637d9c Merge pull request #3159 from neobrain/feature_update_vulkan
Thunks: Update Vulkan thunk to v1.3.261.1
2023-09-26 05:20:18 -07:00
Alyssa Rosenzweig 0d12cce74f Merge pull request #3158 from Sonicadvance1/unittest_for_3153
unittests/ASM: Adds unit test caught by #3153
2023-09-26 08:15:40 -04:00
Tony Wasserka 04592af609 Thunks: Update Vulkan thunk to v1.3.261.1 2023-09-26 12:14:58 +02:00
Ryan Houdek d8366c04dc unittests/ASM: Adds unit test caught by #3153 2023-09-26 00:28:45 -07:00
Ryan Houdek 533f35934c Merge pull request #3155 from neobrain/opt_thunks_rebuilds
Thunks: Avoid recompiling thunk interfaces on FEXLoader changes
2023-09-25 19:21:09 -07:00
Alyssa Rosenzweig 35bb7cc801 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-25 19:41:31 -04:00
Alyssa Rosenzweig 5facb21d30 OpcodeDispatcher: Don't mask small add/sub carries
For the GPR result, the masking already happens as part of the bfi. So the only
point of masking is for the flag calculation. But actually, every flag except
carry will ignore the upper bits anyway. And the carry calculation actually
WANTS the upper bit as a faster impl.

Deletes a pile of code both in FEX and the output :-)

ADC/SBC could probably get similar treatment later.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-25 18:25:30 -04:00
Tony Wasserka adead832a5 Thunks: Avoid recompiling thunk interfaces on FEXLoader changes
The interface files themselves don't use FEXLoader. Only the final library
does.
2023-09-25 23:04:09 +02:00
Ryan Houdek 5eed24a242 Merge pull request #3152 from Sonicadvance1/instcountci_x87_f64
InstCountCI: Support f64 reduced precision mode tests
2023-09-24 19:29:37 -07:00
Ryan Houdek 7907f70ed2 InstCountCI: Adds new x87 reduced precision mode tests 2023-09-24 18:50:05 -07:00
Ryan Houdek 7141332f6f InstCountCI: Support setting environment variables in tests
This will allow us to enable FEX options through environment variables
just like the ASM tests.
2023-09-24 18:50:01 -07:00
Ryan Houdek 234e029391 Merge pull request #3145 from Sonicadvance1/optimize_inline_calls
PassManager: Optimize out CPUID and XGetBV calls
2023-09-24 18:09:18 -07:00
Ryan Houdek 19a7b514e6 Merge pull request #3150 from alyssarosenzweig/opt/ornror
Optimize PF calculation in lahf
2023-09-24 18:05:57 -07:00
Ryan Houdek 220761a0e8 Merge pull request #3151 from Sonicadvance1/unique_name_workflow_jobs
Github: Changes jobs to have unique names
2023-09-24 18:03:57 -07:00
Alyssa Rosenzweig cbd4daddff InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 20:59:28 -04:00
Alyssa Rosenzweig c8519b0b87 OpcodeDispatcher: Remove LoadPF
Now unused, its former users all prefer LoadPFRaw since they can fold in some of
this math into the use.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 20:59:28 -04:00
Alyssa Rosenzweig 68d32ad70d OpcodeDispatcher: Optimize PF in lahf
Use the raw popcount rather than the final PF and use some sneaky bit math to
come out 1 instruction ahead.

Closes #3117

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 20:59:28 -04:00
Ryan Houdek 62890f148f Github: Changes jobs to have unique names
These overlapping names make it impossible to ensure all checks are
required to pass before merge.

Unique names will fix this.
2023-09-24 17:52:47 -07:00
Alyssa Rosenzweig 1f02a6da34 IR: Add Ornror op
Mostly copypaste of Orlshl... we really should deduplicate this mess somehow.
Maybe a shift enum on the core Or op?

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 20:47:50 -04:00
Alyssa Rosenzweig 86063411dc Revert "OpcodeDispatcher: Use plain Lshl for flags"
This logic is unused since 8adfaa9aa ("OpcodeDispatcher: Use SelectCC for x87"),
which addressed the underlying issue.

This reverts commit df3833edbe.
2023-09-24 20:47:50 -04:00
Ryan Houdek 9968e6431f Passes: Rename SyscallOptimization
This is now inlining multiple external calls out of the JIT. Rename it
to InlineCallOptimization.
2023-09-24 17:25:38 -07:00
Ryan Houdek ff24f64b2a PassManager: Optimize out CPUID and XGetBV calls
If we const-prop the required functions and leafs then we can directly
encode the CPUID information rather than jumping out of the JIT.
In testing almost all CPUID executions const-prop which function is
getting called. Worst case that I found was only 85% const-prop rate.

This isn't quite 100% optimal since we need to call the RCLSE and
Constprop passes after we optimize these, which would remove some
redundant moves.

Sadly there seems to be a bug in the constprop pass that starts crashing
applications if that is done.
Easily enough tested by running Half-Life 2 and it immediately hitting
SIGILL.

Even without this optimization, this is stil a significant savings since
we aren't jumping out of the JIT anymore for these optimized CPUIDs.
2023-09-24 17:25:38 -07:00
Ryan Houdek e9a7ef2534 CPUID: Describe CPUID functions if they return constant state or not
Most CPUID routines return constant data, there are four that don't.
Some CPUID functions also need the leaf descriptor, so we need to
describe that as well.

Functions that don't return constant data:
- function 1Ah - Returns different data depending on current CPU core
- function 8000_000{2,3,4} - Different data based on CPU core

Functions that need leaf constprop:
- 4h, 7h, Dh, 4000_0001h, 8000_001Dh
2023-09-24 17:25:38 -07:00
Ryan Houdek 842c57e221 CPUID: Constify some functions
These don't modify CPUIDEmu state.
2023-09-24 17:25:38 -07:00
Ryan Houdek 93aeb157b4 Merge pull request #3149 from Sonicadvance1/fail_on_change
InstCountCI: Fail CI if there was any difference.
2023-09-24 17:23:52 -07:00
Ryan Houdek 02ff9f200c InstCountCI: Upload diff and check for failure 2023-09-24 17:14:08 -07:00
Ryan Houdek f65b40f298 InstCountCI: Fail if inst count has changed 2023-09-24 17:14:08 -07:00
Ryan Houdek c38beff826 Merge pull request #3148 from Sonicadvance1/add_negative_primaries
InstCountCI: Adds negative immediate primary tests
2023-09-24 17:13:34 -07:00
Ryan Houdek 94c22b2269 InstCountCI: Adds negative immediate primary tests
Noticed these were missing
2023-09-24 17:02:58 -07:00
Ryan Houdek bee97309f6 Merge pull request #3147 from alyssarosenzweig/opt/0924
More opts to the dispatcher + 1 to the JIT
2023-09-24 17:01:37 -07:00
Alyssa Rosenzweig 331941dec6 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 19:52:35 -04:00
Alyssa Rosenzweig 8798e0cba0 Arm64: Rewrite Set/GetRoundingMode
I went auditing for places to use cset and what I found was hot garbage.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 19:52:35 -04:00
Alyssa Rosenzweig c5fc03dac4 OpcodeDispatcher: Use cset for blsr/etc flags
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 19:52:35 -04:00
Alyssa Rosenzweig e63871ed2e OpcodeDispatcher: Handle sub in CalculateOF
Gets us the constant source optimization without more code duplication. And
honestly I prefer the combined presentation.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 19:52:35 -04:00
Alyssa Rosenzweig ea8b7633eb OpcodeDispatcher: Optimize OF calc of immediates
If we know the sign of one of the sources, we can do better when calculating OF.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-24 18:16:09 -04:00
Ryan Houdek e795ec683d Merge pull request #3139 from Sonicadvance1/workaround
FEXServerClient: Adds back ServerSocketPath config option
2023-09-23 17:09:04 -07:00
Ryan Houdek 6dc5c0d3be Merge pull request #3144 from Sonicadvance1/optimize_redundant_store_load
RCLSE: Optimize redundant store->load operations
2023-09-23 17:06:10 -07:00
Ryan Houdek eb5e0be569 FEXServerClient: Adds back ServerSocketPath config option
This option was disabled a few months ago when we switched the server
socket from a filesystem unix socket to an abstract socket.
This partially broke our chroot scripts which relied on this option
existing.

Readds support for an explicitly named abstract socket named from
config.

This is a workaround for dealing with chroots that change users.
They end up changing a user while doing operations and then can't
connect to the FEXServer anymore because environment variables have been
wiped away.
2023-09-23 16:59:58 -07:00
Ryan Houdek be3ff804a6 InstCountCI: Update for optimization 2023-09-23 06:11:35 -07:00
Ryan Houdek 9ab2967d71 Arm64: Fixes wide shifts
movprfx is invalid to use when the source register matches the movprfx
destination.

This was getting picked up on by `TwoByte/0F_D1.asm` now that RCLSE is
working better now.
2023-09-23 06:06:18 -07:00
Ryan Houdek d01b457727 RCLSE: Optimize redundant store->load operations
The bug that was causing crashes with this was due to inline syscalls.
Now that this is fixed we can re-enable store->load operations.

This allows constant propagation to work significantly better, which
means inline syscalls start working again. This can significantly
improve syscall performance in some cases.

This is most likely to improve performance in dxsetup and vc_redist but
hard to get a real profile.

Additionally this will let us inline cpuid results in the future which
is pretty nice.
2023-09-23 06:06:18 -07:00
Mai 4e9a114858 Merge pull request #3142 from Sonicadvance1/inline_syscall_fix
Arm64: Fixes inline syscalls
2023-09-23 09:03:49 -04:00
Mai 72d092e951 Merge pull request #3141 from Sonicadvance1/fix_simm9_range
ConstProp: Fixes unscaled signed 9-bit range
2023-09-23 09:03:01 -04:00
Mai da3e172857 Merge pull request #3140 from Sonicadvance1/fix_core_sanitization
Config: Fixes core sanitization
2023-09-23 09:01:42 -04:00
Ryan Houdek 28fa0bda31 Arm64: Fixes inline syscalls
Ever since we reordered registers in `X86Enums.h` this has silently been
broken. This wasn't hit because RCLSE has been broken ever since SRA was
added, so inlinesyscalls just weren't ever happening.

Quick fix while I think of a way to more strictly correlate these
registers so it doesn't happen again.
2023-09-23 02:56:32 -07:00
Ryan Houdek 1f2a3cfa8b ConstProp: Fixes unscaled signed 9-bit range
The range was slightly incorrect which mostly wouldn't have caused
issues.

The lowest byte would have just generated slightly less optimal code.
The upper byte could have generated broken code, which our CI couldn't
catch since TSO instructions only get enabled when multiple threads are
in-flight.

Easy enough to fix.
2023-09-23 01:13:54 -07:00
Ryan Houdek 571b0fe47e Config: Fixes core sanitization
This would have caused core to try and initialize a custom core on
Arm64, which causes a std::function assert because it doesn't support
that.

Users would likely get hit by this immediately since we deleted the
interpreter and shifted all the core numbers.
2023-09-23 00:52:23 -07:00
Ryan Houdek 86ad35c418 Merge pull request #3138 from alyssarosenzweig/opt/train
Requiem for the x86 jit
2023-09-22 16:33:15 -07:00
Alyssa Rosenzweig 0b27029c3f InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-22 19:10:41 -04:00
Alyssa Rosenzweig 223a6562ff IR: Support <32-bit TestNZ
Originally this was going to use setf8/setf16, but it looks like the approach of
shift-and-test turns out to be faster. As a bonus this is a nice delete-the-code
win :-)

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-22 19:08:26 -04:00
Alyssa Rosenzweig b1231c24ef OpcodeDispatcher: Omit AF xor for common constants
The only reason we need to XOR arguments for AF is to get bit 4 correct. But if
the operand in question is known to have bit 4 clear, the XOR will be an
effective no-op and can be skipped. This saves an instruction in a bunch of
common cases, like inc/dec. If we dedicated a register to AF to eliminate the
store, we would not save an instruction from this but would still come out ahead
due to an eor turning into a (zero cycle?) mov that can be handled by the
renamer.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-22 19:08:26 -04:00
Alyssa Rosenzweig 699aa85c4b OpcodeDispatcher: Opt PF selection
Fold the and in.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-22 19:07:42 -04:00
Alyssa Rosenzweig 2d65a3677b OpcodeDispatcher: Optimize NZCV selects
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-22 19:07:42 -04:00
Alyssa Rosenzweig 2a2619c0f5 IR: Add bit masking selects
Add new synthetic condition codes that do an AND as their relational operator,
testing the result. This is 1 IR op for things like

  (A & B) == 0 ? C : D

This can translate to

  tst A, B
  csel A, B, eq

In the future, if A is the NZCV register and B is a supported immediate, eg

  (NZCV & 0x80000000) == 0 ? C : D

this will be able to translate to a single instruction with the appropriate
condition

  csel A, B, pl

but that needs RA support.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-22 19:07:42 -04:00
Ryan Houdek 797c890ff6 Merge pull request #2874 from bylaws/wowfex
Add WOW64 JIT frontend
2023-09-22 15:47:59 -07:00
Ryan Houdek 879b41c184 Merge pull request #3134 from Sonicadvance1/remove_x86_jit
FEXCore: Removes x86 JIT.
2023-09-22 15:36:47 -07:00
Ryan Houdek 0fbf403787 Adds back in host testharnessrunner CI
Necessary for asm tests to still run in the host "core".
Useful for ensuring correct behaviour of our assembly tests.
2023-09-22 14:46:03 -07:00
Billy Laws 04cf418452 Windows: Add SPDX license identifiers 2023-09-22 10:12:40 -07:00
Billy Laws 057a7c6ee8 WOW64: Implement thread suspension handling
This provides more robust handling than a signal based approach, as the
suspender is able to wait for the suspendee to reach a suitable position and
flush its context to memory before returning.
2023-09-22 10:12:40 -07:00
Billy Laws 3d6955592b WOW64: Implement partial self-modifying code handling
This should support most simple cases of SMC, however programs which make use
of separate shared memory mappings for writing and execution are not handled.
The overall approach is the same as is done for linux, where RWX mappings are
protected to RX and then when a write occurs the signal handler invalidates the
faulting page and reprotects it to RWX until code in that page is jitted again.
2023-09-22 10:12:40 -07:00
Billy Laws f57aee0a62 WOW64: Add a templated interval list implementation
Stores binary intervals in a sorted vector container, to be used for SMC
handling.
2023-09-22 10:12:40 -07:00
Billy Laws c978fdd12f WOW64: Implement basic code invalidation handling 2023-09-22 10:12:40 -07:00
Billy Laws 19713bd20a WOW64: Implement exception handling with context restoration
When an exception occurs, pretend that we were just at the point of JIT entry
so the stack can be unwound to the wow64 SEH handler, which then handles
dispatching the exception to the x86 guest with the restored context.
2023-09-22 10:12:40 -07:00
Billy Laws 22b1fea96d WOW64: Handle unaligned atomic accesses
This is done in EnsureConsistentState rather than as a VEH to avoid needing to
go through all of wine's exception handling logic for such a hot path.
2023-09-22 10:12:40 -07:00
Billy Laws be4fcaf65c WOW64: Report CPU features based off of the emulated cpuid 2023-09-22 10:12:40 -07:00
Billy Laws 2add8a7751 Windows: Introduce a barebones FEXCore-based WOW64 BT module
This allows for running x86 applications under wine without having to run all
of wine under FEX. The JIT is invoked when running application code and then
left when handling NT syscalls or unix calls to e.g. the Vulkan driver.
2023-09-22 10:12:40 -07:00
Billy Laws 9612133088 Windows: Generate import libraries for private ntdll and wow64 APIs
The MinGW supplied import libraries are incomplete and miss a lot of
functions necessary to implement lower level windows code. To avoid
needing to many resolve every function, pull in .def files from wine
that detail the entire ntdll and wow64 APIs.
2023-09-22 10:12:40 -07:00
Billy Laws f46fd42977 Windows: Add a minimal set of wine-derived headers
These are cut down versions of wine headers containing only what is necessary
for WOW. This shouldn't carry any license implications for FEX, as per the
LGPLv3 license:

```
The object code form of an Application may incorporate material from a header
file that is part of the Library. You may convey such object code under terms
of your choice, provided that, if the incorporated material is not limited to
numerical parameters, data structure layouts and accessors, or small macros,
inline functions and templates (ten or fewer lines in length), you do both of
the following:

a) Give prominent notice with each copy of the object code that the Library is
used in it and that the Library and its use are covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
```
2023-09-22 10:12:40 -07:00
Billy Laws 51f8c83c76 Context: Add an alternative thread-oriented execute function 2023-09-22 10:12:40 -07:00
Billy Laws d641d3f61e OpcodeDispatcher: Avoid redundantly passing args to WIN32 ABI syscalls 2023-09-22 10:12:39 -07:00
Ryan Houdek 02ae59a348 github: Disables default build test on x64 2023-09-21 18:30:03 -07:00
Ryan Houdek 64df9e31c6 github: Remove mingw tests from x86 CI 2023-09-21 18:30:03 -07:00
Ryan Houdek d32bb993a8 github: Remove glibc fault tests from x86 CI 2023-09-21 18:30:03 -07:00
Ryan Houdek b5cc9a12f2 FEXCore: Removes x86 JIT.
This is blocking performance improvements. This backend is almost
unilaterally unused except for when I'm testing if games run on Radeon
video drivers.

Hopefully AmpereOne and Orin/Grace can fulfill this role when they
launch next year.
2023-09-21 18:30:02 -07:00
Ryan Houdek 65b6df9dbb Merge pull request #3133 from Sonicadvance1/remove_vestigial_interpreter
FEXCore: Removes vestigial Interpreter code
2023-09-21 18:15:32 -07:00
Ryan Houdek 31564354b1 FEXCore: Removes vestigial Interpreter code 2023-09-21 15:49:49 -07:00
Ryan Houdek fea72ce19c Merge pull request #3120 from Sonicadvance1/more_optimal_x87
FEXCore: Support preserve_all ABI for interpreter fallbacks
2023-09-21 15:35:37 -07:00
Ryan Houdek 2b7e1d10ec Merge pull request #3131 from Sonicadvance1/optimize_btr
OpcodeDispatcher: Optimize lock btr
2023-09-21 15:06:55 -07:00
Ryan Houdek 5444810d64 Merge pull request #3132 from alyssarosenzweig/opt/orlshl
Optimize reconstructing x87, harder
2023-09-21 15:02:37 -07:00
Ryan Houdek 4a2ceabfdd InstCountCI: Add atomic bit test instructions
These all can likely be more optimal.
2023-09-21 14:54:51 -07:00
Ryan Houdek 1a4d1d820b OpcodeDispatcher: Optimize lock btr
This is an atomicFetchCLR, removes two mvn instructions that are back to
back negating the source.

We didn't have this instruction combination in InstCountCI so will be a
bit hard to see.
2023-09-21 14:54:51 -07:00
Ryan Houdek 0ae4bbb9c5 IR: Implements support for AtomicFetchCLR
This is the native ARM operation rather than fetchAnd. Will make an
instruction an instruction slightly more optimal.
2023-09-21 14:54:51 -07:00
Ryan Houdek 7d99eb05c6 Merge pull request #3128 from alyssarosenzweig/rm/interp
FEXCore: Gut interpreter
2023-09-21 14:51:44 -07:00
Alyssa Rosenzweig 8247ded2cf unittests: Remove stale comments
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 12:48:12 -04:00
Alyssa Rosenzweig c52741c813 FEXCore: Gut interpreter
It is scarcely used today, and like the x86 jit, it is a significant
maintainence burden complicating work on FEXCore and arm64 optimization. Remove
it, bringing us down to 2 backends.

1 down, 1 to go.

Some interpreter scaffolding remains for x87 fallbacks. That is not a problem
here.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 12:48:12 -04:00
Alyssa Rosenzweig 75ffbc16f2 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 09:14:08 -04:00
Alyssa Rosenzweig 1596e33f58 OpcodeDispatcher: Remove pointless or
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 09:13:41 -04:00
Alyssa Rosenzweig 07d03f1610 OpcodeDispatcher: Don't opencode bfe, badly
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 09:13:41 -04:00
Alyssa Rosenzweig a8b48dcacd OpcodeDispatcher: Swap some selects
...if it lets us use cset.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 09:13:41 -04:00
Alyssa Rosenzweig bb87b2a19d OpcodeDispatcher: Use more Orlshl
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 09:13:41 -04:00
Alyssa Rosenzweig 19eff62c77 OpcodeDispatcher: Use orlshl for FCW
Potentially easier on the RA (bfi has a tied operand), mostly whatever here.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-21 08:55:25 -04:00
Mai 5fc8699db9 Merge pull request #3130 from Sonicadvance1/optimize_fsw
OpcodeDispatcher: Optimize reconstructing FSW
2023-09-21 08:35:16 -04:00
Mai 43fd159689 Merge pull request #3129 from Sonicadvance1/remove_non_explicit_selectcc
OpcodeDispatcher: Removes non-explicit SelectCC function
2023-09-21 08:33:30 -04:00
Ryan Houdek 758820ca86 InstCountCI: Update for optimized FSW reconstruction 2023-09-21 02:27:04 -07:00
Ryan Houdek 5664195e49 OpcodeDispatcher: Optimize reconstructing FSW
Minor optimization using Bfi to insert C0, C1, C2, & C3
2023-09-21 02:07:27 -07:00
Ryan Houdek 683daefc15 InstCountCI: Minor changes 2023-09-21 01:57:08 -07:00
Ryan Houdek 8e9e87f631 OpcodeDispatcher: Removes non-explicit SelectCC function
Renames the explicit sized one to `SelectCC`
Cleans up a bit of duplicated code.
2023-09-21 01:56:38 -07:00
Ryan Houdek 0a0865eb1c InstCountCI: Update for minor change 2023-09-20 18:51:18 -07:00
Ryan Houdek d588d41ab9 InterpreterFallbacks: Converts X87 and String ops to preserve_all
This improves performance!
2023-09-20 18:51:18 -07:00
Ryan Houdek 8aa8d597f6 Arm64: Supports jumping out of the JIT with preserve_all ABI
This improves perferformance when jumping out of the Arm64 JIT by
reducing the number of registers we need to save.
2023-09-20 18:51:18 -07:00
Ryan Houdek 67680d71a4 Merge pull request #3125 from Sonicadvance1/spdx_fexcore
FEXCore: Adds SPDX identifier
2023-09-19 17:42:07 -07:00
Ryan Houdek d86f41e29a Merge pull request #3124 from Sonicadvance1/spdx_fexcore_include
FEXCore/Include: Adds SPDX identifier
2023-09-19 17:41:59 -07:00
Ryan Houdek ba56e514bd Merge pull request #3123 from Sonicadvance1/spdx_fex_linux
FEX: Moves Linux utils and adds spdx
2023-09-19 17:41:51 -07:00
Ryan Houdek 9f5f09b772 Merge pull request #3122 from Sonicadvance1/spdx_fex_common
FEX/Common: Adds SPDX identifier
2023-09-19 17:41:44 -07:00
Ryan Houdek ddf4b5cbd4 Merge pull request #3121 from Sonicadvance1/spdx_tools
FEX/Tools: Adds SPDX identifier
2023-09-19 17:41:34 -07:00
Ryan Houdek e4613477b1 FEXCore/Interface/Core: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek d18ce59187 FEXCore/Interface/Core/JIT: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 1032224d62 FEXCore/Interface/Core/Interpreter: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 44767901fe FEXCore/Interface/Core/Dispatcher: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 1220c86573 FEXCore/Interface/Core/ArchHelpers: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 6ace406a2f FEXCore/Interface/Core/ObjectCache: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 38f1536255 FEXCore/Interface/Core/OpcodeDispatcher: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 324473651e FEXCore/Interface/Core/X86Tables: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 573148b27a FEXCore/Interface/Core/VSyscall: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek 12e1c2eaa0 FEXCore/Interface/Context: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek c678ea3060 FEXCore/Interface/GDBJIT: Adds SPDX identifier 2023-09-19 17:33:15 -07:00
Ryan Houdek e570b07ba0 FEXCore/Interface/Thunks: Adds SPDX identifier 2023-09-19 17:33:14 -07:00
Ryan Houdek afda4d6b7a FEXCore/Interface/IR: Adds SPDX identifier 2023-09-19 17:33:14 -07:00
Ryan Houdek 22daa506f6 FEXCore/Interface/Config: Adds SPDX identifier 2023-09-19 17:33:14 -07:00
Ryan Houdek e85b90c614 FEXCore/Common: Adds SPDX identifier 2023-09-19 17:33:14 -07:00
Ryan Houdek 9d3d33fa27 FEXCore/Utils: Adds SPDX identifier 2023-09-19 17:33:14 -07:00
Ryan Houdek 0d9dce987d Merge pull request #3126 from neobrain/feature_better_wayland_thunks64
Thunks/wayland: Add support for APIs required by zink and Super Meat Boy
2023-09-19 10:44:53 -07:00
Ryan Houdek 65d558b2c4 Merge pull request #3119 from alyssarosenzweig/opt/x87-sel
Make x87 FCMOV slightly less terrible
2023-09-19 10:34:29 -07:00
Tony Wasserka b00d413961 Thunks/wayland: Add more message signatures required by Super Meat Boy with zink 2023-09-19 17:33:24 +02:00
Tony Wasserka 6b54540756 Thunks/wayland: Add support for message signatures with nullable arguments 2023-09-19 17:33:24 +02:00
Tony Wasserka 356a42d330 Thunks/wayland: Reorder listener signatures alphabetically 2023-09-19 17:33:24 +02:00
Tony Wasserka 8fcf419183 Thunks/wayland: Add more functions required by Super Meat Boy via libdecor and SDL 2023-09-19 17:33:23 +02:00
Alyssa Rosenzweig 83c8b64c50 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-19 08:50:40 -04:00
Alyssa Rosenzweig 25943d1d17 OpcodeDispatcher: Sigh.
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-19 08:50:40 -04:00
Alyssa Rosenzweig bf03dab295 Arm64: Use csetm
Saves some moves.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-19 08:50:40 -04:00
Alyssa Rosenzweig 8adfaa9aa6 OpcodeDispatcher: Use SelectCC for x87
Better code gen and will benefit from future work.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-19 08:37:54 -04:00
Ryan Houdek ca6570d5de FEXCore/Include: Adds SPDX identifier 2023-09-18 22:13:10 -07:00
Ryan Houdek 3026f7249c FEX/Tools/CommonTools/Linux: Adds SPDX identifier 2023-09-18 22:03:29 -07:00
Ryan Houdek bea29fd2ba FEX: Moves some Linux utils to CommonTools
Was kind of in a weird place before.
2023-09-18 22:01:56 -07:00
Ryan Houdek fc55091fc5 FEX/Common: Adds SPDX identifier 2023-09-18 21:52:20 -07:00
Ryan Houdek 782cf3f7c7 Tools/Opt: Remove. Unused. 2023-09-18 21:45:25 -07:00
Ryan Houdek 8f25e9d3e6 FEXLoader: Adds SPDX identifier 2023-09-18 21:44:23 -07:00
Ryan Houdek 01175e2e7c FEXLoader/LinuxSyscalls: Adds SPDX identifier 2023-09-18 21:43:17 -07:00
Ryan Houdek 5d9d539495 FEXLoader/LinuxSyscalls/Utils: Adds SPDX identifier 2023-09-18 21:38:13 -07:00
Ryan Houdek efb5624db6 FEXLoader/LinuxSyscalls/EmulatedFiles: Adds SPDX identifier 2023-09-18 21:37:51 -07:00
Ryan Houdek fe0a16f478 FEXLoader/HostRunner: Adds SPDX identifier 2023-09-18 21:37:02 -07:00
Ryan Houdek d9d376d40d FEXLoader/ArchHelpers: Adds SPDX identifier 2023-09-18 21:36:23 -07:00
Ryan Houdek 74e7f88449 FEXLoader/AOT: Adds SPDX identifier 2023-09-18 21:35:59 -07:00
Ryan Houdek b2811ffc59 FEX/CommonGUI: Adds SPDX identifier 2023-09-18 21:35:25 -07:00
Ryan Houdek f08e1da577 FEX/CommonTools: Adds SPDX identifier 2023-09-18 21:35:07 -07:00
Ryan Houdek e863eba364 FEXGetConfig: Adds SPDX identifier 2023-09-18 21:34:42 -07:00
Ryan Houdek 10081595af FEXGDBReader: Adds SPDX identifier 2023-09-18 21:34:22 -07:00
Ryan Houdek 75d53725e5 FEXConfig: Adds SPDX identifier 2023-09-18 21:33:02 -07:00
Ryan Houdek d21335be85 FEXBash: Adds SPDX identifier 2023-09-18 21:32:25 -07:00
Ryan Houdek e0385cd807 FEXRootFSFetcher: Adds SPDX identifier 2023-09-18 21:31:51 -07:00
Ryan Houdek e962462e79 FEXServer: Adds SPDX identifier 2023-09-18 21:31:18 -07:00
Ryan Houdek 5896c30954 CodeSizeValidation: Adds SPDX identifier 2023-09-18 21:30:30 -07:00
Ryan Houdek 745729cdc2 SoftFloat-3e: Adds preserve_all attribute to all functions used
This will let FEX's JIT be more optimal
2023-09-18 17:42:48 -07:00
Ryan Houdek 95e5d37e4c FEXCore: Adds compile time check support for preserve_all 2023-09-18 17:09:54 -07:00
Ryan Houdek 838293c2f0 FEXCore: Remove unused FallbackhandlerIndex LoadFCW
We removed this once passing in FCW explicitly.
2023-09-18 17:06:46 -07:00
Ryan Houdek da21fc937b FHU: Fixes syscall helper caching
check_cxx_source_compiles caches by variable name, so `compiles` was
getting cached and breaking future checks.
2023-09-18 17:05:40 -07:00
Alyssa Rosenzweig 3b188b7f49 Merge pull request #3118 from Sonicadvance1/spdx_fhu
FHU: Prepend SPDX identifier
2023-09-18 18:48:30 -04:00
Ryan Houdek 94bbd415a2 FHU: Prepend SPDX identifier
Added with `sed -i '1 i\\/\/ SPDX-License-Identifier: MIT' *.h`
2023-09-18 11:45:18 -07:00
Ryan Houdek 2ea2300408 Merge pull request #3110 from Sonicadvance1/buffered_jit_symbols
FEXCore/JitSymbols: Buffer writes to reduce overhead
2023-09-18 11:38:06 -07:00
Ryan Houdek 000fb2efae Merge pull request #3068 from neobrain/feature_thunk_testlib
unittests: Add test thunk library
2023-09-18 10:31:42 -07:00
Ryan Houdek 8b523082af Merge pull request #3116 from alyssarosenzweig/minor/flag-opts
Minor/flag opts
2023-09-18 10:28:51 -07:00
Alyssa Rosenzweig 5d2a3cd322 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-18 11:01:46 -04:00
Alyssa Rosenzweig df3833edbe OpcodeDispatcher: Use plain Lshl for flags
If we have PF but no CF this simplifies the IR.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-18 11:01:46 -04:00
Tony Wasserka 527b65648f unittests: Enable logging to stderr when invoking FEXLoader 2023-09-18 16:53:35 +02:00
Tony Wasserka bef64c53f8 unittests: Add test thunk library 2023-09-18 16:53:35 +02:00
Alyssa Rosenzweig 8edcd31404 OpcodeDispatcher: Avoid inverting PF
..if we can fold the invert into the reader.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-18 10:35:39 -04:00
Ryan Houdek fd1b639ad9 Merge pull request #3115 from lioncash/sqxtun
Arm64/VectorOps: Elide moves where applicable in 128-bit VSQXTUN2
2023-09-17 14:51:27 -07:00
Ryan Houdek 950a8dbfe7 Merge pull request #3114 from lioncash/ins
Arm64/VectorOps: Improve handling of 128-bit vector VInsElement
2023-09-17 14:40:38 -07:00
Lioncache 26e4d8ad59 Arm64/VectorOps: Elide moves where applicable in 128-bit VSQXTUN2
If the destination and lower data alias, we can
avoid needing to move into a temporary.
2023-09-17 17:37:36 -04:00
Lioncache d54f590b14 Arm64/VectorOps: Improve handling of 128-bit vector VInsElement
If none of the vectors alias the destination, then we can eliminate
an extra move and usage of a temporary.
2023-09-17 16:56:23 -04:00
Ryan Houdek b3269f20ef Merge pull request #3113 from lioncash/shrn
Arm64/VectorOps: Elide moves in ASIMD VUShrNI2 if possible
2023-09-17 13:03:42 -07:00
Lioncache 047646be6d Arm64/VectorOps: Elide moves in ASIMD VUShrNI2 if possible
In the event the destination and lower source are the same, then
we don't need to perform any moves.
2023-09-17 15:49:54 -04:00
Ryan Houdek 8168a49d10 Merge pull request #3112 from lioncash/assert
Arm64/VectorOps: Assert VTMP1 and VTMP2 are sequential in VTBL2
2023-09-17 12:40:47 -07:00
Lioncache 7f2fd4e9a0 Arm64/VectorOps: Assert VTMP1 and VTMP2 are sequential in VTBL2
Ensures that if our temp vectors change in the future that this is
caught at compile-time rather than runtime.
2023-09-17 15:22:39 -04:00
Lioncache 4ea9f08425 ARMEmitter: Mark index and conversion ops as constexpr
Will be used for assertions. Also makes registers more flexible
for compile-time stuff in general.
2023-09-17 15:19:44 -04:00
Ryan Houdek ffb58761c1 Merge pull request #3111 from lioncash/shift
Arm64/VectorOps: Fix SVE aliasing-path  move in VSShr
2023-09-17 12:15:49 -07:00
Lioncache 8ecdb341e2 Arm64/VectorOps: Fix SVE aliasing-path move in VSShr
Seems like this was a typo from 8d11073, since we'd be moving
into a temporary and then never use it.
2023-09-17 14:43:05 -04:00
Ryan Houdek 0c5c146fcf FEXCore/JitSymbols: Buffer writes to reduce overhead
While this interface is usually pretty fast because it is a write and
forget operation, this has issues when there are multiple threads
hitting the perf map file at the same time. In particular this interface
becomes a bottleneck due to a locking mutex on writes in the kernel.

The situations when this bottleneck occurs is when a bunch of threads
get spawned and they are all jitting code as quickly as possible. In
particular Geekbench's clang benchmark hits this hard where each CPU
thread spends ~40% CPU time on all eight CPU threads because they are
stalled waiting for this mutex to unlock.

To work around this issue, buffer the writes a small amount. Either up
to a page-ish of data or 100ms of time. This completely eliminates
threads waiting on the kernel mutex.
- Around a page of buffer space was chosen by profiling Geekbench's
  clang benchmark and seeing how frequently it was still writing.
   - 1024 bytes was still fairly aggressive, 4096 seemed fine.
- 100ms was chosen to ensure we don't wait /too/ long to write JIT
  symbols.
   - In most cases 100ms is enough that you won't notice the blip in
     perf.

One thing of note is that with profiling enabled and checking the time
on every JIT block still ends up with 2-3% CPUtime in vdso
clock_gettime. We can improve this by using the cyclecounter directly
since that is still guaranteed to be monotonic. Maybe we'll come back to
that if it is actually an issue here.
2023-09-16 17:52:46 -07:00
Ryan Houdek ad8b0c673f Merge pull request #3109 from lioncash/shlx
OpcodeDispatcher: Improve output of SHLX/SHRX/SARX
2023-09-15 18:49:36 -07:00
Ryan Houdek e574cfe681 Merge pull request #3108 from lioncash/mulx
OpcodeDispatcher: Improve output of MULX
2023-09-15 18:09:02 -07:00
Lioncache e9be291cec OpcodeDispatcher: Improve output of SHLX/SHRX/SARX
We can remove some unnecessary moves for the 32-bit cases and
collapse the operations down to a single instruction.
2023-09-15 21:05:50 -04:00
Lioncache d4f87c7db1 OpcodeDispatcher: Improve output of MULX
We can cut down on a few of the generated moves. For
the case where both destinations alias one another,
we can just calculate the high part instead of both of them.
2023-09-15 20:52:02 -04:00
Ryan Houdek 4604c01986 Merge pull request #3107 from lioncash/pext
Arm64/ALUOps: Remove spills in PEXT
2023-09-15 17:40:44 -07:00
Lioncache b0c8ff0ea6 Arm64/ALUOps: Remove spills in PEXT
Reduces the number of emitted instructions for a
corresponding PEXT instruction.

We no longer spill for this IR op.
2023-09-15 19:39:51 -04:00
Ryan Houdek 647629ac23 Merge pull request #3105 from lioncash/rorx
OpcodeDispatcher: Handle RORX corner cases better
2023-09-15 14:55:17 -07:00
Lioncache be90e76422 Arm64/ALUOps: mov in the case of full 32-bit/64-bit BFE
Allows register-renaming mechanisms to be invoked more frequently
2023-09-15 17:38:01 -04:00
Lioncache 4a37ea4819 OpcodeDispatcher: Handle RORX corner cases better
There are a few cases where we were emitting code when we
didn't really need to, or could emit less.
2023-09-15 17:36:36 -04:00
Ryan Houdek 6e08ac65b9 Merge pull request #3106 from lioncash/clwb
HostFeatures: Fix x86 CLWB support check
2023-09-15 14:06:54 -07:00
Lioncache 8705de1893 HostFeatures: Fix x86 CLWB support check
This was clobbering the BMI2 boolean unintentionally.
2023-09-15 16:38:45 -04:00
Alyssa Rosenzweig c8e7c347c3 Merge pull request #3100 from Sonicadvance1/optimize_cmov
OpcodeDispatcher: Optimize cmov
2023-09-15 15:17:51 -04:00
Ryan Houdek 3d0b66407e Merge pull request #3104 from lioncash/vperm2
InstCountCI/VEX_map3: Add missing zeroing vperm2f128/vperm2i128 test cases
2023-09-15 11:27:11 -07:00
Lioncache c86b6dc690 InstCountCI/VEX_map3: Add missing zeroing vperm2f128/vperm2i128 test cases
Allows viewing the codegen for cases where conditional zeroing is performed.

Also fixes up the vperm2f variants shorthanding one of the registers
to make everything a little more explicit.
2023-09-15 14:10:11 -04:00
Ryan Houdek 773e9465bc Merge pull request #3103 from lioncash/warn
DeadContextStoreElimination: Silence unused function warning
2023-09-15 10:57:40 -07:00
Lioncache e1ed7f43fd DeadContextStoreElimination: Turn LastAccessType into an enum class
Makes the type stricter in terms of implicit conversions.
2023-09-15 13:23:00 -04:00
Ryan Houdek 3eb501aa27 InstCountCI: Update for optimized NZCV and cmov 2023-09-15 10:11:50 -07:00
Ryan Houdek d5b58eebaf OpcodeDispatcher: Optimize cmov
cmov was quite terrible in its implementation. Some things of note:
- NZCV cache would cause store for no reason
- {16,32}-bit would zero extend sources for no reason
- 16-bit would zero extend result for no reason

A bunch of flag testing is still doing a ubfx plus compare against zero
when it could end up being a tst instead, but this is a step in the
right direction and switches over to explicit sized selects.
2023-09-15 10:09:37 -07:00
Ryan Houdek 6dbbd9ecfc OpcodeDispatcher: Duplicate SelectCC but with Explicit result size
This is a temporary measure as we are moving Select operations over to
explicit sizes. Once we remove all uses of SelectCC then it will get
removed.
2023-09-15 10:09:37 -07:00
Ryan Houdek 759cc0025a OpcodeDispatcher: Add a dirty flag for tracking NZCV status
Cached NZCV reads don't need to be written back at the end of the block.
This will remove one instruction from the end of some blocks.
2023-09-15 10:09:37 -07:00
Lioncache d05f890147 DeadContextStoreElimination: Silence unused function warning 2023-09-15 13:08:32 -04:00
Alyssa Rosenzweig 9152fb030e Merge pull request #3102 from alyssarosenzweig/inline-xor
Inline constant with PF calculation
2023-09-15 12:48:33 -04:00
Alyssa Rosenzweig 2385c275ac InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-15 12:33:53 -04:00
Alyssa Rosenzweig d29b8bab36 OpcodeDispatcher: Inline constant in PF calculation
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-15 12:33:53 -04:00
Ryan Houdek b6922dff57 Merge pull request #3101 from alyssarosenzweig/opt/dec
Optimize out carry invert for DEC
2023-09-15 09:30:08 -07:00
Alyssa Rosenzweig c560a88de4 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-15 12:10:57 -04:00
Alyssa Rosenzweig fc02f38435 IR: Only invert CF for NZCV if needed
If we are going to throw away the updated value of CF anyway there is no point
wasting an instruction to invert CF. Add an IR toggle for that so the arm64 JIT
can make better choices.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-15 12:08:22 -04:00
Ryan Houdek d5782567e8 Merge pull request #3077 from Sonicadvance1/x86_shifted
FEXCore: Implements support for shifted bitwise ops
2023-09-15 08:09:35 -07:00
Ryan Houdek 060433621a Merge pull request #3097 from Sonicadvance1/disable_enhanced_tso
FEXCore: Disable Enhanced REP MOVSB if Atomic TSO is enabled
2023-09-15 08:08:36 -07:00
Ryan Houdek 9866e238d5 Merge pull request #3080 from Sonicadvance1/defer_softfloat
FEXCore: Defer setting x87 softflow rounding mode until use
2023-09-15 08:08:04 -07:00
Mai f5c4e28696 Merge pull request #3098 from Sonicadvance1/optimize_vectors_sve
Arm64: Optimize wide shifts slightly for 64-bit OpSize
2023-09-15 05:21:20 -04:00
Mai 96bbd01ad6 Merge pull request #3096 from Sonicadvance1/optimal_crc
OpcodeDispatcher: Optimize CRC32
2023-09-15 05:19:05 -04:00
Mai f84a264b0e Merge pull request #3095 from Sonicadvance1/bswap
OpcodeDispatcher: Optimize 16-bit MOVBE
2023-09-15 05:18:26 -04:00
Mai a8c17201b5 Merge pull request #3099 from Sonicadvance1/explicit_but_implicit_select
IR: Changes Select operation to not have implicit sizes
2023-09-15 05:17:47 -04:00
Ryan Houdek d81d89c4fb IR: Changes Select operation to not have implicit sizes
Changes the helper which all the source uses to still calculate the size
implicitly. This is going to take a while to convert all implicit uses
over to the explicit operation.

Get us started by at least having the IR operation itself be explicit.
2023-09-14 20:48:16 -07:00
Ryan Houdek 92212c48f1 IR: Fixes parsing of default arguments with colons
We need to split on the first colon, not every colon in the arguments.

This will be used in the next changes.
2023-09-14 20:37:45 -07:00
Ryan Houdek 42a24bbbd1 InstCountCI: Update CI for optimized wide shifts 2023-09-14 19:53:18 -07:00
Ryan Houdek 021c99e233 Arm64: Optimize wide shifts slightly for 64-bit OpSize
Wide shifts under SVE use 64-bit source elements. If a smaller element
overlaps the 64-bit shift element then it uses that shift
eg:
- Src1[15:0] >> Shift[63:0]
- Src1[31:16] >> Shift[63:0]
- Src1[47:32] >> Shift[63:0]
- Src1[63:48] >> Shift[63:0]
- After this point it will switch to the next 64-bit shift element
- Src1[79:64] >> Shift[127:64]
- Src1[95:80] >> Shift[127:64]
- Src1[111:96] >> Shift[127:64]
- Src1[127:112] >> Shift[127:64]

As seen, we can skip the duplication of the scalar element if the OpSize
is 64-bit, this makes MMX emulation slightly more optimal here.
This also means that a few instructions that weren't claimed to be
optimal actually are since they need the duplication operation (which
vixl always labels as a mov).
2023-09-14 19:35:56 -07:00
Ryan Houdek f730339365 OpcodeDispatcher: Reorder vector loads in shifts
This affects codegen due to RA quirks. This ensures that the wide shifts
don't have to generate a movprfx.
2023-09-14 19:34:00 -07:00
Ryan Houdek 40a4eb90af FEXCore: Disable Enhanced REP MOVSB if TSO is enabled
Hades and the vcruntime hits this very hard in memmove.

`86.56%  [JIT] tid 458574        [.] JIT_0x18000c375_0x7fffc94790c8`

```asm
   0x00007fffc94790f8:  ldaprb  w3, [x2]
   0x00007fffc94790fc:  stlrb   w3, [x1]
   0x00007fffc9479100:  add     x1, x1, #0x1
   0x00007fffc9479104:  add     x2, x2, #0x1
   0x00007fffc9479108:  sub     x0, x0, #0x1
   0x00007fffc947910c:  cbnz    x0, 0x7fffc94790f8
```

This performance is terrible because Cortex's LRCPC performance is bottom-tier.
Work around the performance issue by forcing things to do larger moves with vector moves instead.
2023-09-14 17:23:02 -07:00
Ryan Houdek 31ad26202e InstCountCI: Update for Optimized CRC32 2023-09-14 16:23:33 -07:00
Ryan Houdek a3115d4699 OpcodeDispatcher: Optimize CRC32
The only version of this instruction that was generating optimal code
was the one with 64-bit destination and source.

Optimizes the rest of the operating sizes so that they are all optimal
at one instruction translations
2023-09-14 16:21:42 -07:00
Ryan Houdek c3ead80927 InstCountCI: Update for optimized 16-bit movbe 2023-09-14 16:05:38 -07:00
Ryan Houdek 80cda1bb18 OpcodeDispatcher: Optimize 16-bit MOVBE
16-bit MOVBE is a bit of a special case where it loads 16-bits in to the
bottom of the GPR without clearing the upper bits of the register.
Which means 32-bits or 64-bits depending on operating mode.

Arm64 doesn't support a 16-bit bswap so it needs to operate at 32-bits
instead. We then can insert the resulting bits of the 32-bit rev with a
bfxil in to the lower bits of the resulting destination register.

This allows 16-bit movbe to be optimal now.
2023-09-14 16:05:16 -07:00
Ryan Houdek 90ddee5f8d IR: Implements support for arm64 bfxil
This is useful for extracting a width from a register and inserting in
to the lower bits of a destination.
2023-09-14 15:52:55 -07:00
Ryan Houdek 6fdf2f963b Merge pull request #3082 from Sonicadvance1/minor_storeregsra_opt
FEXCore: Minor optimization to StoreRegisterSRA
2023-09-14 14:54:39 -07:00
Ryan Houdek e1eb151051 Merge pull request #3094 from neobrain/refactor_reorder_ci
CI: Run tests with <30s runtime first
2023-09-14 13:56:12 -07:00
Tony Wasserka 3f8bf01f75 CI: Run tests with <30s runtime first 2023-09-14 20:46:50 +02:00
Mai 92824f5e4d Merge pull request #3093 from Sonicadvance1/optimize_blendp
OpcodeDispatcher: Optimize blendp{s,d}
2023-09-14 00:33:00 -04:00
Mai 213d3c4e2b Merge pull request #3091 from Sonicadvance1/optimize_pinsr
OpcodeDispatcher: Optimize pins{b,w,d,q}
2023-09-14 00:30:56 -04:00
Mai d4c6749d2a Merge pull request #3090 from Sonicadvance1/optimize_pextr
OpcodeDispatcher: Optimize pextr{b,w}
2023-09-14 00:30:43 -04:00
Mai 1804b007ec Merge pull request #3092 from Sonicadvance1/instcountci_compile_log
InstCountCI: Add log before compiling instruction
2023-09-13 23:14:42 -04:00
Mai 655cee070d Merge pull request #3089 from Sonicadvance1/optimize_pshufd
OpcodeDispatcher: Optimize shufpd
2023-09-13 23:12:10 -04:00
Ryan Houdek 28309a1cc5 InstCountCI: Update for blend 2023-09-13 20:08:03 -07:00
Ryan Houdek 29f824cf7a OpcodeDispatcher: Optimize blendp{s,d}
Optimal blendps is worst case 2 instructions.
FEX's RA doesn't quite get there since it can't see through multiple
instructions with SRA destinations. That'll be fixed in the future.

Optimal blendps is always one instruction, one is a no-op.
We always hit this.
2023-09-13 20:06:39 -07:00
Ryan Houdek 2bafa2c26f InstCountCI: Update for optimized pins{b,w,d,q} 2023-09-13 19:53:05 -07:00
Ryan Houdek 5e7d793a6a OpcodeDispatcher: Optimize pins{b,w,d,q}
Inserting from a GPR and memory can both be optimized. These are now
optimal

Needs #3088 merged first.
2023-09-13 19:53:05 -07:00
Ryan Houdek 3e40713ccc InstCountCI: Update for optimized pextr{b,w} 2023-09-13 19:51:56 -07:00
Ryan Houdek 33a2fbb896 OpcodeDispatcher: Optimize pextr{b,w}
Cleans up the code which had special cased some 32-bit optimization
which is unnecessary now that both 8-bit and 16-bit are also optimized.

When FEX does a VExtractToGPR, the result is zero extended to the full
GPR register size. This means we don't need to do a zero extend when
storing to a guest GPR.

Makes pextr{b,w} optimal now.

Needs #3088 merged first.
2023-09-13 19:51:56 -07:00
Ryan Houdek 853ded7df7 InstcountCI: Update for optimized shufpd 2023-09-13 19:50:15 -07:00
Ryan Houdek 67914157cb OpcodeDispatcher: Optimize shufpd
This one is very satisfying since there are only four variants and each
one of them converts to a single instruction.

Needs #3088 merged first
2023-09-13 19:50:15 -07:00
Mai 750d90939d Merge pull request #3088 from Sonicadvance1/instcountci_missing_secondary_opsize
InstCountCI: Adds missing instructions from Secondary OpSize tables
2023-09-13 22:49:17 -04:00
Mai 31d828390f Merge pull request #3087 from Sonicadvance1/tbl2_implementation
OpcodeDispatcher: Implement shufps with VTBL2 in worst case
2023-09-13 22:48:53 -04:00
Ryan Houdek 6c2f8ab085 InstCountCI: Add log before compiling instruction
If CI faults out due to a bug then we would have no log as to which
instruction caused the issue.

I find myself adding this each time an assert fires to see what
instruction it was working on. Just add it directly.
2023-09-13 14:33:33 -07:00
Ryan Houdek 2aea401189 InstCountCI: Adds missing instructions from Secondary OpSize tables
I managed to miss a whole section of instructions from the secondary
opsize tables. This resulted in four instructions missing from the
database.

Adds cmppd, pinsrw, pextrw, and shufpd which are all non-optimal
instruction implementations.
2023-09-13 11:48:50 -07:00
Ryan Houdek c008671509 unittests/asm: Add test with inverted sources
To ensure this is tested with non sequential source registers.
2023-09-13 11:31:20 -07:00
Ryan Houdek 5903be156c InstCountCI: Update for shufps tbl opt 2023-09-13 11:31:20 -07:00
Ryan Houdek db5056f275 OpcodeDispatcher: Implement shufps with VTBL2 in worst case
In the case that source registers are sequential then this turns in to a
load of the vector constant (2 instructions) and the single tbl
instruction.

If the registers aren't sequential then the tbl turns in to 2 moves and
then the single tbl, which with zero-cycle rename isn't too bad.

Since this is a worst case option this is significantly better than the
previous implementation doing a bunch of inserts which was always 9
instructions.
We should still strive to implement faster versions without the use of
TBL2 if possible but this makes it less of a concern.
2023-09-13 11:31:20 -07:00
Ryan Houdek e9d96ce538 IR: Implements support for VTBL2
Skips implementing it for the x86 JIT because that's a bit of a
nightmare to think about.

The ARM64 implementation requires sequential registers which means if
the incoming sources aren't sequential then we need to move the sources
in to the two vector temporaries. This is fine since we have zero-cycle
vector renames and the alternative is slower.
2023-09-13 11:31:20 -07:00
Ryan Houdek 444d4c082d Int: Fixes typo in LoadNamedVectorIndexedConstant
Surprising this didn't break anything before this.
2023-09-13 11:31:20 -07:00
Ryan Houdek cfe620ab15 Merge pull request #3085 from Sonicadvance1/optimize_shufps
OpcodeDispatcher: Optimize a bunch of shufps variants
2023-09-12 21:53:33 -07:00
Ryan Houdek ea8d63350a InstCountCI: Updates for optimized shufps 2023-09-12 19:58:07 -07:00
Ryan Houdek e37cef8283 unittests: Implement shufps optimization test
Tests all current forms of shufps optimizations.
2023-09-12 19:58:07 -07:00
Ryan Houdek 3f1979286f OpcodeDispatcher: Optimize a bunch of shufps variants
Hits a whole bunch of common cases, most of which then emit optimal code
generation.
Two cases that use VInsElement hit the RA quirk where the SRA
destination is dead but RA doesn't see it, so it ends up doing a couple
moves. If RA gets fixed then those two moves will go away.

There are definitely still cases that we could emit more optimal code.
Additionally we could implement a TBL2 IR operation to do a LUT approach
for ones we don't cover.

Problem with implementing a TBL2 ir operation is that we have no way to
ensure registers are sequential so we would need to always do moves
```asm
ldr v2, <LUT Table>
mov v0, v16
mov v1, v18
tbl v16.16b, { v0.16b, v1.16b }, v2.16b
```

Which to be fair isn't terrible, and if we're lucky that the guest uses
sequential registers we can naturally get the more optimal code path.
Ideally our RA could push some operations in to sequential registers but
that's not possible currently.

I'll do a follow-up PR that implements TBL2.
2023-09-12 19:58:07 -07:00
Ryan Houdek d5c3036bc2 JITx86: Fixes VREV64 with 32-bit element size.
This has been incorrect since it has been implemented.
Noticed when implementing optimizations.
2023-09-12 19:23:59 -07:00
Mai ebdca02218 Merge pull request #3084 from Sonicadvance1/optimize_bswap
OpcodeDispatcher: Optimize 32-bit bswap
2023-09-12 20:09:00 -04:00
Mai dda5861bdd Merge pull request #3081 from Sonicadvance1/fix_waitpid
Tools: Fixes usage of waitpid in the face of EINTR
2023-09-12 19:35:05 -04:00
Mai f7e652b616 Merge pull request #3083 from Sonicadvance1/optimize_nop_move
OpcodeDispatcher: Optimize NOP vector move
2023-09-12 19:34:36 -04:00
Ryan Houdek 65bc159ff1 InstCountCI: Update for bswap optimization 2023-09-12 16:19:55 -07:00
Ryan Houdek c362d3a9d8 OpcodeDispatcher: Optimize 32-bit bswap
Removes a redundant move, making it optimal now.
2023-09-12 16:19:10 -07:00
Ryan Houdek 8a44be0c30 InstCountCI: Update for NOP vector moves
Adds a couple of instructions that get tested in this code path.
2023-09-12 16:11:43 -07:00
Ryan Houdek 304dba5f20 OpcodeDispatcher: Optimize NOP vector move
Move instruction to itself here is a nop.
Need to be careful about AVX operations which use a different handler
since those might actually zero the upper bits on 128-bit move
2023-09-12 16:10:39 -07:00
Ryan Houdek b2e61d2deb InstCountCI: Update for minor storeregistersra opt 2023-09-12 14:38:46 -07:00
Ryan Houdek e6c0bebee9 FEXCore: Minor optimization to StoreRegisterSRA
{Load,Store}RegisterSRA always loads or stores GPRSize. 8-bit and 16-bit
are vestigial and all OpcodeDispatcher usage will load the full GPR size
(32-bit or 64-bit) and then extract or insert as necessary.

This cleans up a few bits of codegen in InstCountCI.
2023-09-12 14:36:08 -07:00
Ryan Houdek aa017116b3 Tools: Fixes usage of waitpid in the face of EINTR
waitpid can return early if interrupted due to EINTR.
Loop on this case and try again.
2023-09-12 12:41:43 -07:00
Ryan Houdek 97a6184e53 InstCountCI: Update for FCW optimization 2023-09-12 05:21:06 -07:00
Ryan Houdek 76bd81af15 FEXCore: Defer setting x87 softflow rounding mode until use
Currently FEX will always jump out of the JIT any time FCW was getting
written to, ensuring that the softfloat state is setup to rounding at
the time of FCW getting written.
This has the unintended side-effect that even in "x87 reduced precision"
mode we were jumping out of the JIT.
This hit a real world use case of an installer reloading FCW after every
x87 operation and generating a block with 2297 instructions.

Instead when jumping out of the JIT for handling x87 operations, load
FCW and pass it as the first argument of the handler. Setting the
softfloat state at that point.

This helps the installer's hottest block by cutting it down to 1477
instructions. 64.3% of the original size. The code block is still
burning 90% of the CPU time of the installer but the performance is
significantly better while it is doing its decompression.

In order to optimize this installer's block of code more then we will
likely need to optimize out x87 stack usage.
2023-09-12 05:21:06 -07:00
Mai 90f7937146 Merge pull request #3079 from Sonicadvance1/recover_two_temps
Arm64: Recover two unused vector vector temporary registers
2023-09-11 22:06:03 -04:00
Mai 98f148766d Merge pull request #3078 from Sonicadvance1/detect_flagm
HostFeatures: Detect FlagM/2
2023-09-11 20:57:43 -04:00
Ryan Houdek 9c44e295fa InstCountCI: Update for recovering two vector temps
All the changes are RA changes and spilling/filling taking another
instruction.
2023-09-11 16:50:52 -07:00
Ryan Houdek b5a1d323c2 Arm64: Recover two unused vector vector temporary registers
This leaves us with two temporary vectors that the JIT can use.
As of last month we stopped using v2 and v3 as temporaries and these can
now be given back to the JIT.

Ensures that the registers are still sequentially ordered and adds
support for spilling the FPR counts that are aligned by 2 instead of 4.
Adds a couple of instructions to filling and spilling but isn't that big
of an issue.

InstcountCI has some ridiculously large changes just because RA is
starting at a new register number.
2023-09-11 16:48:25 -07:00
Ryan Houdek b453439968 HostFeatures: Detect FlagM/2
Currently unused but at least detect the feature so that our Arm64 JIT
can use it in the future.
2023-09-11 16:41:30 -07:00
Ryan Houdek 863331b117 FEXCore: Implements support for shifted bitwise ops
This wasn't implemented initially for the interpreter and x86 JIT.

This meant we are maintaining two codepaths. Implement these operations
in the interpreter and x86 JIT so we no longer need to do that.

The emitted code in the x86 JIT is hot garbage, but it's only necessary
for correctness testing, not performance testing there.
2023-09-11 13:17:35 -07:00
Mai 48521a4416 Merge pull request #3075 from Sonicadvance1/optimize_bt_ops
OpcodeDispatcher: Minor optimization to BT/BTC/BTR/BTS
2023-09-11 16:05:33 -04:00
Mai 6fe643d270 Merge pull request #3076 from Sonicadvance1/enable_enhanced_rep_movs
CPUID: Enabled Enhanced REP MOVSB/STOSB
2023-09-11 15:35:56 -04:00
Mai fbc4bda7a6 Merge pull request #3074 from Sonicadvance1/hwcap2_fsgsbase
ELFCodeLoader: Expose FSGSBase in getauxval HWCAP2
2023-09-11 15:35:26 -04:00
Mai 6d9b52452e Merge pull request #3072 from Sonicadvance1/crc32_is_fixed_size
IR: Changes crc32 operation to always return a 32-bit result.
2023-09-11 15:34:37 -04:00
Mai 950007c815 Merge pull request #3071 from Sonicadvance1/update_rcl_opsize
OpcodeDispatcher: Update 32/64-bit RCL for operating size
2023-09-11 15:34:06 -04:00
Mai d029394c27 Merge pull request #3070 from Sonicadvance1/update_rcr_opsize
OpcodeDispatcher: Update 32/64-bit RCR for operating size
2023-09-11 15:33:34 -04:00
Mai 879fcdc6fe Merge pull request #3069 from Sonicadvance1/fix_redundant_load_rclse
IR:RCLSE: Partially reenables the RCLSE pass
2023-09-11 15:32:55 -04:00
Ryan Houdek 2f77982b54 CPUID: Enabled Enhanced REP MOVSB/STOSB
Missed with #2490.
This changes behaviour of glibc's memmove slightly, seems to recover a
bit of performance on Half-Life 2's title screen.
2023-09-10 20:49:08 -07:00
Ryan Houdek e3a00fb2fb InstCountCI: Update for BT minor opt 2023-09-10 20:23:08 -07:00
Ryan Houdek 4feb059f51 OpcodeDispatcher: Optimize the case of all flags invalidated
When flags are invalidated but we're going to insert a new flag we end
up in a situation where we loaded the prior value from memory, claimed
unknown cache status (they were all invalid!), and then did an insert.
2023-09-10 20:16:29 -07:00
Ryan Houdek 3d1bbe505d OpcodeDispatcher: Minor optimization to BT/BTC/BTR/BTS
These instructions set all the flags to undefined and moves the
resulting bit in to CF. No need to calculate the deferred flags when
we are about to write over them.
2023-09-10 20:16:29 -07:00
Ryan Houdek b2a42b6c61 ELFCodeLoader: Expose FSGSBase in getauxval HWCAP2
We have supported this since #163 but we haven't been exposing the
feature in hwcap2.

We have exposed it in CPUID this entire time, just not in hwcap2.
2023-09-10 17:00:21 -07:00
Ryan Houdek 315d1855de IR: Changes crc32 operation to always return a 32-bit result.
CRC32 is always a 32-bit sized operation even with a 64-bit source
value.
This doesn't change any InstCountCI results.
2023-09-09 10:02:30 -07:00
Ryan Houdek 93246878e2 InstCountCI: Update for rcl explicit size change 2023-09-09 09:40:36 -07:00
Ryan Houdek 6c62691af0 OpcodeDispatcher: Update 32/64-bit RCL for operating size
Removes todo from explicit size PR. Saves one instruction.
2023-09-09 09:40:12 -07:00
Ryan Houdek ee5aed51d8 InstCountCI: Update for rcr expliti size change 2023-09-09 09:35:13 -07:00
Ryan Houdek 47f50a7008 OpcodeDispatcher: Update 32/64-bit RCR for operating size
Removes todo from explicit size PR. Saves one instruction.
2023-09-09 09:33:27 -07:00
Ryan Houdek be07254935 Merge pull request #3067 from neobrain/refactor_thunks
Thunks: Minor restructuring and small cleanups
2023-09-07 20:16:17 -07:00
Ryan Houdek 636f8aa4a7 Arm64: Fix undefined behaviour in Push operation
Arm64 store with writeback when source register is the same register as
the address is undefined behaviour.
Depending on hardware details this can do a whole bunch of things.

This situation happens when the x86 code does `push rsp` which is quite
common for applications to do. We would then convert this to a `str x8, [x8, #-8]!`
Which results in undefined behaviour.

Now that redundant loads are optimized this showed up as an issue. Adds
a unit test to ensure we don't hit this again.
2023-09-07 17:38:39 -07:00
Ryan Houdek 22ca46a227 Arm64: Fixes SVE V{S,U}MulH
When the destination overlaps one of the sources we must be careful to
follow a movprfx rule.
```
The destination register must not refer to architectural register state
referenced by any other source operand register of this instruction.
```

We ended up in a situation in the vpmulh{u,}w AVX tests where zm was
overlapping the destination which violated that rule. This also
generated invalid code for this instruction.
```
[INFO] movprfx z6, z4
[INFO] umulh z6.h, p6/m, z6.h, z6.h
```

As seen, we were overwriting one of the sources because the destination
overlapped it. Now instead check if each individual overlap so invalid
code isn't generated.

InstCountCI results aren't affected since this only happens in
situations with multiple instructions.
2023-09-07 16:47:08 -07:00
Ryan Houdek 7b80427de0 OpcodeDispatcher: Remove BLENDV "optimization"
Now that the RCLSE pass finally optimizes redundant loads again this
optimization that lives in the OpcodeDispatcher can be removed.

With InstCountCI reran, the pblendvb results don't change at all, as
expected.
2023-09-07 16:00:56 -07:00
Ryan Houdek b753b9ffa2 InstCountCI: Updates for RCLSE fix
Adds two `packsswb` tests to ensure redundant sources are getting
optimized as expected.
2023-09-07 15:58:43 -07:00
Ryan Houdek c62b5a3103 IR:RCLSE: Partially reenables the RCLSE pass
This is taking steps to start fixing RCLSE which was started by #2700.
Same situation as that PR, since #2170 when we converted
{Load,Store}Context in to {Load,Store}Register we broke this pass
entirely. It hasn't been doing anything for redundant GPRs and FPRs
since at least November of last year.

Technically it was potentially still optimizing redundant MMX
accesses, but it is so broken that it doesn't matter.

Instead of going all in like #2700 did, tear down the pass and start
again. We are now /only/ optimizing redundant context/register loads.
This fixes an issue that comes up commonly where the same register used
as sources was getting loaded twice, causing redundant moves.

`packsswb xmm0, xmm0` for example was generating a four instruction
sequence instead of three instructions because we weren't eliminating
the redundant load.

Going to take reimplementing all the optimizations that this pass does
in steps. This way we can track any regression in the independent steps
unlike what happened in #2700.

Confirmed that Proton/Sonic Mania still works after this.
2023-09-07 15:50:27 -07:00
Ryan Houdek 3c729bcacb IR/RCLSE: Removes unused CalculateControlFlowInfo
This is unused and this only optimizes inside of a block.
2023-09-07 15:49:29 -07:00
Tony Wasserka 677b77f1bb Thunks: Simplify PackedArguments invocation code 2023-09-07 13:56:53 +02:00
Tony Wasserka 024fb268c0 unittests/ThunkLibs: Move utility code to a dedicated header 2023-09-07 13:56:53 +02:00
Tony Wasserka ab8bc052a0 Thunks/gen: Split interface parsing and code emission into separate files 2023-09-07 13:56:53 +02:00
Tony Wasserka 971821460c Thunks/gen: Move diagnostic marshaling helper to a dedicated header 2023-09-07 13:56:53 +02:00
Ryan Houdek 615ab8d80c Merge pull request #3066 from neobrain/fix_procfsint_regression
FileManagement: Fix inverted boolean check for procfs/interpreter support
2023-09-06 17:37:12 -07:00
Tony Wasserka 83c74e86c8 FileManagement: Fix inverted boolean check for procfs/interpreter support 2023-09-06 17:00:58 +02:00
Mai 9ff5544d55 Merge pull request #3065 from Sonicadvance1/fix_docs_location
Scripts: Update generate_doc_outline for moved FEXCore
2023-09-06 10:54:20 -04:00
Ryan Houdek 2b9265d9fe Scripts: Update generate_doc_outline for moved FEXCore
Otherwise all of the FEXCore docs get deleted.
2023-09-05 23:23:27 -07:00
Ryan Houdek bf66cac272 Docs: Update for release FEX-2309 2023-09-05 22:16:35 -07:00
Ryan Houdek 016c3c0f07 Merge pull request #3057 from Sonicadvance1/support_procfs_interpreter
FEXInterpreter: Supports procfs/interpreter
2023-09-05 21:53:45 -07:00
Ryan Houdek 09a49a3420 FEXInterpreter: Supports procfs/interpreter
This is a new procfs symlink path that changes behaviour of binfmt_misc
when exposed. We need to check both procfs/exe and procfs/interpreter
and see if they exist AND also differ.

Once/if they do then we can disable a bunch of checking of paths once
they do. The fallback when none of this is supported has the same
behaviour has previously where it still does all the regular checking.

During binfmt_misc install cmake will check the kernel version for the
raw binfmt_misc writing. Which will never pass until we have a real
kernel version that it is upstreamed in.

For update-binfmts we add a new optional argument where the tool will
drop the flag if the host kernel version isn't new enough to handle the
option.
2023-09-05 21:30:47 -07:00
Ryan Houdek 16f826c18e Merge pull request #3064 from alyssarosenzweig/ir/rm-phi
IR: Remove phi nodes
2023-09-05 19:43:52 -07:00
Alyssa Rosenzweig e6db2d0b96 IR: Remove phi nodes
It turns out that pure SSA isn't a great choice for the sort of emulation we do.
On one hand, it discards information from the guest binary's register allocation
that would let us skip stuff. On the other hand, it doesn't have nearly as many
benefits in this setting as in a traditional compiler... We really *don't* want
to do global RA or really any global optimization. We assume the guest optimizer
did its job for x86, we just need to clean up the mess left from going x86 ->
arm. So we just need enough SSA to peephole optimize.

My concrete IR proposals are that:

  * SSA values must be killed in the same block that they are defined.
  * Explicit LoadGPR/StoreGPR instructions can be used for global persistence.
  * LoadGPR/StoreGPR are eliminated in favour of SSA within a block.

This has a lot of nice properties for our setting:

  * Except for some internal REP instruction emulation (etc), we already have
    registers for everything that escapes block boundaries, so this form is very
    easy to go into -- straightforward local value numbering, not a full into
    SSA pass.

  * Spilling is entirely local (if it happens at all), since everything is in
    registers at block boundaries. This is excellent, because Belady's algorithm
    lets us spill nearly optimally in linear-time for individual blocks. (And
    the global version of Belady's algorithm is massively more complicated...)
    A nice fit for a JIT.

    Relatedly, it turns out allowing spilling is probably a decent decision,
    since the same spiller code can be used to rematerialize constants in a
    straightforward way. This is an issue with the current RA.

  * Register assignment is entirely local. For the same reason, we can assign
    registers "optimally" in linear time & memory (e.g. with linear scan). And
    the impl is massively simpler than a full blown SSA-based tree scan RA. For
    example, we don't have to worry about parallel copies or coalescing phis or
    anything. Massively nicer algorithm to deal with.

  * SSA value names can be block local which makes the validation implicit :~)

It also has remarkably few drawbacks, because we didn't want to do CFG global
optimization anyway given our time budget and the diminishng returns. The few
global optimizations we might want (flag escape analysis?) don't necessarily
benefit from pure SSA anyway.

Anyway, we explicitly don't want phi nodes in any of this. They're currently
unused. Let's just remove them so nobody gets the bright idea of changing that.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 16:35:12 -04:00
Ryan Houdek 04228952fa Merge pull request #3063 from alyssarosenzweig/flag/defer-pf-completely
Defer PF calculation completely
2023-09-05 13:18:22 -07:00
Alyssa Rosenzweig 5a3dc8c2ab InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 16:00:10 -04:00
Alyssa Rosenzweig 8efe2eeef6 OpcodeDispatcher: Defer PF invert
Now the calculation of PF is entirely deferred, by inverting our internal
representation of PF. All the (e.g.) logical op needs to do is store the low
8-bits of the result.

This is a bit of a mixed bag. Primary ALU ops all save an instruction, by
skip the XOR. Loading PF takes an extra instruction, that's expected. The tricky
cases are:

* Zeroing PF. This now requires writing 1 instead of 0, which may require an
  extra move for the constant. Some of this will go away when we merge PF+AF
  into a single register, which is next up on the list. In that case, the
  two stores will turn into 1 `or`. So if we need to write a 1 to PF (zeroing
  x86 view of PF), that will get absorbed into the or, if we also write AF. If
  we leave AF undefined and need to write a 1, that's a single mov instruction
  and we couldn't do better anyway if not inverted (since we'd still have a mov
  wzr even then). So in view of the future work, this isn't something I'm
  concerned about.

* Float comparisons that put Unordered into PF. These require an extra invert to
  match the new convention. These are already so unnecessarily bloated that I'm
  not convinced I'm making things materially worse here. But we realistically
  need multiple destination support in the IR to fix this particular mess.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 15:58:18 -04:00
Ryan Houdek 8184c55424 Merge pull request #3062 from alyssarosenzweig/flag/no-pf
Remove ABINoPF option
2023-09-05 12:26:45 -07:00
Alyssa Rosenzweig 79a20b899b Remove ABINoPF option
Now that PF calculation is deferred, the cost of calculating PF correctly should
be tolerable. Remove the speed hack to skip PF. It's fundamentally broken, and
there are enough broken things in FEX as it is that we don't need to maintain
this one ;-)

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 14:56:43 -04:00
Ryan Houdek a8bc6bbb2e Merge pull request #3059 from alyssarosenzweig/flag/defer-af-xor
Defer second XOR for AF
2023-09-05 11:38:47 -07:00
Alyssa Rosenzweig 305bb98cf8 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 14:21:18 -04:00
Alyssa Rosenzweig 02c864d837 OpcodeDispatcher: Defer second XOR for AF
AF is calculated as:

  ((Src1 ^ Src2) ^ Res)[4]

Due to the extract, this is equivalent to

  ((Src1 ^ Src2) ^ (Res ^ 1))[4]

We already store (Res ^ 1) as the PF byte. So, it suffices to store

  AF Byte = Src1 ^ Src2

and then we can recover the flag value

  AF = (AF Byte ^ PF Byte)[4]

This saves an instruction from the AF calculation. It does couple PF/AF writes.
In practice, most instructions fall into one of these categories:

  * Both PF and AF written together, the coupling is correct.
  * PF written but AF invalidated, irrelevant.
  * Both invalidated, irrelevant.

None of these require special handling. Where we do need special handling is
when we want to write them separately, in which case we can fix-up the value of
AF as appropriate.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 14:21:18 -04:00
Alyssa Rosenzweig 80ac824dd9 Unittests: Fix bogus lahf tests
Logical ops leave AF undefined so we can't expect it to be zero after. Mask the
result of lahf to avoid testing UB. These unit tests would regress from the work
in this MR otherwise.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 14:21:18 -04:00
Ryan Houdek c27f69dd6b Merge pull request #3061 from alyssarosenzweig/flag/cmc
OpcodeDispatcher: Optimize CMC
2023-09-05 11:06:21 -07:00
Alyssa Rosenzweig b6462ee854 OpcodeDispatcher: Optimize CMC
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 13:55:05 -04:00
Ryan Houdek 252ca88b3e Merge pull request #3058 from alyssarosenzweig/flag/undef
Stop zeroing undefined flags
2023-09-05 10:49:02 -07:00
Alyssa Rosenzweig 8ba1e91699 InstCountCI: Update
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 12:25:14 -04:00
Alyssa Rosenzweig ff0b514da8 OpcodeDispatcher: Invalidate PF/AF in more cases
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 12:25:14 -04:00
Alyssa Rosenzweig 240260576b OpcodeDispatcher: Stop zeroing so many flags
Use an explicit invalidate, so we can zero easily enough if we need to for
debugging later but we can save the instrs ordinarily.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 12:10:29 -04:00
Ryan Houdek 486f0ba1e3 Merge pull request #3038 from alyssarosenzweig/flag/af
Defer AF extract
2023-09-05 08:54:47 -07:00
Ryan Houdek 67a26a0e98 Merge pull request #3056 from Sonicadvance1/constpool_heuristic
ConstProp: Adds constpool distance heuristic
2023-09-05 08:46:22 -07:00
Alyssa Rosenzweig a1e3ce3cdf InstCountCI: Update for deferred AF extract
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 11:39:32 -04:00
Alyssa Rosenzweig 2a44acb144 OpcodeDispatcher: Defer AF extract
The AF calculation is a Bfe of an XOR result. We can't defer the XOR (since it
combines multiple inputs into one), but we can & should defer the Bfe. Since AF
is written much more often than it is read, this should come out ahead.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 11:37:35 -04:00
Alyssa Rosenzweig e5883fe892 OpcodeDispatcher: Extract CalculateAF
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 11:34:13 -04:00
Alyssa Rosenzweig 5edd9cb35b OpcodeDispatcher: Use SetAF
For constants.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 11:34:12 -04:00
Alyssa Rosenzweig f46ba52e0e OpcodeDispatcher: Use LoadAF
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 11:33:36 -04:00
Alyssa Rosenzweig c88b022d9f OpcodeDispatcher: Add AF accessors
For now these are trivial to let us refactor without functional changes. Later
in this series, they will be made nontrivial to let us defer AF calculation.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-05 11:33:36 -04:00
Ryan Houdek a24680b0a1 ConstProp: Adds constpool distance heuristic
FEX has a problem with large blocks that uses a ton of constants spread
throughout the block. Once a block gets large enough with enough
constants that have large live ranges, FEX slows down to unusable speeds
due to the register allocator spending more time calculating node
interferences than anything else in the program.

This adds a little heuristic to ensure that constants aren't reused if
the previous value is past a certain distance threshold. This threshold
works well enough that XeSS's pedantic initialization code doesn't have
issues now. See https://github.com/FEX-Emu/FEX/issues/2688 for more
information about that.

FEX itself should work to remove bad constant usages to make this pass
less necessary anyway. In most cases we are materializing duplicated 0,
1, and masks which could be done without a constant entirely.
Maybe once we've improve that enough we could remove this constant
pooling entirely.

To note, this doesn't fix the issue that XeSS causes our register
allocator, this is purely a heuristic workaround.
2023-09-03 18:27:43 -07:00
Mai 2d78b1fbca Merge pull request #3055 from Sonicadvance1/defer_disasm_init
Arm64: Only allocate vixl::Decoder if enabled
2023-09-03 18:58:42 -04:00
Ryan Houdek ca1c33047c Arm64: Only allocate vixl::Decoder if enabled
This class is very expensive to initialize so if you happen to have the
disassembler configuration enabled you were eating a very bad
initialization cost for no reason.

Only initialize the data member if any disassembler runtime option is
enabled, this completely removes the overhead.
2023-09-03 12:53:18 -07:00
Ryan Houdek b18592f153 Merge pull request #3050 from Sonicadvance1/fix_flag_reconstruction
OpcodeDispatcher: Fixes NZCV and PF flag compacting
2023-09-03 10:15:44 -07:00
Mai 8017a91e52 Merge pull request #3054 from Sonicadvance1/remove_small_ir_size_assumptions
OpcodeDispatcher: Remove final assumptions about small IR operating sizes
2023-09-03 05:47:38 -04:00
Ryan Houdek a235c3d81a InstCountCI: Update for small operator changes and opt 2023-09-03 02:26:23 -07:00
Ryan Houdek 5cc6eff62c OpcodeDispatcher: Remove final assumptions about small IR operating sizes
With Or, Orlshl, Bfe, and Bfi there were some assumptions made that i8
and i16 operations made sense. Which required us to disable the IR
validation for these operations when it was just added.

This removes the final assumptions about these IR operations supporting
these small operating sizes allowing us to enable the IR validation.

Also a very minor optimization by moving a couple extracts from source
before trying to BFI from it, making RA more optimal.
2023-09-03 02:25:34 -07:00
Mai 62fcf6cbd0 Merge pull request #3053 from Sonicadvance1/rflag_handling_32bit
OpcodeDispatcher: Cleans up RFLAGS size handling
2023-09-03 05:13:42 -04:00
Ryan Houdek cb8f183e9f InstCountCI: Update for flags cleanup 2023-09-03 01:41:52 -07:00
Ryan Houdek 44a14e7fd0 OpcodeDispatcher: Cleans up RFLAGS size handling
When moving everything away from implicit size handling, I kept this the
same codegen even though it was uglier.

Now that implicit stuff is mostly done, switch this over to 32-bit
operations. The behaviour of these changes is no functional change, just
cleans up the operations.
2023-09-03 01:41:04 -07:00
Mai 2d22176699 Merge pull request #3052 from Sonicadvance1/remove_todo_shlimm
OpcodeDispatcher/Flags: Update SHLimm to use Opsize upfront
2023-09-03 04:38:52 -04:00
Mai 338cb199a0 Merge pull request #3051 from Sonicadvance1/remove_todo_shiftleft
OpcodeDispatcher/Flags: Update ShiftLeft to use Opsize upfront
2023-09-03 04:38:00 -04:00
Ryan Houdek 16839671e5 InstcountCI: Update for SHLImm changes 2023-09-03 00:28:36 -07:00
Ryan Houdek c425db7284 OpcodeDispatcher/Flags: Update SHLimm to use Opsize upfront
This one was easy, barely anything changes behaviour, as seen by
InstCountCI changes.
2023-09-03 00:27:28 -07:00
Ryan Houdek ba422c1dc9 InstCountCI: Update for ShiftLeft changes 2023-09-03 00:16:46 -07:00
Ryan Houdek d0595b5f13 OpcodeDispatcher/Flags: Update ShiftLeft to use Opsize upfront 2023-09-03 00:16:26 -07:00
Ryan Houdek 8bae58dcc3 Merge pull request #3049 from bylaws/x87
FEXCore: Rework X87 tag word handling
2023-09-03 00:00:12 -07:00
Ryan Houdek cdfa6939b3 FEXLinuxTests: Adds unit tests to ensure we set EFLAGS correctly
Tests all five of the flags that need specific handling. Without the
prior patch FEX would fail these.
2023-09-02 23:10:57 -07:00
Ryan Houdek cb5d665046 OpcodeDispatcher: Fixes NZCV and PF flag compacting
Currently in main today, FEX fails to compact OF/CF/ZF/SF and PF.

This is due to recent optimizations with flag calculations on each of
these. Now that we have a centralized location where we compact and set
our internal representation of flags we can do this in one location.
2023-09-02 23:10:57 -07:00
Billy Laws 393b8c657a InstCountCI: Update for x87 changes 2023-09-02 09:17:33 -07:00
Billy Laws 3792f707dc FEXLoader: Convert between abridged/full tag fmts in signal dispatch
X86 fpstate expects FTW to be saved in the FSAVE format, whereas X64
fpstate expects it to be saved in the abridged format used by FXSAVE.
2023-09-02 09:17:33 -07:00
Billy Laws 13b8f95f85 X87: Switch all stack pointer accesses to 32-bit OpSize 2023-09-02 09:17:33 -07:00
Billy Laws cb49373f47 FEXCore: Rework X87 tag word handling
The FXSAVE and FSAVE tag words are written out in different formats,
with FXSAVE using an abridged version that lacks the zero/special/valid
distinction. Switch to using this abridged version internally for
simplicity, and to allow the calculation of zero/special/valid
distinction to be deferred until an fxsave instruction (in the future,
currently the distinction is ignored and only valid/empty states are
possible).
2023-09-02 09:17:33 -07:00
Ryan Houdek ea965810e5 Merge pull request #3048 from Sonicadvance1/construct_eflags
Context: Adds helper to reconstruct and consume packed EFLAGS
2023-09-02 08:14:58 -07:00
Ryan Houdek 435f03c703 Context: Adds helper to reconstruct and consume packed EFLAGS
Currently FEX's internal EFLAGS representation is a perfect 1:1 mapping
between bit offset and byte offset. This is going to change with #3038.
There should be no reason that the frontend needs to understand how to
reconstruct the compacted flags from the internal representation.

Adds context helpers and moves all the logic to FEXCore. The locations
that previously needed to handle this have been converted over to use
this.
2023-09-02 07:05:54 -07:00
Ryan Houdek 81046efcd3 Merge pull request #3047 from alyssarosenzweig/build/no-telem
SignalDelegator: Fix build with telemetry disabled
2023-09-02 05:47:33 -07:00
Alyssa Rosenzweig 69b0747281 SignalDelegator: Fix build with telemetry disabled
/home/alyssa/FEX/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.cpp:1546:7: error: use of undeclared identifier 'CrashMask'
      CrashMask |= (1ULL << Signal);

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-09-02 08:09:05 -04:00
Mai f0ab9603e1 Merge pull request #3046 from Sonicadvance1/fix_telemetry_exit_group
Syscalls: Fix telemetry with exit_group
2023-09-01 05:15:56 -04:00
Ryan Houdek aea4a88e00 Syscalls: Fix telemetry with exit_group
Picked up on more games exiting with exit_group that I want to ensure
their telemetry data gets saved. Implement support for this.
2023-09-01 01:53:07 -07:00
Ryan Houdek ee8092bdc8 Merge pull request #3035 from alyssarosenzweig/flag/opts
Optimize ADD flag calculation
2023-08-31 16:30:01 -07:00
Alyssa Rosenzweig 31c92a422b InstCountCI: Update for add/sub work
Big Delete The Code energy.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-31 09:06:04 -04:00
Alyssa Rosenzweig 2edce18a59 ConstProp: Optimize XOR with 0
This cleans up the AF flag calculation for `neg`.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-31 09:06:04 -04:00
Alyssa Rosenzweig 659568ec1b ConstProp: Propagate 0 to first argument of SubNZCV
This allows inlining a constant into the comparison for Neg.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-31 09:06:01 -04:00
Alyssa Rosenzweig dcb09b085b OpcodeDispatcher: Use AddNZCV/SubNZCV
32-bit or 64-bit addition without carry-in. This matches the baseline hardware
semantic. Generalizing to support other cases can come later, this should be a
win already.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-31 09:05:41 -04:00
Alyssa Rosenzweig 45d99a2cce OpcodeDispatcher: Fix source sizes for Sub flags
For correct carry/overflow behaviour, we need to use a compare of the right size. The existing logic
to look at the source sizes doesn't work for this, since a 32-bit NEG instruction will compare a
32-bit source with a 64-bit _Constant(0) .. which needs a 32-bit compare but the existing logic
would use a 64-bit compare. This is not yet a bug fix, since the overflow code is currently in
software for 32-bit negates so it's irrelevant. But it should prevent regressions from using native
compares later in this series. Presumably this was intended all along but left as-is to avoid
disturbing instcountci once noticed. Time to disturb CI!

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-31 09:05:39 -04:00
Alyssa Rosenzweig f688364bf2 IR: Add AddNZCV/SubNZCV op
AddNZCV is a new op to return the NZCV for an addition directly, which lets us
skip software flag calculation in some cases. In the future it would be nice to
fuse this into the Add itself as a second destination to avoid repeating the
addition, but that's a very involved change and right now I'm building FEX on an
old Chromebook because my M1 kernel is FUBAR.

Similarly, SubNZCV returns flags for Sub. This has the extra twist of needing to
invert the carry bit due to the inverted definition between arm64 and x86_64.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-31 07:52:12 -04:00
Mai 7c81a0d4fe Merge pull request #3042 from Sonicadvance1/optimize_call
OpcodeDispatcher: Optimize calls with push
2023-08-31 02:23:06 -04:00
Mai 7f997387b0 Merge pull request #3044 from Sonicadvance1/optimize_aes
Arm64: Optimize AES operations by caching a zero register
2023-08-31 00:20:10 -04:00
Ryan Houdek cfe6b929bb InstCountCI: Updates for aes optimization
XMM AES operation is now optimal.
Most other changes are just because of RA changing around the zero
register.
2023-08-30 20:43:11 -07:00
Ryan Houdek 61df7a576a OpcodeDispatcher: Be super defensive when starting a new block
Ensure all cached data is correct.
2023-08-30 20:43:11 -07:00
Ryan Houdek ffaa908475 OpcodeDispatcher: Use the named zero register for each usage
This will let us reuse it in some cases. In some of these
implementations there is a bad code smell around using the zero register
but that isn't going to get solved in this commit.
2023-08-30 20:43:11 -07:00
Ryan Houdek a2307f28d6 Arm64: Optimize AES operations by caching a zero register
A bunch of the AES operations take a zero register upfront and we
currently materialize it for each instruction.
Considering that most AES operations are used back to back, we can
eliminate these materializations by caching it between instructions.

Additionally removes a move in the optimal case when destination matches
the state register, which is exactly what the SSE operation ends up
doing.

AESKeyGenAssist has an edge case that if the destination RA overlaps the
zero register then we still need to eat a move, hopefully doesn't happen
too frequently in practice. This is also the lesser used instruction so
it isn't a big deal. RA constraints could solve that still.
2023-08-30 19:00:43 -07:00
Ryan Houdek 1446d4fe12 IR: Adds support for named vector zero
This is useful for caching a zero register vector which we use in
various locations. This will be abused soon.
2023-08-30 18:59:38 -07:00
Mai 9fb8c95ef4 Merge pull request #3043 from Sonicadvance1/optimize_blendv
OpcodeDispatcher: Optimize BLENDV when xmm0 is one of the sources
2023-08-30 20:36:14 -04:00
Ryan Houdek 58caee3614 InstCountCI: Update for optimized blendv 2023-08-30 17:13:05 -07:00
Ryan Houdek 24215f7ad0 OpcodeDispatcher: Optimize BLENDV when xmm0 is one of the sources
This instruction has xmm0 be one of the implicit sources. We were
loading xmm0 twice. #2700 would also fix this but that breaks other
things for some reason.
2023-08-30 17:09:34 -07:00
Ryan Houdek b20c518bf0 OpcodeDispatcher: Optimize calls with push
InstCountCI doesn't cover branch instructions so needs manual
inspection.
2023-08-30 16:25:01 -07:00
Ryan Houdek d738538a34 Merge pull request #3041 from Sonicadvance1/telem_runtime
FEXCore: Allows disabling telemetry at runtime
2023-08-30 13:30:51 -07:00
Ryan Houdek 48df17b836 InstCountCI: Update for changed tests
Since the telemetry array is always in the corestate now some numbers
change.
2023-08-30 13:01:27 -07:00
Ryan Houdek 81a32c3998 FEXCore: Allows disabling telemetry at runtime
This is useful for InstCountCI so you can disable the telemetry
gathering even if enabled so it doesn't affect the CI system.
2023-08-30 12:59:41 -07:00
Mai 02b891c0fe Merge pull request #3040 from Sonicadvance1/optimize_aeskeygen
Arm64: Optimize AESKeyGenAssist
2023-08-30 15:58:29 -04:00
Mai 012750f2bb Merge pull request #3037 from Sonicadvance1/remove_debug_log
OpcodeDispatcher: Removes erroneous debug log
2023-08-30 15:57:31 -04:00
Ryan Houdek b40147a269 InstCountCI: Update for aeskeygenassist 2023-08-30 12:17:07 -07:00
Ryan Houdek d8f131fa3d Arm64: Optimize AESKeyGenAssist
We can load the swizzle table from our constant pool now. This removes
the only usage of VTMP3 from our Arm64 JIT.

I would say the this is now optimal for the version without RCON set.
With RCON we could technically make some of the move of the constant
more optimal.
2023-08-30 12:15:09 -07:00
Ryan Houdek 2c2081c977 Merge pull request #3039 from Sonicadvance1/print_OpSize
IR: Adds printer for OpSize
2023-08-30 11:50:13 -07:00
Ryan Houdek 572cc57aa3 IR: Adds printer for OpSize 2023-08-30 11:32:43 -07:00
Ryan Houdek 9bfd4b650f OpcodeDispatcher: Removes erroneous debug log 2023-08-30 11:12:12 -07:00
Ryan Houdek e1c2033fa9 Merge pull request #3036 from Sonicadvance1/OpSize_IRDump_Parse
IR: Adds IR::OpSize to IRDumper
2023-08-30 11:01:06 -07:00
Ryan Houdek d3ee794dcb IR: Adds IR::OpSize to IRDumper
This was missing before.
2023-08-30 10:43:23 -07:00
Mai b40f784da0 Merge pull request #3034 from Sonicadvance1/remove_implicit_add
IR: Removes implicit sized add
2023-08-30 08:50:49 -04:00
Ryan Houdek f741ebf970 IR: Removes implicit sized add
Saw a few locations in here that we operate things at 64-bit
unconditionally around pointer calculation. Will be coming back for
those when running in 32-bit mode.

This is the last of the implicit sized ALU operations! After this I'll
be going through the IR more individually to try and remove any
stragglers.
Then should be able to start cleaning up and actually optimizing GPR
operations.
2023-08-29 22:26:51 -07:00
Mai 351c0eee42 Merge pull request #3033 from Sonicadvance1/remove_implicit_bfe
IR: Removes implicit sized bfe
2023-08-29 23:02:02 -04:00
Ryan Houdek e8b767b553 IR: Removes implicit sized bfe
This one is a bit of a mess, looking forward to coming back and cleaning
this up.
2023-08-29 19:43:39 -07:00
Mai 8239f8aa27 Merge pull request #3031 from Sonicadvance1/remove_implicit_and
IR: Removes implicit sized and
2023-08-29 02:32:40 -04:00
Ryan Houdek 9e70aa4192 IR: Removes implicit sized and 2023-08-28 22:43:21 -07:00
Ryan Houdek 59a4d15907 Merge pull request #3030 from Sonicadvance1/remove_implicit_sub
IR: Removes implicit sized sub
2023-08-28 22:20:30 -07:00
Ryan Houdek b5dc6a69c7 IR: Removes implicit sized sub 2023-08-28 22:05:02 -07:00
Mai 227ba9f9fc Merge pull request #3029 from Sonicadvance1/remove_variable_bfi
IR: Removes bfi from variable size
2023-08-29 00:50:45 -04:00
Ryan Houdek a276b37252 IR: Removes bfi from variable size
This one was already explicit sized. Just convert it over to OpSize.
2023-08-28 21:31:37 -07:00
Mai 9b55a34e75 Merge pull request #3028 from Sonicadvance1/remove_implicit_xor
IR: Removes implicit sized xor
2023-08-28 23:59:39 -04:00
Ryan Houdek 8bc84c202c IR: Removes implicit sized xor 2023-08-28 19:51:14 -07:00
Ryan Houdek e9a3848602 Merge pull request #3027 from Sonicadvance1/remove_implicit_andn
IR: Removes implicit sized andn
2023-08-28 19:39:49 -07:00
Ryan Houdek 516f27bff5 Merge pull request #3026 from Sonicadvance1/remove_implicit_or
IR: Removes implicit sized or
2023-08-28 19:39:42 -07:00
Ryan Houdek 1699ec9a76 IR: Removes implicit sized andn 2023-08-28 19:16:16 -07:00
Ryan Houdek db6c8852fc IR: Removes implicit sized or 2023-08-28 19:06:05 -07:00
Ryan Houdek 24a9254e37 Merge pull request #3025 from Sonicadvance1/remove_implicit_lshr
IR: Removes implicit sized lshr
2023-08-28 19:05:47 -07:00
Ryan Houdek 65dc6f3e90 IR: Removes implicit sized lshr 2023-08-28 18:16:56 -07:00
Mai 8534d3dfbf Merge pull request #3024 from Sonicadvance1/remove_implicit_lshl
IR: Removes implicit sized lshl
2023-08-28 21:08:05 -04:00
Ryan Houdek 60c4438780 IR: Removes implicit sized lshl 2023-08-28 17:50:41 -07:00
Ryan Houdek 915e52046c Merge pull request #3023 from Sonicadvance1/remove_mul
IR: Removes implicit sized mul ops
2023-08-28 17:49:32 -07:00
Ryan Houdek 898ce1ce8f IR: Removes implicit sized UMulH 2023-08-28 17:20:55 -07:00
Ryan Houdek aa8dfd6af1 IR: Removes implicit sized UMul 2023-08-28 17:20:55 -07:00
Ryan Houdek 6a6d808b0d IR: Removes implicit sized MulH 2023-08-28 17:20:55 -07:00
Ryan Houdek fac5b2ac72 IR: Removes implicit sized Mul 2023-08-28 17:20:55 -07:00
Ryan Houdek a55616e4db Merge pull request #3022 from Sonicadvance1/remove_sext
IR: Removes sext IR helper
2023-08-28 17:20:39 -07:00
Ryan Houdek b9e4a1423f IR: Removes sext IR helper
You hold no power here IR operation.
2023-08-28 17:03:38 -07:00
Ryan Houdek 6f661534a2 Merge pull request #3021 from Sonicadvance1/remove_implicit_op_part_move
IR: Removes implicit sized {Create,Extract}ElementPair
2023-08-28 17:03:12 -07:00
Ryan Houdek c0bb6a053f IR: Removes implicit sized {Create,Extract}ElementPair 2023-08-28 16:50:00 -07:00
Ryan Houdek bc1e89d91d Merge pull request #3020 from Sonicadvance1/remove_implicit_ops_pt_atomic
Remove implicit sized IR ops part atomic
2023-08-28 07:27:47 -07:00
Ryan Houdek c1b4c11e54 Merge pull request #3018 from Sonicadvance1/opcodedispatcher_sizeopt
OpcodeDispatcher: Optimize Get{Src,Dst}Size
2023-08-28 07:13:41 -07:00
Ryan Houdek a36427d01e IR: Removes implicit sized CAS 2023-08-28 07:12:31 -07:00
Ryan Houdek 594baff705 IR: Removes implicit sized CASPair 2023-08-28 07:12:31 -07:00
Ryan Houdek e9f2bc037f IR: Removes non-opsize AtomicAdd/Sub/And/Or
These were unused
2023-08-28 07:12:31 -07:00
Ryan Houdek 6dcfd6eb73 IR: Removes non-opsize AtomicXor 2023-08-28 07:12:31 -07:00
Ryan Houdek 4f8a63459c IR: Removes non-opsize AtomicSwap 2023-08-28 07:12:31 -07:00
Ryan Houdek ab230bf527 IR: Removes non-opsize AtomicFetchAdd 2023-08-28 07:12:31 -07:00
Ryan Houdek 8ba9613972 IR: Removes non-opsize AtomicFetchSub 2023-08-28 07:12:31 -07:00
Ryan Houdek 9370e30af4 IR: Removes non-opsize AtomicFetchAnd 2023-08-28 07:12:31 -07:00
Ryan Houdek 25ce57ef92 IR: Removes non-opsize AtomicFetchOr 2023-08-28 07:12:31 -07:00
Ryan Houdek 436e0f6f86 IR: Removes non-opsize AtomicFetchXor 2023-08-28 07:12:31 -07:00
Ryan Houdek f44c6f394f IR: Removes non-opsize AtomicFetchNeg 2023-08-28 07:12:31 -07:00
Ryan Houdek 5415e4c95f Merge pull request #3017 from Sonicadvance1/remove_implicit_ops_pt1
Remove implicit sized IR ops part 1
2023-08-28 07:11:44 -07:00
Ryan Houdek bb1362b2bf OpcodeDispatcher: Optimize Get{Src,Dst}Size
These functions are called a lot....a lot a lot.
Optimize these in to a couple of ALU operations instead of a whole
table lookup. Confirming with output assembly that this becomes more
optimal.
2023-08-28 05:29:56 -07:00
Ryan Houdek 51baf594c9 OpcodeDispatchers: Adds OpSizeFromSrc/Dst helpers
To reduce how cluttered `IR::SizeToOpSize(GetSrcSize(Op))` is.
2023-08-28 05:11:32 -07:00
Ryan Houdek 48669b7006 IR: Removes implicit sized orlshl/orlshr 2023-08-28 05:04:32 -07:00
Ryan Houdek 16481c0e55 IR: Removes implicit sized rev 2023-08-28 05:04:32 -07:00
Ryan Houdek b00310674a IR: Removes implicit sized not 2023-08-28 05:04:32 -07:00
Ryan Houdek 5768444ce9 IR: Removes implicit sized abs 2023-08-28 05:04:32 -07:00
Ryan Houdek 1f1473eb74 IR: Removes implicit sized neg 2023-08-28 05:04:32 -07:00
Ryan Houdek cccd7001cb IR: Removes implicit sized ashr 2023-08-28 05:04:32 -07:00
Ryan Houdek ce8392d5ae IR: Removes implicit sized ror 2023-08-28 05:02:01 -07:00
Ryan Houdek 386cf36cfd IR: Removes implicit sized sbfe
This one is a bit weird since currently it /always/ assumes a 64-bit
operating size.

We'll likely need to revisit this.
2023-08-28 05:02:01 -07:00
Ryan Houdek 0ddc23a5c9 IR: Removes implicit sized Popcount 2023-08-28 05:02:01 -07:00
Ryan Houdek b95648a4ab IR: Removes implicit sized FindMSB 2023-08-28 05:02:01 -07:00
Ryan Houdek bf18672999 IR: Removes implicit sized FindLSB 2023-08-28 05:02:01 -07:00
Ryan Houdek f405c1be69 IR: Removes inverted EntrypointOffset/InlineEntrypointOffset 2023-08-28 05:02:01 -07:00
Ryan Houdek f3cd115fa7 IR: Removes implicit sized CountLeadingZeroes 2023-08-28 05:02:00 -07:00
Ryan Houdek a14720e130 IR: Removes implicit sized FindTrailingZeroes 2023-08-28 05:02:00 -07:00
Ryan Houdek 86ed909de8 IR: Removes implicit sized DIV/REM 2023-08-28 05:02:00 -07:00
Ryan Houdek ac7e75c06b IR: Removes implicit sized UDIV/UREM 2023-08-28 05:02:00 -07:00
Ryan Houdek ec3e7ceeb5 IR: Removes implicit sized EXTR 2023-08-28 05:02:00 -07:00
Ryan Houdek c8c8ddbd4f IR: Removes implicit sized PDEP/PEXT 2023-08-28 05:02:00 -07:00
Ryan Houdek 35013bda37 IR: Adds helper to convert between an integer size and IR::OpSize
This is a nop operation and will get optimized away in release builds.
2023-08-28 05:02:00 -07:00
Ryan Houdek 62a9a075b7 Arm64: Leave a comment that 32-bit division shouldn't leave garbage in upper 64-bits 2023-08-28 05:02:00 -07:00
Ryan Houdek ea6d068cc5 IR: Removes implicit sized LDIV/LREM 2023-08-28 05:02:00 -07:00
Ryan Houdek 5013473ec0 IR: Removes implicit sized LUDIV/LUREM 2023-08-28 05:02:00 -07:00
Alyssa Rosenzweig b801bacb36 Merge pull request #3016 from Sonicadvance1/update_instcountci
InstCountCI: Update for previous changes
2023-08-28 07:42:20 -04:00
Ryan Houdek f02d291da1 InstCountCI: Update for previous changes
Missed thsi in the SRA PR
2023-08-27 23:41:41 -07:00
Ryan Houdek 6f2b3e76ac Merge pull request #3013 from Sonicadvance1/32bit_sra
IR/Passes/RA: Enable SRA for 32-bit GPRs
2023-08-27 21:30:39 -07:00
Ryan Houdek 1d7c280367 Merge pull request #3012 from Sonicadvance1/optimize_movmskps
OpcodeDispatcher: Optimizes SSE movmaskps
2023-08-27 21:29:04 -07:00
Ryan Houdek d6864871ed InstCountCI: Update for movmskps optimization 2023-08-27 21:07:20 -07:00
Ryan Houdek 514a8223d9 OpcodeDispatcher: Optimizes SSE movmaskps
This now improves the instruction implementation from 17 instructions
down to 5 or 6 depending on if the host supports SVE.

I would say this is now optimal.
2023-08-27 21:07:20 -07:00
Ryan Houdek 8d110738ac IR: Add option to disable vector shift range clamping
The range check and clamping is necessary in the cases of passing x86
shift amounts directly through VUSHL/VSSHR.

Some AVX operations are still using these with range clamping. A future
investigation task should be the check if they can be switched over to
the wide variants that we implemented for the SSE instructions.

When consuming our own controlled data, we don't want the range clamping
to be enabled.
2023-08-27 21:07:20 -07:00
Mai 590345b125 Merge pull request #3014 from Sonicadvance1/remove_implicit_alu_ops
IR: Convert all Move+Atomic+ALU ops from implicit to explicit size
2023-08-27 20:25:11 -04:00
Mai e2ac97f4db Merge pull request #3015 from Sonicadvance1/instcount_ci_rorx_abovemax
InstCountCI: Test rorx at max mask size
2023-08-27 20:21:45 -04:00
Ryan Houdek a216ea465c InstCountCI: Test rorx at max mask size
Just to ensure we capture the nop nature of passing in the rotate amount
of the operating size.
2023-08-27 01:43:47 -07:00
Ryan Houdek e4bb0df486 IR: Convert all Move+Atomic+ALU ops from implicit to explicit size
The number of times the implicit size calculation in GPR operations has
bit us is immeasurable and was a mistake from the start of the project.
The vector based operations never had this problem since they were
explicitly sized for a long time now.

This converts the base IR operations to be explicitly sized, but adds
implicit sized helpers for the moment while we work on removing implicit
usage from the OpcodeDispatcher.

Should be NFC at this moment but it is a big enough change that I want
it in before the "real" work starts.
2023-08-27 01:35:08 -07:00
Ryan Houdek 7146691360 IR/Passes/RA: Enable SRA for 32-bit GPRs
Noticed that we hadn't ever enabled this, which was a concern when our
GPR operations weren't as strict about leaving garbage in the upper bits
when operating as a 32-bit operation.

Now that our ALU operations are more strict about enforcing upper bit
zeroing we can enable this.

This causes Half-Life: Source FPS to get to > 200FPS finally. Causes
significant performance improvements for 32-bit games because we're no
longer redundantly moving registers before and after every operation.
Causing a bunch of 3-4 instruction sequences to convert to 1.
2023-08-26 18:22:50 -07:00
Ryan Houdek 6cb1afc2ad unittests/asm: Adds ADC/SBB test 2023-08-26 18:22:50 -07:00
Ryan Houdek 572d6cd3e6 OpcodeDispatcher: Fixes ADC and SBB 2023-08-26 18:22:50 -07:00
Ryan Houdek fcc37bf6a8 OpcodeDispatcher: Fixes RCR and ADOX 32-bit
Automatic size inheritance was breaking these operations.
2023-08-26 18:22:50 -07:00
Ryan Houdek 8f7925d06f Arm64: Simple typo fix 2023-08-26 18:22:50 -07:00
Ryan Houdek eace648fa9 OpcodeDispatcher: Fixes bug in UMUL
This was trying to operating on a 32-bit value but BFE the upper
32-bits.

Actually fixes this so it is operating on the 64-bit multiply result.
2023-08-26 18:22:50 -07:00
Ryan Houdek a4ac21a4e4 OpcodeDispatcher: Fixes bug in GetRFLAG with CachedNZCV
This was only operating at byte size but it was attempting to get bit
offsets at greater than operating size.
Change operating size over to 64-bit.
2023-08-26 18:22:50 -07:00
Ryan Houdek a01e69092d Arm64: Ensure Bfe and Sbfe operate at 32-bit or 64-bit op size
For Sbfe at least it ensures the upper bits don't get filled with
garbage.
Bfe it doesn't change behaviour but best to be correct.
2023-08-26 18:22:50 -07:00
Ryan Houdek 2fde2140ef Arm64: Ensure assert is testing correct array 2023-08-26 18:22:50 -07:00
Ryan Houdek 547daf8b6e Merge pull request #2857 from Sonicadvance1/fix_ra_validation
IR: Fixes RAValidation for 32-bit applications
2023-08-26 18:22:18 -07:00
Ryan Houdek e10afefb2b IR: Fixes RAValidation for 32-bit applications
RAValidation was making an assumption that GPR register class would only
have up to 16 registers for either SRA or dynamic registers.

When running a 32-bit application we allow 17 GPRs to be dynamically
allocated, since we can take 8 back from SRA in that case.

Just split the two classes in the RAValidation pass since they will
never overlap their allocation.

Fixes validation in `32Bit_Secondary/15_XX_0.asm` locally that changed
behaviour due to tinkering.
2023-08-26 16:18:03 -07:00
Mai 0195bb6e5a Merge pull request #3010 from Sonicadvance1/exit_group_quit
Linux: Call exit_group when application tries
2023-08-25 21:13:59 -04:00
Ryan Houdek edd27a2723 Linux: Call exit_group when application tries
When the application calls exit_group we no longer need to care about
cleanup because the entire process group is leaving.

Just immediately call exit group and get out. Might revisit this in the
future.

Fixes #2752
2023-08-25 17:41:57 -07:00
Mai 7c6660f634 Merge pull request #3009 from Sonicadvance1/fix_frint64
X8764: Ensure frndint uses host rounding mode
2023-08-25 20:01:01 -04:00
Ryan Houdek 9ba46f429e X8764: Ensure frndint uses host rounding mode
This previously used `Round_Nearest` which had a bug on Arm64 that it
actually was always using `Round_Host` aka frinti.
Ever since 393cea2e8ba47a15a3ce31d07a6088a2ff91653c[1] this has been fixed
so that `Round_Nearest` actually uses frintn for neaest.

This instruction actually wants to use the host rounding mode.
Once issue with this is that x87 and SSE have different rounding mode
flags and currently we conflate the two in our JIT. This will need to be
fixed in the future.

In the meantime this restores behaviour that it actually uses the host
rounding mode, which fixes black screen and broken vertices in Grim
Fandango Remastered.

[1] e89321dc60 for scalar.
2023-08-25 16:04:01 -07:00
Mai db3dc3edf4 Merge pull request #3003 from Sonicadvance1/optimize_pshuf
OpcodeDispatcher: Optimize PSHUF{LW, HW, D}!
2023-08-25 16:36:52 -04:00
Ryan Houdek c26798af83 InstCountCI: Update for shuffles! 2023-08-25 12:59:40 -07:00
Ryan Houdek a76c2c57b0 OpcodeDispatcher: Optimize PSHUF{LW, HW, D}!
This is way more optimal!
2023-08-25 12:59:40 -07:00
Ryan Houdek 7f63d87295 IR: Adds support for new LoadNamedVectorIndexedConstant IR 2023-08-25 12:59:40 -07:00
Mai bf12f08218 Merge pull request #3002 from Sonicadvance1/optimize_movmaskpd
OpcodeDispatcher: Optimize 128-bit movmaskpd
2023-08-25 08:50:28 -04:00
Mai 1f7d138d2a Merge pull request #3008 from Sonicadvance1/optimize_movddup
OpcodeDispatcher: Optimize movddup from register
2023-08-25 08:48:40 -04:00
Mai f36f07055a Merge pull request #3007 from Sonicadvance1/optimize_cvtdq2pd
OpcodeDispatcher: Optimize cvtdq2pd from register source
2023-08-25 08:47:47 -04:00
Mai 30a1a382c4 Merge pull request #3006 from Sonicadvance1/optimize_movq
OpcodeDispatcher: Optimizes movq
2023-08-25 08:46:02 -04:00
Mai 631655dd81 Merge pull request #3005 from Sonicadvance1/nontemporalmoves
OpcodeDispatcher: Optimize nontemporal moves
2023-08-25 08:44:48 -04:00
Mai 0ef439f830 Merge pull request #3004 from Sonicadvance1/optimize_scalar_cvt
OpcodeDispatcher: Generate more optimal code for scalar GPR converts
2023-08-25 08:43:56 -04:00
Ryan Houdek 2aba628a24 InstCountCI: Update for movddup optimization 2023-08-25 03:44:30 -07:00
Ryan Houdek 2fbcf2e4a9 OpcodeDispatcher: Optimize movddup from register
This is now optimal
2023-08-25 03:44:10 -07:00
Ryan Houdek 7367f166b0 InstCountCI: Update for cvtdq2pd optimization 2023-08-25 03:39:45 -07:00
Ryan Houdek 00124205e5 OpcodeDispatcher: Optimize cvtdq2pd from register source
This is now optimal
2023-08-25 03:39:24 -07:00
Ryan Houdek 1a3a59dd38 InstCountCI: Update for movq optimization 2023-08-25 03:30:52 -07:00
Ryan Houdek 81281e2115 OpcodeDispatcher: Optimizes movq
Removes a redundant move between registers and makes it optimal.
Also removes a couple redundant moves on the avx version.
2023-08-25 03:29:58 -07:00
Ryan Houdek a901e5f3df InstCountCI: Update for optimized cvtsi2s{s,d} 2023-08-25 03:19:11 -07:00
Ryan Houdek 1cb2b084b3 OpcodeDispatcher: Generate more optimal code for scalar GPR converts
1) In the case that we are converted a GPR, don't zero extend it first.
2) In the case that the scalar comes from memory, load it first in an
   FPR and converted it in-place.

These are now optimal in the case of AFP is unsupported.
2023-08-25 03:19:11 -07:00
Ryan Houdek 189b0da68f JIT/Int: Add support for scalar conversion as well 2023-08-25 03:19:11 -07:00
Ryan Houdek 9fa877e512 InstCountCI: Update for move temporal optimization 2023-08-25 03:13:28 -07:00
Ryan Houdek f3679a99ec OpcodeDispatcher: Optimize nontemporal moves
These are now optimal.
2023-08-25 03:13:10 -07:00
Ryan Houdek 62156f2152 ARM64JIT: Adds support for scalar cvt 2023-08-25 02:34:30 -07:00
Ryan Houdek 3808f97283 InstCountCI: Update for movmaskpd optimization 2023-08-24 17:27:54 -07:00
Ryan Houdek 3a6d25f56a unittests/ASM: Update movmskpd test to include an edge of garbage but no sign bit 2023-08-24 17:27:54 -07:00
Ryan Houdek 9a54898429 OpcodeDispatcher: Optimize 128-bit movmaskpd
I'd consider this optimal now.

Thanks to @dougallj for the optimization idea again!
2023-08-24 17:27:54 -07:00
Ryan Houdek 80d871fb18 Merge pull request #3001 from Sonicadvance1/optimize_cvtps2pd
OpcodeDispatcher: Optimize cvtps2pd
2023-08-24 16:09:12 -07:00
Ryan Houdek 4d58ec1025 Merge pull request #3000 from Sonicadvance1/optimize_cvt
OpcodeDispatcher: Optimize MMX conversion operation
2023-08-24 16:09:04 -07:00
Ryan Houdek 60ad76732a InstCountCI: Update for cvtps2pd optimization 2023-08-24 15:56:02 -07:00
Ryan Houdek 3731e6d88b OpcodeDispatcher: Optimize cvtps2pd
SSE version is now optimal and AVX version gets rid of a redundant move.
2023-08-24 15:55:11 -07:00
Ryan Houdek e3812f9c3c InstCountCI: Update tests for mmx optimization 2023-08-24 15:46:54 -07:00
Ryan Houdek c441b238c7 OpcodeDispatcher: Optimize MMX conversion operation
These instructions are now optimal
2023-08-24 15:46:19 -07:00
Ryan Houdek 72ce7ddf2d Arm64: Optimize CVT operations for 64-bit variants
Using 128-bit converts for 64-bit versions cuts their throughput in half
on Cortex. Ensure we use the 64-bit version when possible.
2023-08-24 15:45:07 -07:00
Ryan Houdek e025d32531 Merge pull request #2999 from lioncash/catch
Externals: Update Catch2 to v2.13.10
2023-08-24 15:19:23 -07:00
Ryan Houdek 200dbdd0e6 Merge pull request #2998 from Sonicadvance1/optimize_addsub_fcma
OpcodeDispatcher: Optimize addsubp{s,d} using fcadd
2023-08-24 15:19:06 -07:00
Lioncache 989fe22e2d Externals: Update Catch2 to v2.13.10
Updates it to the latest v2 branch tag
2023-08-24 18:04:59 -04:00
Ryan Houdek 4e7adeec85 InstCountCI: Adds new files for FCMA 2023-08-24 15:00:42 -07:00
Ryan Houdek a1210f892a OpcodeDispatcher: Optimize addsubp{s,d} using fcadd
This extension was added with seemingly Cortex-A710 and turns this
instruction in to two instructions which is quite good.

Needs #2994 merged first.

Huge thanks to @dougallj for the optimization idea!
2023-08-24 15:00:41 -07:00
Ryan Houdek ba01eac467 IR: Adds support for ARM's FCMA FCADD instruction 2023-08-24 15:00:41 -07:00
Ryan Houdek c5d147322f HostFeatures: Adds support for FCMA 2023-08-24 15:00:41 -07:00
Ryan Houdek df99b7b9b6 Merge pull request #2994 from Sonicadvance1/cache_namedvectorconstants
OpcodeDispatcher: Cache named vector constants in the block
2023-08-24 15:00:05 -07:00
Ryan Houdek 565b30e15e OpcodeDispatcher: Cache named vector constants in the block
If the named constant of that size gets used multiple times then just
use the previous value if it was in scope.

Makes addsubp{s,d} and phminposuw more optimal for each that are in a
block.

Needs #2993 merged first.
2023-08-24 14:46:37 -07:00
Mai ab83ab42dd Merge pull request #2993 from Sonicadvance1/addsub_opt
OpcodeDispatcher: Optimize AddSubP{S,D}
2023-08-24 17:44:41 -04:00
Ryan Houdek b0ec4197ba Merge pull request #2997 from lioncash/fmt
Externals: Update fmt to 10.1.0
2023-08-24 14:19:18 -07:00
Lioncache 9035a29906 Externals: Update fmt to 10.1.0
Updates fmt to the latest version.
2023-08-24 17:01:40 -04:00
Ryan Houdek b547550442 InstCountCI: Update for addsubp 2023-08-23 20:33:55 -07:00
Ryan Houdek f300196d90 OpcodeDispatcher: Optimize AddSubP{S,D}
Use a named constant for loading the sign inversion, then EOR the second
source and just FAdd it all.
In a vacuum it isn't a significant improvement, but as soon as more than
one instruction is in a block it will eventually get optimized with
named constant caching and be a significant win.

Thanks to @rygorous for the idea!
2023-08-23 20:32:51 -07:00
Ryan Houdek c5f358b47a Merge pull request #2992 from lioncash/doc
x86_64/MemoryOps: Fix mislabeled IR op messages
2023-08-23 20:05:39 -07:00
Lioncache 42ccc18606 x86_64/MemoryOps: Fix mislabeled IR op messages 2023-08-23 22:54:36 -04:00
Mai 66c6f96120 Merge pull request #2990 from Sonicadvance1/optimize_pmulh
OpcodeDispatcher: Optimize PMULH{U,}W using new IR operations
2023-08-23 22:06:14 -04:00
Ryan Houdek 1aa2c534f7 Merge pull request #2991 from lioncash/pcl
OpcodeDispatcher: Remove redundant moves from PCLMULQDQ and AES operations
2023-08-23 18:47:52 -07:00
Ryan Houdek e9f292462a InstCountCI: Update for pmulh{u,}w optimization 2023-08-23 18:38:05 -07:00
Ryan Houdek 77b6d854b9 OpcodeDispatcher: Optimize PMULH{U,}W using new IR operations
SSE implementations are now optimal.
SVE-128bit operation makes it more optimal.
2023-08-23 18:38:05 -07:00
Ryan Houdek 05b9651279 IR: Implements new vector multiply returning high bits
SVE implemented a new instruction that does this explicitly, so we
should support it directly.
2023-08-23 18:38:05 -07:00
Lioncache 26c81224ac OpcodeDispatcher: Remove redundant moves from AESIMC
Zero-extension will occur automatically upon storing if necessary.

We can also join the SSE and AVX implementations together.
2023-08-23 21:34:37 -04:00
Lioncache 8a622a3c1a OpcodeDispatcher: Remove redundant moves from VAESEnc
Zero-extension will occur automatically upon storing if necessary.
2023-08-23 21:30:06 -04:00
Lioncache f4848fd1a7 OpcodeDispatcher: Remove redundant moves from VAESEncLast
Zero-extension will occur automatically upon storing if necessary.
2023-08-23 21:28:14 -04:00
Lioncache d37ce08ae9 OpcodeDispatcher: Remove redundant move from VAESDec
Zero-extension will occur automatically upon storing if necessary.
2023-08-23 21:26:49 -04:00
Lioncache a6f1a9f8e8 OpcodeDispatcher: Remove redundant moves from VAESDecLast
Zero-extension will occur upon storing if necessary.
2023-08-23 21:25:15 -04:00
Lioncache 52ab3f6a1e OpcodeDispatcher: Remove redundant moves from VAESKeyGenAssist
Zero-extension will occur upon storing if necessary.

We can also join the AVX implementation with the SSE one.
2023-08-23 21:22:00 -04:00
Lioncache 410e99ba09 OpcodeDispatcher: Remove redundant moves from VPCLMULQDQOp
Zero-extension will occur if necessary upon storing.
2023-08-23 21:18:39 -04:00
Ryan Houdek 6e4765d48b Merge pull request #2989 from lioncash/ins
Arm64/ConversionOps: Remove redundant moves in AdvSIMD VInsGPR
2023-08-23 18:09:15 -07:00
Ryan Houdek 172c8f3ba6 Merge pull request #2988 from lioncash/half
Arm64/ConversionOps: Add missing half-precision conversions to scalar functions
2023-08-23 17:56:23 -07:00
Lioncache 203a2b1105 Arm64/ConversionOps: Remove redundant moves in AdvSIMD VInsGPR
If Dst and DestVector alias one another, then we don't need to
move the vector unnecessarily.
2023-08-23 20:50:21 -04:00
Ryan Houdek 4297e13fcf Merge pull request #2986 from lioncash/ext
Arm64/VectorOps: Remove redundant moves in SVE VExtr when possible
2023-08-23 17:40:50 -07:00
Ryan Houdek 5b8a0f1e0d Merge pull request #2987 from lioncash/shift
Arm64/VectorOps: Remove redundant moves from VSQXTN2/VSQXTUN2/VSQSHL/VSRSHR
2023-08-23 17:40:39 -07:00
Lioncache 5ad56ad52e Arm64/ConversionOps: Add missing half-precision operations to Float_FromGPR_S
Provides parity with vector operations.
2023-08-23 20:36:34 -04:00
Lioncache 24e7baf28f Arm64/ConversionOps: Add missing half-precision conversions to Float_FToF
Provides parity with the vector conversion operations.
2023-08-23 20:31:49 -04:00
Lioncache b248ae4c04 Arm64/VectorOps: Remove redundant moves from SVE SQSHL
We don't need to emit a move if the destination and source alias.
2023-08-23 20:12:09 -04:00
Lioncache 95bea864cf Arm64/VectorOps: Remove redundant moves from SVE SRSHR
We don't need to perform a move is the destination aliases
the source vector to be shifted.
2023-08-23 20:10:51 -04:00
Lioncache 47c4507bb6 Arm64/VectorOps: Remove redundant moves from SVE VSQXTUN2
We don't need to perform a move if the destination aliases the lower vector.
2023-08-23 20:10:29 -04:00
Lioncache 5ea0b6db28 Arm64/VectorOps: Remove redundant moves from SVE VSQXTN2
We don't need to perform a move if the destination aliases the
lower vector.
2023-08-23 20:02:28 -04:00
Mai ee10153d14 Merge pull request #2984 from Sonicadvance1/optimize_pack
OpcodeDispatcher: Use new IR ops for pack instructions
2023-08-23 20:02:16 -04:00
Lioncache d0d94adabe Arm64/VectorOps: Remove redundant moves in SVE VExtr when possible
We don't need to do any moves here is the destination aliases the
lower bits.
2023-08-23 19:56:10 -04:00
Ryan Houdek 926b8c2c97 Merge pull request #2985 from lioncash/shift
Arm64/VectorOps: Remove redundant moves from SVE variable/immediate/vector shifts when possible
2023-08-23 16:41:39 -07:00
Lioncache 18ebcdc9de Arm64/VectorOps: Remove redundant moves in VUshrNI2
If the destination and VectorLower alias, then we don't need
to emit a movprfx.
2023-08-23 18:52:32 -04:00
Lioncache f31a9a52e6 Arm64/VectorOps: Remove redundant moves from SVE immediate vector shifts when possible
If the destination and source vector alias one another, then the
operation can largely be done in place.
2023-08-23 18:36:21 -04:00
Lioncache 03504a5f8c Arm64/VectorOps: Remove redundant moves from SVE vector shifts when possible
If the destination and the vector to be shifted alias, then we can
avoid needing to move some data around.
2023-08-23 18:24:58 -04:00
Lioncache d29b4de1ee Arm64/VectorOps: Remove redundant moves from SVE variable vector register shifts when possible
In the event that the destination and the vector to be shifted
alias one another, then we can skip the movprfx, since it's not
necessary.
2023-08-23 18:24:53 -04:00
Ryan Houdek 5e20be756e InstCountCI: Update for pack instruction optimization 2023-08-23 15:16:54 -07:00
Ryan Houdek fc4559d3c4 OpcodeDispatcher: Use new IR ops for pack instructions
The MMX and SSE versions of these instructions are now optimal.
2023-08-23 15:14:38 -07:00
Ryan Houdek c508570da0 IR: Implements VSQXT{U,}NPair operations
This takes the two independent VSXT{U}N{2,} operations and merges them
in to a single IR operations.
In some cases this can result in a more optimal implementation since
there is no need for moves inbetween.
2023-08-23 15:13:07 -07:00
Ryan Houdek ec6548e302 Merge pull request #2983 from lioncash/bsl
Arm64/VectorOps: Remove redundant moves from SVE BSL when possible
2023-08-23 15:11:34 -07:00
Lioncache d5e145c4b0 Arm64/VectorOps: Remove redundant moves from SVE BSL when possible
If the destination and true vector alias one another, then we can
perform the operation in place instead of moving data around.
2023-08-23 17:54:10 -04:00
Ryan Houdek 350bca97c6 Merge pull request #2982 from lioncash/imin
Arm64/VectorOps: Remove redundant moves from SVE V{S,U}Min/V{S,U}Max when possible
2023-08-23 14:53:15 -07:00
Ryan Houdek 226405880f Merge pull request #2981 from lioncash/fmin
Arm64/VectorOps: Remove redundant moves from SVE VFMin/VFMax when possible
2023-08-23 14:46:03 -07:00
Lioncache 37a8cb6821 Arm64/VectorOps: Remove redundant moves from SVE VSMax when possible
When the destination and first source alias one another, then we
can perform the operation in place instead of moving data around.
2023-08-23 17:34:52 -04:00
Lioncache fe2c7dbf97 Arm64/VectorOps: Remove redundant moves from SVE VUMax when possible
When the destination and source alias one another, then we
can perform the operation in place without needing to move
data around.
2023-08-23 17:32:17 -04:00
Lioncache 787b4f37fb Arm64/VectorOps: Remove redundant moves from SVE VSMin when possible
When the destination and first source alias one another, then we can
perform the operation in place without moving any data.
2023-08-23 17:30:08 -04:00
Lioncache c3faa019f5 Arm64/VectorOps: Remove redundant moves from SVE VUMin when possible
If the destination and first source alias, then we can perfom the operation
in place.
2023-08-23 17:27:52 -04:00
Ryan Houdek da098d8204 Merge pull request #2979 from lioncash/div
Arm64/VectorOps: Remove moves from SVE VFDiv if possible
2023-08-23 14:22:46 -07:00
Lioncache 149852b122 Arm64/VectorOps: Remove redundant moves from SVE VFMax if possible
If Dst and Vector1 alias one another, then the operation can be
performed in place instead of moving data around.
2023-08-23 17:18:41 -04:00
Lioncache ecf02846e6 Arm64/VectorOps: Remove redundant moves from SVE VFMin is possible
If Dst and Vector1 alias one another, then we can do the merging move
in place instead of shuffling data around.
2023-08-23 17:16:25 -04:00
Ryan Houdek 2501ebc1cd Merge pull request #2980 from lioncash/avg
Arm64/VectorOps: Remove redundant moves from SVE VURAvg if possible
2023-08-23 14:05:51 -07:00
Lioncache a8f7529847 Arm64/VectorOps: Remove moves from SVE VFDiv if possible
Given the operation is:

Dst = Vector1 / Vector2

If Dst and Vector1 alias one another, then we can just perform
the division as is without any moving of data around.
2023-08-23 16:53:56 -04:00
Lioncache 8431ab43a0 Arm64/VectorOps: Remove redundant moves from SVE VURAvg if possible
If Dst and Vector1 alias one another, then we can perform the operation
without needing to move any data around.
2023-08-23 16:51:17 -04:00
Mai 4b06069c0d Merge pull request #2972 from Sonicadvance1/optimize_scalar_mov
OpcodeDispatcher: Optimizes scalar movd/movq
2023-08-23 16:30:45 -04:00
Ryan Houdek b646f4b781 Merge pull request #2978 from lioncash/misc
OpcodeDispatcher: Remove redundant moves from remaining AVX ops
2023-08-23 13:18:25 -07:00
Ryan Houdek 38853c2a9b InstCountCI: Update for vmovd/vmovq optimization 2023-08-23 12:56:11 -07:00
Ryan Houdek 8836ab8988 OpcodeDispatcher: Optimizes scalar movd/movq
MMX and SSE versions are now optimal.
2023-08-23 12:56:11 -07:00
Ryan Houdek a40526a541 Merge pull request #2977 from lioncash/pack
OpcodeDispatcher: Remove redundant moves from VPACKUSOP/VPACKSSOp
2023-08-23 12:33:05 -07:00
Lioncache ea9747289a OpcodeDispatcher: Remove redundant moves from remaining AVX ops
Zero-extension will occur automatically if necessary upon storing.
2023-08-23 15:31:59 -04:00
Lioncache 735e2060a3 OpcodeDispatcher: Remove redundant moves from VPACKUSOP/VPACKSSOp
Zero-extension will occur automatically if necessary.
2023-08-23 15:09:57 -04:00
Ryan Houdek 86ef6fe48d Merge pull request #2976 from lioncash/mov
OpcodeDispatcher: Remove unnecessary moves from AVX move ops where applicable
2023-08-23 12:02:05 -07:00
Lioncache 8e7e91d61f OpcodeDispatcher: Remove redundant moves in VMOVLPOp
Zero-extension will automatically occur upon storing if necessary.
2023-08-23 14:23:57 -04:00
Lioncache bcba3700c8 OpcodeDispatcher: Remove redundant moves from VMOVVectorNTOp
Zero-extension will automatically occur if necessary upon storing.

We can also join the SSE and AVX implementations.
2023-08-23 14:17:27 -04:00
Lioncache 7d05797e82 OpcodeDispatcher: Remove redundant moves from VMOVHPOp
Zero-extension will automatically occur if necessary upon storing.
2023-08-23 14:14:35 -04:00
Lioncache c409ea78bc OpcodeDispatcher: Remove unnecessary moves from VMOV{A,U}PS/VMOV{A,U}PD
Zero-extension will occur automatically upon storing if necessary.

We can also join the SSE and AVX implementations together.
2023-08-23 14:10:49 -04:00
Ryan Houdek a62ba75ede Merge pull request #2975 from lioncash/scalar
Arm64/ConversionOps: Add scalar support to Vector_FToI
2023-08-23 10:57:38 -07:00
Lioncache 4a7ef3da13 OpcodeDispatcher: Remove unnecessary moves in AVXVectorRound
Zero-extension will occur automatically if necessary upon storing.
2023-08-23 13:41:56 -04:00
Lioncache 990b70dcd6 OpcodeDispatcher: Use scalar rounding for scalar round instructions 2023-08-23 13:34:01 -04:00
Lioncache 393cea2e8b Arm64/ConversionOps: Correct AdvSIMD round-to-nearest Vector_FToI case
This was previously using frinti, which uses the host rounding mode, rather
than round to nearest.
2023-08-23 13:27:44 -04:00
Ryan Houdek 6624f50abf Merge pull request #2974 from lioncash/extend
OpcodeDispatcher: Remove unnecessary moves from AVXExtendVectorElements
2023-08-23 10:25:56 -07:00
Ryan Houdek cd1f401363 Merge pull request #2973 from lioncash/vfcmp
OpcodeDispatcher: Remove unnecessary moves in AVXVFCMPOp
2023-08-23 10:25:16 -07:00
Lioncache e89321dc60 Arm64/ConversionOps: Add scalar support to Vector_FToI
This can be used for the scalar conversions instead of always using the
vector variants.
2023-08-23 13:23:07 -04:00
Lioncache d99bcbf01b OpcodeDispatcher: Remove unnecessary moves from AVXExtendVectorElements
Zero-extension will already occur if necessary upon storing.

Also we can join the AVX and SSE implementations together and get
rid of some template instantiations, now that the only differing
behavior is removed.
2023-08-23 12:47:31 -04:00
Mai 0819338dbf Merge pull request #2970 from Sonicadvance1/optimize_pminmax
Arm64: Optimize VFMin/VFMax
2023-08-23 12:39:37 -04:00
Lioncache f516aed4b7 OpcodeDispatcher: Remove unnecessary moves in AVXVFCMPOp
Zero-extension will already occur if necessary upon storing.
2023-08-23 12:37:45 -04:00
Ryan Houdek 76430baf88 Merge pull request #2971 from lioncash/blend
OpcodeDispatcher: Remove redundant moves in AVX blend special cases
2023-08-22 21:42:07 -07:00
Lioncache 3858e4124b OpcodeDispatcher: Remove redundant moves in AVX blend special cases
Zero-extension will happen if necessary upon storing.
2023-08-23 00:08:33 -04:00
Mai 819fe110da Merge pull request #2967 from Sonicadvance1/optimize_storeelement
OpcodeDispatcher: Optimize MOVHP{S,D}
2023-08-23 00:01:25 -04:00
Ryan Houdek 5db5944ad2 InstCountCI: Update for min/max optimization 2023-08-22 21:00:27 -07:00
Ryan Houdek f0b1030e54 Arm64: Optimize VFMin/VFMax
We can be more optimal on the selects. This makes 3DNow! and SSE packed
min/max operations optimal.
2023-08-22 20:59:11 -07:00
Ryan Houdek adfd6787c0 Merge pull request #2969 from lioncash/insert
OpcodeDispatcher: Remove unnecessary moves from AVX inserts
2023-08-22 20:51:55 -07:00
Ryan Houdek b35ad8d8ed InstCountCI: Update for movhp{s,d} optimization 2023-08-22 20:42:24 -07:00
Ryan Houdek 0ee2579a5e OpcodeDispatcher: Optimize MOVHP{S,D}
Loads can turn in to element Loads.
Stores can turn in to element stores.

These four instruction variants are now optimal.
2023-08-22 20:42:24 -07:00
Ryan Houdek 6aa2cab41c IR: Implement support for vector store element
Matches ARM64 ST1 semantics
2023-08-22 20:42:24 -07:00
Mai bb2f7107cd Merge pull request #2963 from Sonicadvance1/optimize_loadelement
OpcodeDispatcher: Optimize MOVLP{S,D} loads
2023-08-22 23:41:59 -04:00
Lioncache c33f3ff8df OpcodeDispatcher: Remove unnecessary moves from AVX inserts
We already zero-extend on stores when necessary.
2023-08-22 23:29:40 -04:00
Ryan Houdek 5d44a445dd Merge pull request #2968 from lioncash/shift2
OpcodeDispatcher: Remove unnecessary moves from AVX register shifts
2023-08-22 20:23:12 -07:00
Ryan Houdek 519c670374 InstCountCI: Update for load element optimization
Adds movhlps special case which was missed before.
2023-08-22 20:15:16 -07:00
Ryan Houdek de239cde67 OpcodeDispatcher: Optimize MOVLP{S,D} loads
This now uses the new load element IR operation and makes these
instructions optimal.

LRPCPC3 will introduce instructions in the future for TSO emulation to
help these operations, but that doesn't exist today.
2023-08-22 20:15:16 -07:00
Ryan Houdek 5e57ec94cf IR: Implement support for vector load element
Matches Arm64 LD1 semantics.
2023-08-22 20:15:16 -07:00
Lioncache 2f5fae7677 OpcodeDispatcher: Remove unnecessary moves from AVX register shifts
Zero-extension will occur automatically when necessary upon storing.
2023-08-22 23:06:53 -04:00
Ryan Houdek fb65fb29c7 Merge pull request #2966 from lioncash/shift
OpcodeDispatcher: Remove redundant moves from AVX immediate shifts
2023-08-22 20:02:14 -07:00
Lioncache 8f8062eb4e OpcodeDispatcher: Remove redundant moves from AVX immediate shifts
These zero-extensions will occur automatically when applicable.
2023-08-22 22:50:10 -04:00
Ryan Houdek 36a54183f5 Merge pull request #2965 from lioncash/mov
OpcodeDispatcher: Remove unnecessary moves from AVX conversion operations
2023-08-22 19:38:46 -07:00
Lioncache e5f5629ffc OpcodeDispatcher: Remove unnecessary moves from AVX conversion operations
These zero-extensions will already happen automatically if necessary.
2023-08-22 22:20:13 -04:00
Ryan Houdek 4443c667ec Merge pull request #2964 from Sonicadvance1/missed_optimal
InstCountCI: Fix some mislabeled instructions
2023-08-22 19:05:14 -07:00
Ryan Houdek ead141fd90 Merge pull request #2962 from lioncash/variable
OpcodeDispatcher: Remove unnecessary moves from AVXVariableShiftImpl
2023-08-22 18:52:47 -07:00
Ryan Houdek 2c64523317 InstCountCI: Fix some mislabeled instructions
These were all optimal. Fixed now.
2023-08-22 18:47:17 -07:00
Lioncache 2b071e282e OpcodeDispatcher: Remove unnecessary moves from AVXVariableShiftImpl
We already zero-extend on a store if necessary.
2023-08-22 21:20:34 -04:00
Ryan Houdek 14144523f7 Merge pull request #2961 from lioncash/minpos
OpcodeDispatcher: Remove unnecessary move from VPHMINPOSUW
2023-08-22 18:19:46 -07:00
Ryan Houdek 1f2c5fc6c6 Merge pull request #2960 from lioncash/index
Arm64: Optimize SVE VInsElement
2023-08-22 18:19:13 -07:00
Mai 42200bf7b6 Merge pull request #2959 from Sonicadvance1/movlpd_store
X86Tables: Optimize MOVLPD stores
2023-08-22 21:02:23 -04:00
Lioncache fa17d9fae9 OpcodeDispatcher: Remove unnecessary move from VPHMINPOSUW
We already do a zero-extend if necessary in StoreResult.

This also lets us unify both the SSE and AVX handling code.
2023-08-22 20:59:56 -04:00
Lioncache 398a70312e Arm64: Optimize SVE VInsElement
This can be done without storing any data to memory and also
compressing the amount of instructions being used.

Thanks to @dougallj for the optimization suggestions.
2023-08-22 20:36:29 -04:00
Ryan Houdek 76afc653e8 InstCountCI: Update for movlpd store optimization 2023-08-22 17:34:15 -07:00
Ryan Houdek d3ed9766e8 X86Tables: Optimize MOVLPD stores
Just use the full register size and store the lower bits.
2023-08-22 17:33:34 -07:00
Ryan Houdek ed7f1b017d Merge pull request #2958 from Sonicadvance1/optimize_phminpos
OpcodeDispatcher: Optimize phminposuw
2023-08-22 16:58:27 -07:00
Ryan Houdek fb60f9e406 InstCountCI: Update for phminposuw optimization 2023-08-22 16:29:06 -07:00
Ryan Houdek c795d42d21 OpcodeDispatcher: Optimize phminposuw
I would now consider the XMM version of this to be optimal.

Thanks to @rygorous for giving the idea for how to optimize this!
2023-08-22 16:29:06 -07:00
Ryan Houdek bbf9cb9d52 IR: Implements new VRev32 and LoadNamedVectorConstant ops
VRev32 matches Arm64 semantics directly.
LoadNamedVectorConstant allows FEX to quickly load "named constants".
This will allow us to have specific hardcoded vector constant values
that we can load with a ldr(State)+ldr(Value) and will be more abused in
the future.
This also allows us to do a very simple optimization in the future where
we can optimize away redundant loads of these loads if they are used
multiple times in the same block. (Not implemented here).
2023-08-22 16:29:06 -07:00
Mai 6c7933e7b1 Merge pull request #2957 from Sonicadvance1/optimize_pfnacc
OpcodeDispatcher: Optimize PFNACC
2023-08-22 10:10:11 -04:00
Ryan Houdek 364f084604 Merge pull request #2956 from Sonicadvance1/optimize_hsubp
OpcodeDispatcher: Optimize hsubp
2023-08-21 20:47:50 -07:00
Ryan Houdek ffa8f1e3dc Merge pull request #2955 from lioncash/sign
OpcodeDispatcher: Remove redundant move from VPSIGN
2023-08-21 20:47:41 -07:00
Ryan Houdek be5d5b06f8 InstCountCI: Update for pfnacc 2023-08-21 20:38:25 -07:00
Ryan Houdek ad6738939b OpcodeDispatcher: Optimize PFNACC
Turns out this can be even more optimal.
2023-08-21 20:38:18 -07:00
Mai 9df94d8a93 Merge pull request #2954 from Sonicadvance1/optimize_pmuludq
OpcodeDispatcher: Optimize pmuludq
2023-08-21 23:33:45 -04:00
Ryan Houdek 9ae85b2251 InstCountCI: Update for hsubp 2023-08-21 20:24:37 -07:00
Ryan Houdek dcb3e4ee86 OpcodeDispatcher: Optimize hsubp
This makes the SSE version optimal.
This dramatically improves the AVX version as well.
2023-08-21 20:22:14 -07:00
Lioncache dbbe6288de OpcodeDispatcher: Remove redundant move from VPSIGN
StoreResult will already zero-extend if the vector is 128-bit.
2023-08-21 23:11:07 -04:00
Ryan Houdek de1f75f7a5 InstCountCI: Update for pmuludq 2023-08-21 20:08:19 -07:00
Ryan Houdek 1563398d2c OpcodeDispatcher: Optimize pmuludq
MMX version was already optimal, SSE version is now also.
AVX version is significantly improved.
2023-08-21 20:07:35 -07:00
Ryan Houdek 71984fc0ea Merge pull request #2953 from lioncash/scalar
OpcodeDispatcher: Remove redundant move in AVXVectorScalarALUOpImpl
2023-08-21 19:51:23 -07:00
Lioncache 920a0fb132 OpcodeDispatcher: Remove redundant move in AVXVectorScalarALUOpImpl
Our store will already zero-extend if the vector is 128-bit.
2023-08-21 22:39:07 -04:00
Ryan Houdek 3c88671cca Merge pull request #2952 from lioncash/alu
OpcodeDispatcher: Remove redundant moves in AVXVectorALUOp
2023-08-21 19:26:22 -07:00
Lioncache ce8169794f OpcodeDispatcher: Remove redundant moves in AVXVectorALUOp
We already zero-extend on a store if we have 256-bit vectors and the stored
vector is 128-bit.
2023-08-21 22:13:29 -04:00
Mai 185e3bfcb6 Merge pull request #2950 from Sonicadvance1/optimize_pmaddwd
OpcodeDispatcher: Optimize pmaddwd
2023-08-21 21:39:30 -04:00
Mai 3c49b3238a Merge pull request #2949 from Sonicadvance1/optimize_phsub
OpcodeDispatcher: Optimize phsub
2023-08-21 21:39:02 -04:00
Ryan Houdek 2d7a3a578e Merge pull request #2931 from Sonicadvance1/optimize_psign
Optimize PSIGN and VBSL
2023-08-21 18:30:55 -07:00
Ryan Houdek 3a2a576c35 Merge pull request #2951 from lioncash/shift
OpcodeDispatcher: Handle zero immediate shifts better
2023-08-21 18:20:18 -07:00
Ryan Houdek fe4de26250 InstCountCI: Updates tests for pmaddwd optimization
MMX and SSE implementations are now optimal.
2023-08-21 17:55:20 -07:00
Ryan Houdek 869136b907 OpcodeDispatcher: Optimize pmaddwd
This is actually fairly trivial looking at it.
2023-08-21 17:53:32 -07:00
Lioncache af8b6766d8 OpcodeDispatcher: Handle zero immediate shifts better
In the SSE and lower cases, we don't need to do anything,
since the value is already in the destination.
2023-08-21 20:46:41 -04:00
Ryan Houdek d283d1ba11 InstCountCI: Update for phsub optimization
MMX and SSE now optimal
2023-08-21 17:38:24 -07:00
Ryan Houdek 30e9beba51 JIT64: Fixes i32v2 unzips
This has just been broken since it was implemented. Turns out we had
never used these with XMM operations before today.
2023-08-21 17:38:24 -07:00
Ryan Houdek 8ee6262e5e Merge pull request #2946 from Sonicadvance1/optimize_mpsad
OpcodeDispatcher: Optimizes mpsadbw
2023-08-21 17:33:03 -07:00
Ryan Houdek 637a5d3b18 InstCountCI: Updates results from mpsadbw optimization
Pretty sure the SSE versions are optimal implementations.
The AVX versions still have spurious moves that can probably get
removed.
2023-08-21 16:57:13 -07:00
Ryan Houdek a29076244f OpcodeDispatcher: Optimizes mpsadbw
Two optimizations here:
1) The final VInsElement was generating three instructions
   - This itself could have been change to vzip, which would have
     removed two instructions.
2) Optimize how the pairwise elements are calculated to shave one
   instruction off the calculation.
   - addp odd elements and even elemnts first
   - Then transpose those elements
   - Then use one final addp to generate the result in the correct
     order.

The ext+uabdl+addp pairs of operations could be reordered to shave off
one temporary register usage if we really care later.
2023-08-21 16:56:41 -07:00
Ryan Houdek 445c43792b OpcodeDispatcher: Optimize phsub
This was...surprisingly bad. I blame myself entirely.
2023-08-21 16:54:35 -07:00
Mai f4f9b20f32 Merge pull request #2948 from Sonicadvance1/fix_newline
InstCountCI: Add newline to end of file
2023-08-21 19:50:53 -04:00
Mai 34a7feffe8 Merge pull request #2947 from Sonicadvance1/optimize_pmaddubsw
OpcodeDispatcher: Optimize SSE/AVX pmaddubsw
2023-08-21 19:50:10 -04:00
Ryan Houdek b559982515 InstCountCI: Update for newlines 2023-08-21 16:26:46 -07:00
Ryan Houdek ca96a25a7a InstCountCI: Add newline to end of file
This way these endlines don't constantly keep getting toggled.
2023-08-21 16:26:20 -07:00
Ryan Houdek 3f82c8cfe5 InstCountCI: Update for pmaddubsw
Not quite optimal but a heck of a lot better.
2023-08-21 16:22:00 -07:00
Ryan Houdek b35f4df798 OpcodeDispatcher: Optimize SSE pmaddubsw
Can be slightly more optimal with a slightly change algorithm but will
require implementing some IR ops which can be put off. It's only about
an instruction savings.
2023-08-21 16:21:01 -07:00
Ryan Houdek e765bd8986 Merge pull request #2945 from lioncash/interpdata
Interpreter: Tie SSA data elements to supported vector width
2023-08-21 11:52:01 -07:00
Lioncache f1d020ce95 Interpreter: Tie SSA data elements to supported vector width
Now, if we ever increase our vector sizes, the allocated data elements
will follow suit without needing to remember to handle this as well.
2023-08-21 14:41:00 -04:00
Ryan Houdek 8b9ee997b2 Merge pull request #2944 from lioncash/interparray
Interpreter: Use alias for temporary vector data
2023-08-21 11:28:24 -07:00
Lioncache a9a7cbce21 Interpreter: Use alias for temporary vector data
Lets us extract the size into one location for easy size
changes in the future if necessary.
2023-08-21 14:12:55 -04:00
Ryan Houdek 084d102c9c Merge pull request #2943 from Sonicadvance1/wide_shifts
IR: Implements support for wide scalar shifts
2023-08-21 09:14:38 -07:00
Ryan Houdek 214dad25b5 InstCountCI: Updates instruction tables for wide shifts
Even on platforms without SVE these have improved slightly but the real
improvement comes from SVE.

Adds some new InstCountCI files for SVE128 enabled testing.

Also enables SVE128 in the VEX maps. Host features should probably
enable SVE128 when SVE256/AVX is enabled, but that isn't the case today.
2023-08-20 19:19:15 -07:00
Ryan Houdek a3bf952f2b IR: Implements support for wide scalar shifts
This matches x86 vector shift behaviour closely for ps{rl,ra,ll}{w,d,q}
where the vector is shifted by a scalar value that is 64-bits wide.
Anything larger than the element size will set that element to zero.

With SVE we have some new wide element shifts that match this behaviour
exactly (except supports wide shift sources rather than scalar).

This is a significant improvement even on platforms that only support
128-bit SVE.
2023-08-20 19:16:40 -07:00
Mai 5cc30bdf10 Merge pull request #2942 from Sonicadvance1/fix_nontelemetry_compilation
FEXInterpeter: Fixes compilation when telemetry is disabled
2023-08-20 17:16:13 -04:00
Ryan Houdek c3b4bfbc8d FEXInterpeter: Fixes compilation when telemetry is disabled
Oops, this has been broken for a while now.
2023-08-20 13:50:33 -07:00
Ryan Houdek b39e8ed5f0 InstCountCI: Update for improvements to psign/vbsl
Quite a few improvements here.
2023-08-20 13:46:01 -07:00
Ryan Houdek ac77986c44 IR: Implements support for saturating/rounding vector shifts 2023-08-20 13:38:23 -07:00
Ryan Houdek c5f5a03c68 OpcodeDispatcher: Optimize PSIGN
This dramatically improves the performance of the PSIGN instructions.
2023-08-20 13:38:23 -07:00
Ryan Houdek 1ed9ec63be Arm64: Optimize BSL when possible.
With ASIMD FEX would never optimize BSL out of fear if some registers
overlapped it would break things. So it had previously always moved to a
temporary first and then moved the result back out when done.

Now instead check upfront if any of the source registers overlap the
destination. If the destination register overlaps any of the three
sources we can bsl, bit, or bif depending on which register gets
overlapped.

Worst case the destination doesn't overlap any of the source registers
and still needs these moves.
2023-08-20 13:38:23 -07:00
Ryan Houdek a523858f66 Merge pull request #2923 from Sonicadvance1/nonnull_legacy_segment_telemetry
FEXCore: Adds telemetry around legacy segment register setting
2023-08-20 10:27:56 -07:00
Ryan Houdek 9e4888c6a1 Merge pull request #2930 from Sonicadvance1/support_push
OpcodeDispatcher: Implement support for push IR operation
2023-08-20 10:25:26 -07:00
Ryan Houdek 277345d2a5 Merge pull request #2941 from lioncash/sha1
OpcodeDispatcher: Improve SHA1MSG1 output
2023-08-20 10:18:10 -07:00
Lioncache 8167626a07 x86_64/VectorOps: Properly handle VExtr element sizes other than bytes
We need to convert the index into a byte index.
2023-08-20 13:03:27 -04:00
Lioncache c3778a9729 OpcodeDispatcher: Improve SHA1MSG1 output
We can simplify these inserts down to a single EXT
2023-08-20 12:50:07 -04:00
Ryan Houdek 34722348e8 Merge pull request #2912 from Sonicadvance1/optimize_flag_clearing
OpcodeDispatcher: Minor optimization around clearing flags
2023-08-19 23:03:40 -07:00
Ryan Houdek 4286d44e92 Merge pull request #2940 from lioncash/crypto
Arm64/EncryptionOps: Use MOVI reg, #0 to zero vectors
2023-08-19 20:58:35 -07:00
Lioncache 1fbf193739 Arm64/EncryptionOps: Use MOVI reg, #0 to zero vectors
This is a little more optimal than XORing the vector by itself.
2023-08-19 23:20:36 -04:00
Ryan Houdek 8302ef8c22 InstCountCI: Update changed instructions due to flag clearing improvements
Some instructions reordered, but a bunch of operations had their number of instructions reduced as well
2023-08-19 20:19:39 -07:00
Ryan Houdek 92c3014aaa OpcodeDispatcher: Minor optimization around clearing flags
When clearing multiple flags it is more optimal to load the mask
constant in to a register and then clear with a single and/bic.

Back to back bfi is actually less optimal due to dependency tracking.

With #2911, this is a total win since this hits an edge case with
constant loading that #2911 fixes.
2023-08-19 20:14:37 -07:00
Mai 6960fca256 Merge pull request #2929 from Sonicadvance1/signaldelegator_getconfig
SignalDelegator: Allow getting the internal configuration
2023-08-19 23:12:06 -04:00
Mai affbcd2241 Merge pull request #2928 from Sonicadvance1/remove_x18_saving
Arm64Emitter: Stop saving and restoring platform register
2023-08-19 23:11:44 -04:00
Mai a2e5c231ae Merge pull request #2908 from Sonicadvance1/optimize_stc_clc
IR/ConstProp: Ensure that BFI with constant bitfields can optimize to Andn or Or
2023-08-19 23:10:48 -04:00
Ryan Houdek b973c193be Merge pull request #2939 from lioncash/round
OpcodeDispatcher: Eliminate redundant moves in {AVX}VectorRound
2023-08-19 19:29:01 -07:00
Ryan Houdek 4c409ea47d Merge pull request #2938 from lioncash/fcmp
OpcodeDispatcher: Eliminate unnecessary moves in {AVX}VFCMPOp
2023-08-19 19:27:24 -07:00
Ryan Houdek ac53913c37 Merge pull request #2937 from lioncash/scalarunary
OpcodeDispatcher: Remove unnecessary moves in {AVX}VectorUnaryOp
2023-08-19 19:22:10 -07:00
Ryan Houdek 2224c23c79 Merge pull request #2934 from lioncash/scalarfp
OpcodeDispatcher: Remove extraneous moves in {V}CVTSD2SS/{V}CVTSS2SD
2023-08-19 19:20:20 -07:00
Ryan Houdek 6ce380d7a9 Merge pull request #2936 from lioncash/scalaralu
OpcodeDispatcher: Remove unnecessary moves in {AVX}VectorScalarALUOp
2023-08-19 19:09:06 -07:00
Lioncache 5152854b98 OpcodeDispatcher: Remove extraneous moves in {V}CVTSD2SS/{V}CVTSS2SD
Since all we're going to be doing is an insert as the final operation,
in the cases where our source is a vector, we can specify the size of
the vector rather than the size of the element to avoid doing unnecessary
zero-extending.
2023-08-19 22:07:25 -04:00
Ryan Houdek 8dade7eea1 Merge pull request #2935 from lioncash/scalarfp2
OpcodeDispatcher: Remove redundant moves from {V}CVTSD2SI/{V}CVTSS2SI
2023-08-19 19:05:52 -07:00
Ryan Houdek add5baeba5 Merge pull request #2933 from lioncash/movss
OpcodeDispatcher: Remove some extraneous MOVs from VMOVSD/VMOVSS
2023-08-19 19:03:24 -07:00
Lioncache 6ba42e5cf1 OpcodeDispatcher: Eliminate redundant moves in {AVX}VectorRound
When dealing with scalar source registers, we can opt to not zero-extend
the vector and just perform the scalar operation and then insert the result.
2023-08-19 21:27:59 -04:00
Lioncache 343b00818d OpcodeDispatcher: Eliminate unnecessary moves in {AVX}VFCMPOp
We dealing with scalar vector sources, we don't need to zero-extend
the vector, and we can just use it as is.
2023-08-19 21:20:03 -04:00
Lioncache 09addb217a OpcodeDispatcher: Remove unnecessary moves in {AVX}VectorUnaryOp
When dealing with source vectors, we can use the vector length
rather than using a smaller size and zero extending the register,
especially since the resulting value is just inserted into another
vector.
2023-08-19 20:09:19 -04:00
Lioncache 4d1f002dea OpcodeDispatcher: Remove unnecessary moves in AVXVectorScalarALUOp
Same thing as the SSE variant, but for AVX.
2023-08-19 19:22:42 -04:00
Lioncache 1158ad7b2a OpcodeDispatcher: Remove unnecessary moves in VectorScalarALUOp
We can explicitly specify the vector width when working with a
vector source, so that we don't do any unnecessary zero-extending
on the element.
2023-08-19 19:15:13 -04:00
Lioncache 6907fdca6b OpcodeDispatcher: Remove redundant moves from {V}CVTSD2SI/{V}CVTSS2SI
We can specify the full vector length when dealing with a source vector
to avoid zero-extending the vector unnecessarily. When dealing with a
memory operand, however, we only want to load the exact source size.
2023-08-19 18:46:08 -04:00
Lioncache c31329609f OpcodeDispatcher: Unify handling code for MOVSD and MOVSS
These have the same behavior and only differ based on element size,
so we can join the implementations together instead of duplicating
them across both functions.
2023-08-19 17:44:45 -04:00
Lioncache 1fe8470933 OpcodeDispatcher: Remove extraneous moves from VMOVSS/VMOVSD xmm to mem case
Like the changes made to the xmm to xmm case, since we're going to be storing
a 64-bit value, we don't directly need to zero-extend the vector on a load.
2023-08-19 17:35:52 -04:00
Lioncache 99b5aaa426 OpcodeDispatcher: Remove extraneous moves in VMOVSS/VMOVSD register case
In the event that we have a full length vector, we can just load and move
from it, which gets rid of a little bit of mov noise. Since all we intend
to do is perform an insert from one vector into another, we don't need the
zero-extending behavior that an 64-bit vector load would do.
2023-08-19 17:35:12 -04:00
Ryan Houdek db60a2fd4b Merge pull request #2932 from lioncash/perm
OpcodeDispatcher: Handle broadcasting cases in VPERMQ/VPERMPD
2023-08-18 22:48:30 -07:00
Lioncache 84f228a75a x86_64/VectorOps: Simplify index handling in VInsElement
While we're in the area, we can simplify these cases down a little.
2023-08-19 01:28:45 -04:00
Lioncache 83a330b039 x86_64/VectorOps: Fix insertion bugs in VDupElement for 256-bit
Previously we weren't hitting this because we were never broadcasting
from the upper lane with VDupElement.
2023-08-19 01:21:12 -04:00
Lioncache bbed4d73ed OpcodeDispatcher: Improve VPERMQ/VPERMPD broadcast cases
For a bunch of cases that act as broadcasts (where all
indices in the imm8 specify the same element), we
can use VDupElement here rather than iterating through.
2023-08-19 01:08:30 -04:00
Ryan Houdek a7e81ca731 InstCountCI: Update for push optimization
Some of these instructions improved a decent amount. Some even ending up
as being optimal.
2023-08-18 14:28:03 -07:00
Ryan Houdek 8b051b5e63 OpcodeDispatcher: Implement support for push IR operation
This paves the way to optimizing pushes in to both push operations and
push pair operations to more optimally match Arm64 push support.

While this does the first step for supporting the base push, we'll leave
optimizing push pairs to future work.
2023-08-18 14:19:16 -07:00
Ryan Houdek 1fdc4d2c62 IR: Implement support for a push operation
This is a bit of tricky operation where due to our our usage of SSA, the
incoming source isn't guaranteed to end its live-range at this
instruction.

This gives us a behaviour where to be optimal we need to take different
paths depending on if the incoming address register is the same as the
destination node.
Once we have form of RA constraints or non-SSA IR form that can
guarantee this restriction then this will go away.
2023-08-18 14:14:38 -07:00
Ryan Houdek ea5c67da80 SignalDelegator: Allow getting the internal configuration
Not used by FEX today but will be used by the WINE integration.
2023-08-18 11:56:52 -07:00
Ryan Houdek 0373826f46 Arm64Emitter: Stop saving and restoring platform register
FEX doesn't use the platform register on wine platforms so there is no
reason to save  and restore it.

On Linux we can still use it at some point but for now it isn't part of
our RA.
2023-08-18 11:49:44 -07:00
Ryan Houdek f912715df3 InstCountCI: STC and CLC is now optimal
Interestingly the segment register move instructions were previously
considered optimal on accident. They are actually optimal now which is
funny.
2023-08-18 11:41:21 -07:00
Ryan Houdek 7db2e487c3 IR/ConstProp: Remove some UBSAN behaviour
Changes the idiom used for constant mask generation to a ternary.
This pattern is definitely used elsewhere in code but we can get rid of
all instances here.
2023-08-18 11:41:11 -07:00
Ryan Houdek 6e5111b876 IR/ConstProp: Ensure that BFI with constant bitfields can optimize to Andn or Or
This optimizes the clc and stc instructions for flag setting and
clearing.
2023-08-18 11:33:11 -07:00
Ryan Houdek d502ad63f4 IR/ConstProp: Ensure ANDN is optimized 2023-08-18 11:27:06 -07:00
Ryan Houdek fc84f6b345 Merge pull request #2927 from bylaws/interrupt
FEXCore: Allow for interrupting the JIT on block entry
2023-08-18 06:14:24 -07:00
Billy Laws de63fd05d0 FEXCore: Allow for interrupting the JIT on block entry
This takes a similar approach to deferred signal handling and allows any given
thread to be interrupted while running JIT code by protecting the appropriate
page as RO. When the thread then enters a new block, it will try to acccess
that page and segfault. This is safer than just sending a signal to the thread
as that could stop in a place where JIT context couldn't be recovered correctly.
2023-08-18 05:58:51 -07:00
Ryan Houdek d3f0c7e969 Merge pull request #2925 from bylaws/winfile
Support for Config.json loading on WIN32
2023-08-18 05:00:22 -07:00
Ryan Houdek f1aa6208eb Merge pull request #2926 from bylaws/mingw
CMake: Add mingw toolchain file
2023-08-18 05:00:09 -07:00
Ryan Houdek b4d172649e Merge pull request #2924 from bylaws/logs
LogMan: Commonise log level to string conversion
2023-08-18 04:57:15 -07:00
Billy Laws 00556023c2 Remove unnecessary WIN32 file handling TODOs
With WOW, all allocations from 64-bit code use the full address space
and limiting is handled on the syscall thunk side so theres need to
worry about STL allocations stealing AS.
2023-08-18 04:37:40 -07:00
Billy Laws 5de0714766 FileLoading: Fix handling of non-existent files on WIN32 2023-08-18 04:37:40 -07:00
Billy Laws b862203491 Config: Add windows config loading support
This relies on wine's behaviour passing through linux paths and env vars,
so that the config in the user's home directory can be accessed outside
of the wine prefix.
2023-08-18 04:37:40 -07:00
Billy Laws bbfd15f801 LogMan: Commonise log level to string conversion 2023-08-18 04:36:31 -07:00
Billy Laws 0954c7eb9f AllocatorHooks: Add C++17 aligned new/delete functions 2023-08-18 04:32:16 -07:00
Billy Laws 8b2be809ff CI: Update to use mingw toolchain file 2023-08-18 04:31:41 -07:00
Billy Laws 193157812f CMake: Add mingw toolchain file 2023-08-18 04:31:41 -07:00
Ryan Houdek f09d9af3db Merge pull request #2922 from lioncash/psrld
OpcodeDispatcher: Improve {V}PSRLDQ shift by 0
2023-08-17 17:05:35 -07:00
Ryan Houdek d19e2507e5 FEXCore: Adds telemetry around legacy segment register setting
Due to Intel dropping support for legacy segment registers[1] there is a
concern that this will break legacy 32-bit software that is doing some
magic segment register handling.

Adds some simple telemetry for 32-bit applications that when they
encounter an instruction that sets the segment register or uses a
segment register that the JIT will do a /relatively/ quick four
instruction check to see if it is not a null segment.

It's not enough to just check if the segment index is 0 or not, 32-bit
Linux software starts with non-zero segment register indexes but the LDT
for each segment index is a null-descriptor.

Once the segment address is loaded, the IR operation will do a quick
check against zero and if it /isn't/ zero then set the telemetry value.

A very minor optimization that segment registers only get checked once
per block to ensure overhead stays low.

[1] https://www.intel.com/content/www/us/en/developer/articles/technical/envisioning-future-simplified-architecture.html
   - 3.6 - Restricted Subset of Segmentation
      - `Bases are supported for FS, GS, GDT, IDT, LDT, and TSS
        registers; the base for CS, DS, ES, and SS is ignored for 32-bit
        mode, same as 64-bit mode (treated as zero).`
   - 4.2.17 - MOV to Segment Register
      - Will fault if SS is written (Breaking anything that writes to
        SS).
      - Will not fault if CS, DS, ES are written (Thus it sets the
        segment but gets ignored due to 3.6).
2023-08-17 17:00:41 -07:00
Lioncache 9e54ec2724 OpcodeDispatcher: Improve {V}PSRLDQ shift by 0
While it would be bizarre if this actually occurred frequently
in practice, we can still tune it so there's no subpar assembly
output in the cases it actually does happen.
2023-08-17 19:33:09 -04:00
Ryan Houdek 461ca6fe7c Merge pull request #2921 from lioncash/shift
OpcodeDispatcher: Remove unnecessary conditionals in {V}PSLLIOp
2023-08-17 16:03:16 -07:00
Lioncache 5a1f32c339 OpcodeDispatcher: Remove unnecessary conditionals in {V}PSLLIOp
PSLLIImpl already checks for and handles a shift value of zero.
2023-08-17 18:47:50 -04:00
Ryan Houdek a4a68b47ce Merge pull request #2920 from lioncash/ddup
OpcodeDispatcher: Improve VMOVDDUP output
2023-08-17 15:47:06 -07:00
Lioncache 01515cea2c OpcodeDispatcher: Improve VMOVDDUP output
We can make use of TRN1 here to collapse a bunch of these moves.
2023-08-17 18:30:44 -04:00
Ryan Houdek f70b6f37a2 Merge pull request #2919 from lioncash/sldup
OpcodeDispatcher: Improve output of {V}MOVSLDUP/{V}MOVSHDUP
2023-08-17 14:49:09 -07:00
Lioncache 764c844225 OpcodeDispatcher: Improve output of {V}MOVSHDUP 2023-08-17 17:01:50 -04:00
Lioncache 31719aac6a OpcodeDispatcher: Improve output of {V}MOVSLDUP 2023-08-17 17:01:46 -04:00
Ryan Houdek c9856daaee Merge pull request #2891 from alyssarosenzweig/move-fexcore
Move External/FEXCore/ to FEXCore/
2023-08-17 13:56:07 -07:00
Alyssa Rosenzweig af21b8f3c7 Move External/FEXCore/ to FEXCore/
It is not an external component, and it makes paths needlessly long.
Ryan seemed amenable to this when we discussed on IRC earlier.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-17 16:32:16 -04:00
Ryan Houdek 425b0347a9 Merge pull request #2918 from lioncash/quad
IR: Allow 128-bit broadcasts in VBroadcastFromMem
2023-08-17 11:32:04 -07:00
Lioncache e14e71aaff IR: Allow 128-bit broadcasts in VBroadcastFromMem
Now all vbroadcast implementations go down the more optimal path.

For non-SVE 128-bit cases where we only have 128-bit wide registers,
we behave like ld1rqb and just act as a normal 128-bit load for
interface convenience.
2023-08-17 14:20:12 -04:00
Ryan Houdek a9dea29f03 Merge pull request #2917 from lioncash/quad
ARMEmitter: Handle SVE load and broadcast quadword groups
2023-08-17 10:59:27 -07:00
Lioncache f97df2a40f ARMEmitter: Handle SVE load and broadcast quadword (scalar plus scalar) group 2023-08-17 13:32:20 -04:00
Lioncache 6f9bc1e2fe ARMEmitter: Handle SVE load and broadcast quadword (scalar plus imm) category 2023-08-17 13:32:17 -04:00
Ryan Houdek 49b8b7cd2c Merge pull request #2916 from Sonicadvance1/128bit_predicate
Arm64Emitter: Ensure that 128-bit predicate is generated with SVE
2023-08-17 09:56:56 -07:00
Ryan Houdek cfc6368064 Merge pull request #2914 from lioncash/broad
IR: Add VBroadcastFromMem opcode
2023-08-17 09:45:11 -07:00
Ryan Houdek 059c022255 Arm64Emitter: Ensure that 128-bit predicate is generated with SVE
In the case of running on a 128-bit SVE system this predicate wasn't
setup. Since we never had any predicate usage before this wasn't an
issue. Now that #2914 is using the 128-bit predicate we need to make
sure that we are generating it.
2023-08-17 09:37:55 -07:00
Lioncache 25708be807 IR: Add TSO handling to VBroadcastFromMem 2023-08-17 12:30:57 -04:00
Lioncache c8e3ca481f OpcodeDispatcher: Remove explicit zero-extending in VBROADCASTOp
Since the implementations zero the upper lanes when appropriate, we can
remove the unnecessary explicit move.
2023-08-17 12:17:24 -04:00
Lioncache 879bc5176e IR: Add VBroadcastFromMem opcode
Allows the implementations of the vbroadcast instructions to perform the
load and broadcast in one operation as opposed to doing the load and then
broadcast separately.

Notably, the broadcasting loads can also be used on systems that have SVE 128-bit
support as well, not only 256-bit.

On non-SVE systems, we use the equivalent AdvSIMD instructions.
2023-08-17 12:17:24 -04:00
Ryan Houdek 6d562f8b3b Merge pull request #2911 from Sonicadvance1/stop_abusing_orr
Arm64: Stop abusing orr in LoadConstant
2023-08-17 09:13:36 -07:00
Ryan Houdek 1029bb1fae Merge pull request #2910 from Sonicadvance1/minor_bfi_opt
Arm64: Optimize non-optimal BFI move case
2023-08-17 09:12:37 -07:00
Ryan Houdek 1343c14db0 Merge pull request #2909 from Sonicadvance1/optimize_clzero_clear
Arm64: Optimize CacheLine{Clear,Clean}
2023-08-17 09:10:45 -07:00
Ryan Houdek 6580796169 Merge pull request #2915 from lioncash/unused
OpcodeDispatcher: Remove unused variable in AVXVectorUnaryOpImpl
2023-08-17 08:44:18 -07:00
Lioncache 77c64285cb OpcodeDispatcher: Remove unused variable in AVXVectorUnaryOpImpl
Forgot to remove this when getting rid of the unnecessary
explicit zero-extending behavior
2023-08-17 11:26:41 -04:00
Ryan Houdek 9f3730f251 Merge pull request #2913 from Sonicadvance1/string_view_self
Filemanagement: Optimize GetSelf using a string_view
2023-08-17 06:32:51 -07:00
Ryan Houdek 2be8e22e14 Filemanagement: Optimize GetSelf using a string_view
Instead of allocating a temporary copy of the string, return a view of
it instead. Should improve the performance of system calls that take
file paths. Since it was allocating a string for every single syscall
that uses them in this case.
2023-08-16 21:39:05 -07:00
Ryan Houdek fe37c89109 FEXCore/Config: Stop making temporary string copies
For config values that were string objects we were unnecessary creating
copies each time the string was accessed.

Convert the () operator over to returning a reference.
2023-08-16 21:35:13 -07:00
Ryan Houdek d11563f48f InstCountCI: Update instructions for data movement
Instruction counts don't change at all here, just the instructions being
used.
2023-08-16 19:36:12 -07:00
Ryan Houdek 23fd79a3b3 Arm64: Stop abusing orr in LoadConstant
The current implementation uses orr excessively. This has FEX missing
hardware optimization opportunities where some CPU cores will zero-cycle
move constants that fit in to the 16-bits of movz/movk.

First evaluate up front if the number of 16-bit segments is > 1, in
those cases we should check if it is a bitfield that can be moved in one
instruction with orr.

After that point we will use movz for 16-bit constant moves.

Additionally this optimizes the case where a constant of zero is loaded
to be a `mov <reg>, zr` which gets renamed in most hardware.
2023-08-16 19:35:15 -07:00
Ryan Houdek 13a4009bd3 InstCountCI: Update changed operations due to bfi operation
No instruction count changes here, just moving from lsr to mov.
2023-08-16 14:37:36 -07:00
Ryan Houdek a3b40c37c2 Arm64: Optimize non-optimal BFI move case
Commonly we are doing a BFI into a 32-bit register, which is hitting the
ubfx (lsr alias) path.

In the case of 32-bit destination we can also do a regular move, which
will take advantage of CPU's rename functionality and give a minor speed
boost.
2023-08-16 14:35:41 -07:00
Ryan Houdek 6cb0f52e94 Merge pull request #2907 from Sonicadvance1/fix_dumpir_bug
FEXCore/IR: Fixes bug in IRDumper without specification
2023-08-16 14:24:33 -07:00
Ryan Houdek f283ba4b05 InstCountCI: clwb/clfush/clflushopt is now optimal
One or two instructions depending.
2023-08-16 14:20:22 -07:00
Ryan Houdek 4522a766e0 Arm64: Optimize CacheLine{Clear,Clean}
When the cacheline size matches the expected x86 cacheline size then we
can remove the spurious move + add.
2023-08-16 14:20:22 -07:00
Ryan Houdek dc0cf98a81 Merge pull request #2898 from Sonicadvance1/instcount_asm
InstCountCI: Support encoding expected Arm64 ASM in JSON
2023-08-16 13:51:43 -07:00
Ryan Houdek fc12958095 FEXCore/IR: Fixes bug in IRDumper without specification
Didn't notice this in the previous PR, When DUMPIR=stderr without and
selection of where to place it in PASSMANAGERDUMPIR it was supposed to
put the dumper at the end of the passes.

We need to make sure that it it placed at the end of the passes rather
than current `it`.
2023-08-16 13:51:03 -07:00
Ryan Houdek fd40e058e8 InstCountCI: Update tests with inline asm
This will result in a decent amount of data churn but it's all
automated so it is a non-issue.
2023-08-16 13:38:45 -07:00
Ryan Houdek 8706f895d0 InstCountCI: Sanitize out vixl address calculations 2023-08-16 13:38:45 -07:00
Ryan Houdek 9753ebdebc InstCountCI: Support encoding expected Arm64 ASM in JSON
This will allow investigating the Arm64 directly next to the test, plus
publicly linking directly to badly behaving tests.

Perfect for nerdsniping implementations.
2023-08-16 01:50:06 -07:00
Mai f95ef7092c Merge pull request #2905 from Sonicadvance1/instcountci_updatetests
InstCountCI: Update tests from actual ARM64 device
2023-08-16 04:13:05 -04:00
Ryan Houdek d4d5bc9635 InstCountCI: Update tests from actual ARM64 device
Previous numbers were from the simulator which were slightly different.
2023-08-15 14:39:37 -07:00
Mai df3d4efc80 Merge pull request #2904 from Sonicadvance1/instcountci_only_arm
GIthub: Only enable InstCountCI on an ARM platform
2023-08-15 17:33:10 -04:00
Mai dac220a6ff Merge pull request #2903 from Sonicadvance1/instcountci_rng
InstCountCI: Adds RNG support
2023-08-15 17:21:03 -04:00
Ryan Houdek 9ba7f2fd0e InstCountCI: Fixes instcountci_tests depends 2023-08-15 14:14:24 -07:00
Ryan Houdek 1441cb76b9 HostFeatures: Adds support for overriding ARMv8.1 LSE atomics
Always enable it on the InstCountCI.
2023-08-15 14:12:27 -07:00
Ryan Houdek 2d20513e34 GIthub: Only enable InstCountCI on an ARM platform
simulator generates some instruction count differences that we don't
care about. Just run this on ARM platforms only instead.
2023-08-15 14:12:27 -07:00
Mai da334fe7d5 Merge pull request #2901 from Sonicadvance1/instcountci_only_disassembler
InstCountCI: Disables tests with unsupported configurations
2023-08-15 16:01:47 -04:00
Ryan Houdek 04f1f073c8 InstCountCI: Adds RNG support
Some instructions in the SecondaryGroup need RNG support for testing.
2023-08-15 12:58:53 -07:00
Ryan Houdek 0e52158cef Merge pull request #2902 from lioncash/unary
OpcodeDispatcher: Eliminate unnecessary moves in AVXVectorUnaryOpImpl
2023-08-15 12:54:47 -07:00
Lioncache 17956eac5f OpcodeDispatcher: Eliminate unnecessary moves in AVXVectorUnaryOpImpl
We no longer need to do any manual zero-extending here, since this
will occur automatically on hardware with SVE when 128-bit AdvSIMD
is used.
2023-08-15 15:43:34 -04:00
Ryan Houdek 6ad053a1e6 Merge pull request #2900 from lioncash/sqrt
Arm64/VectorOps: Remove redundant move in VFRSqrt SVE path
2023-08-15 12:39:02 -07:00
Ryan Houdek b18e5e2f63 InstCountCI: Disables tests with unsupported configurations
Need to have the vixl disassembler enabled for instcountci.

Also need to make sure the host platform is using the ARM64 JIT.
2023-08-15 12:27:32 -07:00
Lioncache 2708374d95 Arm64/VectorOps: Remove redundant move in VFRSqrt SVE path
We can perform the SQRT first and then broadcast 1.0 into the destination
since all the intermediary work is done, meaning we don't have to worry
about Dst and Vector aliasing one another.
2023-08-15 15:22:21 -04:00
Mai 3c99fb84e2 Merge pull request #2899 from Sonicadvance1/instcountci_fix_asm_name
InstCountCI: Ensure output nasm name doesn't conflict
2023-08-15 14:44:44 -04:00
Ryan Houdek 710a3928ff Merge pull request #2897 from lioncash/broad
ARMEmitter: Handle SVE load and broadcast element group
2023-08-15 11:36:20 -07:00
Ryan Houdek ac1d2ec1d9 InstCountCI: Ensure output nasm name doesn't conflict
Pretty sure this is why CI is unhappy. If a test in a different file has
the same name then it is highly likely to conflict when nasm is
generating files and will overwrite and erase, causing CI to break.

Include the incoming json filename as part of the asm keys so it can't
conflict here.
2023-08-15 11:29:39 -07:00
Lioncache 6acce60855 ARMEmitter: Handle SVE load and broadcast element group
These can be used to improve vbroadcast implementations from
doing a mem load+dup in the non-GPR case into just directly
loading into the destination.
2023-08-15 13:47:12 -04:00
Mai 63f28eae4f Merge pull request #2896 from Sonicadvance1/primary_table
InstCountCI: Adds primary tables
2023-08-15 13:07:35 -04:00
Ryan Houdek eda67eb0a5 Merge pull request #2895 from lioncash/scalar
ARMEmitter: Handle load/store multiple structures (scalar plus scalar) groups
2023-08-15 09:12:40 -07:00
Ryan Houdek 21cac6ef0b InstCountCI: Adds primary tables
Surprisingly few instructions are optimal here.
This is all the instruction tables completed now!
2023-08-15 09:11:39 -07:00
Ryan Houdek 1193b55150 InstCountCI: Script auto change line 2023-08-15 09:11:29 -07:00
Ryan Houdek 27a53280bd InstCountCI: Fixes bitness in script 2023-08-15 09:11:07 -07:00
Mai 135b9ac425 Merge pull request #2894 from Sonicadvance1/instcount_secondary_prefixes
InstCountCI: Adds secondary prefix tables
2023-08-15 10:22:28 -04:00
Lioncache 81115f64f6 ARMEmitter: Handle SVE Store Multiple Structures (scalar plus scalar) 2023-08-15 10:18:37 -04:00
Lioncache 0176efa3bb ARMEmitter: Handle SVE Load Multiple Structures (scalar plus scalar) group 2023-08-15 10:01:15 -04:00
Ryan Houdek 2508274ddb InstCountCI: Adds secondary prefix tables
Most of these 128-bit vector ops are looking pretty good. Scalar and
edge case ops aren't always optimal though.
2023-08-15 06:57:47 -07:00
Mai 24d01cd8d2 Merge pull request #2893 from Sonicadvance1/instcount_secondary
InstCountCI: Adds secondary tables
2023-08-15 04:24:40 -04:00
Ryan Houdek 2fb72f822d InstCountCI: Adds secondary tables
A decent number of instructions that are optimal but still quite a lot
that aren't.
2023-08-14 16:04:31 -07:00
Ryan Houdek a2c2b042b2 CodeSizeValidation: Fixes nullptr dereference 2023-08-14 16:04:20 -07:00
Ryan Houdek 398e76be89 X86Tables: Fixes typo 2023-08-14 16:04:05 -07:00
Ryan Houdek 3885bc42f6 Merge pull request #2892 from Sonicadvance1/minor_config_changes
Config: Minor changes
2023-08-14 15:10:42 -07:00
Ryan Houdek c5439b294c TestHarnessRunner: InitializeConfig paths
Just removes an erroneous message in the TestHarnessRuner on each
execution.
2023-08-14 12:37:35 -07:00
Ryan Houdek f248e7f3e7 Config: If DumpIR is enabled, default enable a passmanager option
If DumpIR is enabled but the PassManagerDumpIR option isn't enabled then
this currently does nothing.

As a convenience, enable dumping the final optimized IR if an option
hasn't been specified.
2023-08-14 12:29:56 -07:00
Ryan Houdek e51606c669 Config: Fixes mixup in PassManagerDumpIR
The opt and pass options were inverted in PassManager.
Renames the enum to make this more clear.
2023-08-14 12:28:37 -07:00
Ryan Houdek 648d8aeb65 Config: Adds missing server option to DumpIR description
This was accepted but I failed to describe it when added.
2023-08-14 12:22:35 -07:00
Ryan Houdek 112c463655 Config: Ensure OutputLog to server doesn't try to expand path
"server" isn't a path, this was missed when it was added.
2023-08-14 12:20:58 -07:00
Ryan Houdek 2f0c690d54 Merge pull request #2890 from alyssarosenzweig/constprop/set-but-not
ConstProp: Fix set-but-not-used mask variable
2023-08-14 10:17:42 -07:00
Alyssa Rosenzweig 7ecbbd6c04 ConstProp: Fix set-but-not-used mask variable
I think this was the intended logic?

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-14 11:59:59 -04:00
Mai 103e6044fa Merge pull request #2889 from Sonicadvance1/instcount_primarygroup
InstCountCI: Adds Primary group tables
2023-08-13 23:07:25 -04:00
Mai e334278f80 Merge pull request #2887 from Sonicadvance1/instcount_vex_map3
InstCountCI: Adds VEX map3 tables
2023-08-13 23:06:55 -04:00
Mai 7fe2d3b305 Merge pull request #2888 from Sonicadvance1/instcount_vex_map_group
InstCountCI: Adds VEX map group tables
2023-08-13 23:06:06 -04:00
Ryan Houdek 51d4bd5f05 InstCountCI: Adds Primary group tables
A lot of things fairly close to optimal but not quite there.
2023-08-13 17:26:09 -07:00
Ryan Houdek 2a871957f8 InstCountCI: Adds VEX map3 tables
Pretty much no instructions are optimal here. 64-bit RORX is the
outlier.
2023-08-13 13:00:00 -07:00
Ryan Houdek d9266167e1 InstCountCI: Adds VEX map group tables
No instructions are optimal in this table.
2023-08-13 12:59:11 -07:00
Ryan Houdek cbfea75037 Increase maximum instruction name to 128-bytes
Some long name instructions are going beyond 32-bytes. Obviously not
hurting to encode a few additional bytes.
2023-08-13 12:32:14 -07:00
Ryan Houdek ef3887ca4f X86Tables: Fixes typo in VEX table 2023-08-13 12:32:14 -07:00
Mai 871434dab9 Merge pull request #2886 from Sonicadvance1/instcount_vex_map2
InstCountCI: Adds FEX map2 tables
2023-08-13 07:42:01 -04:00
Ryan Houdek 33b84337ea InstCountCI: Adds FEX map2 tables
A handful of instructions are optimal, a few random ones are close, but
a lot aren't close.
2023-08-13 04:29:19 -07:00
Ryan Houdek 1e2b5ecd5d InstCountParser: Allow the ability to skip instructions 2023-08-13 04:29:07 -07:00
Mai a4647982cd Merge pull request #2885 from Sonicadvance1/vex_map1
InstCountCI: Adds VEX map1 tables
2023-08-13 07:05:52 -04:00
Ryan Houdek 047bbca5bb InstCountCI: Adds VEX map1 tables
None of the instructions here are optimal, a couple are close.

Will be splitting each of the map tables in to their own json files
since each one can get fairly large.
2023-08-13 02:15:53 -07:00
Mai 1146c42044 Merge pull request #2884 from Sonicadvance1/instcount_x87
InstCountCI: Adds x87 table
2023-08-12 11:12:01 -04:00
Ryan Houdek fc85431a0e InstCountCI: Adds x87 table
Most of these instructions aren't optimal.
Even outside of the ones that jump out of the JIT they have a issues in
most cases.
2023-08-12 06:53:30 -07:00
Mai a15934bd55 Merge pull request #2883 from Sonicadvance1/instcount_h0f38
InstCountCI: Adds H0F38 table
2023-08-12 09:05:27 -04:00
Ryan Houdek e1ef84f933 InstCountCI: Adds H0F38 table
Some of these instructions haven't been fully audited, which is why they
are classified as Unknown.
2023-08-12 05:20:44 -07:00
Ryan Houdek 139dd4ccbc Merge pull request #2882 from lioncash/adr
ARMEmitter: Handle SVE ADR
2023-08-11 20:52:42 -07:00
Lioncache 8fd810c3c0 ARMEmitter: Migrate adr off SVEMemOperand
We need to move the modifier enum out of the SVEMemOperand class
since it's also used with adr. Plus, this can also be convenient
not being tied down to the class itself.

This also makes accessing modifiers less noisy, since the class
2023-08-11 22:17:46 -04:00
Lioncache 068db933bf ARMEmitter: Handle SVE ADR 2023-08-11 19:50:21 -04:00
Ryan Houdek 72357e50a2 Merge pull request #2881 from lioncash/imm8
ARMEmitter: Handle SVE CPY (immediate)
2023-08-11 15:50:39 -07:00
Lioncache 0bf74a1f3e ARMEmitter: Use signed imm8 handler with dup_imm
Lets us deduplicate the behavior used for dup and cpy.
2023-08-11 18:30:45 -04:00
Lioncache 78f06c7fcb ARMEmitter: Handle SVE CPY (immediate)
Also adds the relevant aliases.
2023-08-11 18:24:26 -04:00
Ryan Houdek 6d1fcfce09 Merge pull request #2877 from Sonicadvance1/classification_adds
InstructionCountCI: Adds three more instruction tables
2023-08-11 15:09:29 -07:00
Ryan Houdek 5a0a6dd0ca Merge pull request #2880 from lioncash/vfp
ARMEmitter: Migrate off vixl float utils
2023-08-11 14:35:23 -07:00
Mai da17e24996 Merge pull request #2879 from Sonicadvance1/fix_adcx
FEXCore: Fixes bug with 32-bit adcx
2023-08-11 17:27:00 -04:00
Lioncache aef0795dc8 ARMEmitter: Make FloatToEquivalentUInt a little more robust
Rather than compare sizes, we should be comparing the types directly,
prevents any shenanigans from happening if interface changes occur to
Float16.
2023-08-11 17:16:14 -04:00
Lioncache f63498a558 ARMEmitter: Migrate off vixl float utils 2023-08-11 17:12:34 -04:00
Ryan Houdek 9aa3fde174 FEXCore: Fixes bug with 32-bit adcx
When a 32-bit adcx instruction was encountered, it was getting treated
as a 16-bit adcx instruction instead. This is because of the 0x66 prefix
required to handle this instruction.

Adds a unit test to ensure it doesn't break again.
2023-08-11 14:09:31 -07:00
Ryan Houdek 8fce13386a Merge pull request #2878 from lioncash/fcpy
ARMEmitter: Handle SVE FCPY (predicated)
2023-08-11 13:58:31 -07:00
Lioncache 247c7ce784 ARMEmitter: Handle SVE FCPY (predicated)
While we're at it, we can reduce our dependence on vixl's utils by
implementing our own based off the pseudocode of VFPExpandImm.
2023-08-11 16:18:20 -04:00
Ryan Houdek 7e12b056dc InstructionCountCI: Adds three more instruction tables
3DNow! table, H0F3A table, and SecondaryModRM table.

H0F3A table skips the SSE4.2 string instructions because they're
nightmares for now.
2023-08-11 11:13:42 -07:00
Ryan Houdek e8e52af2e8 CodeSizeValidation: Adds support for overriding CLZero support
Vixl simulator by default doesn't support this.
2023-08-11 11:12:46 -07:00
Ryan Houdek 6c7371af60 Merge pull request #2872 from Sonicadvance1/instruction_count_ci
FEX: Adds instruction count CI
2023-08-11 11:12:00 -07:00
Ryan Houdek acc7f2fa8f FEX: Adds instruction count CI
Implements CI for tracking instruction counts for generate blocks of
code when transforming from x86 to ARM64 assembly.

This will end up encompassing every instruction in our instruction
tables similarly to how our assembly tests try to test everything in our
instruction tables.

Incidentally, the data for this CI is generated using our assembly
tests. By enabling disassembly and instruction stats when executing a
suite of instructions, this gives the stats that can be added to a json
file.

The current implementation only implements the SecondGroup table of
instructions because it is a relatively small table and has known
inefficiencies in the instruction implementations. As this gets merged I
will be adding more tables of instructions to additional json files for
testing.

These JSON files will support adjusting CPU features regardless of the
host features so it can test implementations depending on different CPU
features. This will let us test things like one instruction having
different "optimal" implementations depending on if it supports SVE128,
SVE256, SVEI8MM, etc.

This initial instruction auditing is what found the bug in our vector
shift instructions by size of zero. If inspecting the result of the CI
run, you can tell that these instructions still aren't "optimal" because
they are doing loads and stores that can be eliminated.

The "Optimal" in the JSON is purely for human readable and grepping
ability to see what is optimal versus not. Same with the "Comment"
section.

According to my auditing spreadsheet, the total number of instructions
that will end up in these json files will be about 1000, but we will
likely end up with more since there will be edge cases that can be more
optimal depending on arguments.
2023-08-11 09:10:36 -07:00
Ryan Houdek b8b4dd8008 FEXCore/Utils: Add the ability to write a fextl::string 2023-08-11 09:08:54 -07:00
Ryan Houdek ec8855f8fb Arm64: Consolidate simulator and diassembler code in to Arm64Emitter
This was confusingly split between Arm64Emitter, Arm64Dispatcher, and
Arm64JIT.

- Arm64JIT objects were unnecessary and free to be deleted.
- Arm64Dispatcher simulator and decoder moved to Arm64Emitter
- Arm64Emitter disassembler and decoder renamed
  - Dropped usage of the PrintDisassembler since it is hardcoded to go
    through a FILE* type
  - We instead want its output to go through LogMan, which means using a
    split Decoder+Disassembler object pair.
  - Can't reuse the object from the vixl simulator since the simulator
    registers the decoder as a visitor, causing the simulator to execute
    while disassembling instructions if reused.
- Disassembly output for blocks and dispatcher now output through Logman
  - Blocks wrapped in Begin/End text for tracking purposes for CI.
2023-08-11 08:05:10 -07:00
Ryan Houdek 969ad9b3b0 Merge pull request #2869 from Sonicadvance1/sve_128bit_ci
Github: Adds a CI runner for 128-bit SVE testing
2023-08-11 07:37:47 -07:00
Ryan Houdek 5de7eeea20 Merge pull request #2876 from lioncash/comment
ARMEmitter: Remove resolved TODO comment
2023-08-11 07:22:19 -07:00
Ryan Houdek 0109e88082 Merge pull request #2875 from lioncash/ff
ARMEmitter: Handle contiguous first fault load (scalar plus scalar) group
2023-08-11 07:09:32 -07:00
Lioncache 73288f377f ARMEmitter: Remove resolved TODO comment
I forgot to remove this after implementing the normal gather instruction handling.
2023-08-11 10:03:10 -04:00
Lioncache 0aaa9503c9 ARMEmitter: Add missing ld1w (scalar plus scalar) tests
ld1sw was mistakenly tested twice.

Also groups the tests by data sizes.
2023-08-11 09:51:04 -04:00
Lioncache 14cc23b6c3 ARMEmitter: Handle contiguous first fault load (scalar plus scalar) group
Adds the only missing implementation category for the first-faulting loads,
making the interface more consistent.
2023-08-11 09:46:40 -04:00
Ryan Houdek 5404dba360 Github: Adds a CI runner for 128-bit SVE testing
We don't currently have a device in CI that can run SVE with 128-bit
width registers. Until we have a device with this, make sure the vixl
simulator is also running the ASM tests in this width.
2023-08-10 22:27:59 -07:00
Ryan Houdek 833c07e9e2 CoreState: Zero initialize some important members
This was causing test failure locally where some values were set to
uninitialized data. Ensure that gregs, YMM, and MMX registers are all
zero initialized.
2023-08-10 22:27:59 -07:00
Ryan Houdek 186ec201aa Config: Stop passing a temporary std::string_view outside of scope
Was causing strenum variables to be parsed, then leaving scope would
break the string.
2023-08-10 22:27:59 -07:00
Ryan Houdek 887c47c451 Config: Adds an option to override SVE width for CI 2023-08-10 21:25:57 -07:00
Ryan Houdek 0f3460e025 Config: Fixes typo in HostFeatures disable{sve,avx} 2023-08-10 21:25:57 -07:00
Ryan Houdek fadba9a3e1 External: Update vixl
Fixes simulator bug
2023-08-10 21:25:57 -07:00
Ryan Houdek 9d26af95ab Merge pull request #2873 from neobrain/refactor_warning_fixes
Various warning fixes
2023-08-10 16:58:14 -07:00
Tony Wasserka aed4dda3e4 Arm64: Remove unused function 2023-08-10 18:45:14 +02:00
Tony Wasserka e0d21e61cc Syscalls: Fix warnings about unused variables in Release builds 2023-08-10 18:45:14 +02:00
Tony Wasserka 45d0f0d349 ARMEmitter: Fix warnings about unused variables in Release builds 2023-08-10 18:45:14 +02:00
Tony Wasserka f1cc76614b Include VIXL as a system library
This suppresses warnings from VIXL headers.
2023-08-10 18:45:14 +02:00
Ryan Houdek 099f29f1ed Merge pull request #2871 from Sonicadvance1/fix_stats_missing_member
FEXCore: Fixes Arm64 stats disassembly
2023-08-10 06:23:02 -07:00
Ryan Houdek b1a3f82923 FEXCore: Fixes Arm64 stats disassembly
Requires the IR headerop to house the number of host instructions this
code is translating for the stats.

Fixes compiling with disassembly enabled, will be used with the
instruction count CI.
2023-08-10 03:23:25 -07:00
Ryan Houdek 6f4a23dd15 Merge pull request #2870 from lioncash/indexed
ARMEmitter: Handle SVE FP multiply-add long groups
2023-08-09 21:35:33 -07:00
Ryan Houdek f3182036bc Merge pull request #2867 from Sonicadvance1/dummy_thin_handlers
FEX: Create a CommonTools static library
2023-08-09 21:34:46 -07:00
Lioncache 444961ad79 ARMEmitter: Handle SVE FP multiply-add long group 2023-08-09 15:20:04 -04:00
Lioncache 48a3271fbc ARMEmitter: Handle SVE FP multiply-add long (indexed) group 2023-08-09 15:19:49 -04:00
Mai ea8fbc61c2 Merge pull request #2868 from Sonicadvance1/irdumper_passmanager
IR: Adds Option to run the IRDumper with more configurations
2023-08-09 10:28:25 -04:00
Ryan Houdek 35e97ec9bc IR: Adds Option to run the IRDumper with more configurations
This is incredibly useful and I find myself hacking this feature in
every time I am optimizing IR. Adds a new configuration option which
allows dumping IR at various times.

Before any optimization passes has happened
After all optimizations passes have happened
Before and After each IRPass to see what is breaking something.

Needs #2864 merged first
2023-08-09 05:58:20 -07:00
Ryan Houdek 53ac8abce9 Merge pull request #2863 from Sonicadvance1/stats
Arm64: Adds stats to the disassembly
2023-08-09 04:06:22 -07:00
Ryan Houdek fe351353f6 Merge pull request #2865 from Sonicadvance1/first_sve_opt
Arm64: Implement first SVE-128bit optimization
2023-08-09 04:06:05 -07:00
Ryan Houdek a23cb0447b Arm64: Implement first SVE-128bit optimization
This is a /very/ simple optimization purely because of a choice that ARM
made with SVE in latest Cortex.

Cortex-A715:
   - sxtl/sxtl2/uxtl/uxtl2 can execute 1 instruction per cycle.
   - sunpklo/sunpkhi/uunpklo/uunpkhi can execute 2 instructions per cycle.

Cortex-X3:
   - sxtl/sxtl2/uxtl/uxtl2 can execute 2 instruction per cycle.
   - sunpklo/sunpkhi/uunpklo/uunpkhi can execute 4 instructions per cycle.

This is fairly quirky since this optimization only works on SVE systems
with 128-bit Vector length. Which since it is all of the current
consumer platforms, it will work.
2023-08-09 03:51:57 -07:00
Ryan Houdek f2aa2ce4bb Arm64: Rename HostSupportsSVE
We need to know the difference between the host supporting SVE with
128-bit registers versus 256-bit registers. Ensure we know the
difference.

No functional change here.
2023-08-09 03:51:56 -07:00
Ryan Houdek cf93652708 Config: Adds support for overriding host features
This allows use to both enable and disable regardless of what the host
supports. This replaces the old `EnableAVX` option.

Unlike the old EnableAVX option which was a binary option which could
only disable, each of these options are technically trinary states.
Not setting an option gives you the default detection, while explicitly
enabling or disabling will toggle the option regardless of what the host
supports.

This will be used by the instruction count CI in the future.
2023-08-09 03:51:37 -07:00
Ryan Houdek eaed5c4704 Merge pull request #2862 from Sonicadvance1/optimize_vector_zero
ARM64: Optimize vector zeroing
2023-08-09 03:51:04 -07:00
Mai c77ed78f5a Merge pull request #2861 from Sonicadvance1/fix_vector_shift_by_zero
FEXCore: Fixes vector shifts by zero
2023-08-09 05:52:10 -04:00
Ryan Houdek 348844a95b FEX: Create a CommonTools static library
Moves the dummy handlers over to this library. This will end up getting
used for more than the mingw test harness runner once the instruction
count CI is operational.
2023-08-09 02:27:13 -07:00
Ryan Houdek e8fb322025 unittests: Adds tests for vector shifts with zero immediate
To ensure FEX doesn't encounter the encoding bug again.
2023-08-09 02:16:17 -07:00
Ryan Houdek 5f0efda8fe ARM64: Fixes shift by immediate zero
These would emit invalid instructions in most cases. Turn in to a move
or a no-op if the shift is zero.
2023-08-09 02:16:17 -07:00
Ryan Houdek d198d701aa OpcodeDispatcher: Fixes vector shifts by immediate zero
pslldq logic was wrong in the case of zero shift.
The rest should just return their source in the case of zero shift.
2023-08-09 02:16:17 -07:00
Mai c4c7620ed5 Merge pull request #2866 from Sonicadvance1/remove_unnecessary_loadconstant
Arm64: Remove erroneous LoadConstant
2023-08-09 05:10:11 -04:00
Ryan Houdek bf5719770e Arm64: Remove erroneous LoadConstant
This was a debug LoadConstant that would load the entry in to a temprary
register to make it easier to see what RIP a block was in.

This was implemented when FEX stopped storing the RIP in the CPU state
for every block. This is now no longer necessary since FEX stores the
in the tail data of the block.

This was affecting instructioncountci when in a debug build.
2023-08-08 22:56:36 -07:00
Ryan Houdek 0f6a268243 Arm64: Adds stats to the disassembly
I use this locally when looking for optimization opportunities in the
JIT.
The instruction count CI in the future will use this as well.
Just get it upstreamed right away.
2023-08-08 22:28:52 -07:00
Ryan Houdek e0461497a0 ARM64: Optimize vector zeroing
`eor <reg>, <reg>, <reg>` is not the optimal way to zero a vector
register on ARM CPUs. Instead we should move by constant or zero
register to take advantage of zero-latency moves.
2023-08-08 22:24:11 -07:00
Ryan Houdek e9e5b6fb0b Docs: Update for release FEX-2308 2023-08-06 02:34:55 -07:00
Ryan Houdek 68cb6e61d1 Merge pull request #2860 from lioncash/alias
ARMEmitter: Add missing atomic aliases
2023-08-06 02:05:30 -07:00
Mai 5d0b2060e2 Merge pull request #2858 from Sonicadvance1/fix_clzero
X86Tables: Fixes CLZero destination address
2023-08-06 05:05:07 -04:00
Lioncache b7d05a65c7 ARMEmitter: Add missing atomic aliases 2023-08-04 21:49:59 -04:00
Lioncache 93fe2fe06c ARMEmitter: Detemplatize LoadStoreAtomicLSE
Lets us lessen some template instantiations.
2023-08-04 19:47:03 -04:00
Ryan Houdek 21eb6e03c7 Merge pull request #2859 from bylaws/ooo
Fix 16-bit popa insertion behaviour
2023-08-04 16:37:09 -07:00
Lioncache 1b53337925 ARMEmitter: Simplify LoadStoreAtomicLSE variant
We can move the base opcode into the implementation function.
2023-08-04 19:35:54 -04:00
Billy Laws 52e5b8ccd9 OpcodeDispatcher: Fix 16-bit popa insertion behaviour
The 16-bit writes shouldn't overwrite the upper half of the 32-bit register for
POPA.
2023-08-04 17:24:55 +01:00
Billy Laws 8c8a8c84df unittests: Test for 16-bit popa insertion behaviour 2023-08-04 17:24:52 +01:00
Ryan Houdek 0d6837f1a1 Merge pull request #2856 from Sonicadvance1/allow_override_linker
CMake: Allow overriding linker
2023-08-04 03:37:17 -07:00
Ryan Houdek 7ef3cb88f9 CMake: Allow overriding linker
While the ENABLE_LLD and ENABLE_MOLD options are nice, they don't handle
the case when the linker of `lld` or `mold` doesn't match the compiler.

This particularly crops up when overriding the C compiler to a new
version of clang but the globally installed `ld.lld` is still the old
clang version.
This then causes clang to fail with unusual errors when upstream breaks
compatibility with itself.

Easy enough to use by passing the linker to cmake:
`-DUSE_LINKER=/usr/bin/ld.lld-15`

This also removes the ENABLE_LLD and ENABLE_MOLD options to use
USE_LINKER directly.
- ldd: `-DUSE_LINKER=lld`
- mold: `-DUSE_LINKER=mold`

Example of compiler failure when built with clang-15 but attempting to
link with ld.lld 14:
```bash
ld.lld-14: error: unittests/APITests/CMakeFiles/Filesystem.dir/Filesystem.cpp.o: Opaque pointers are only supported in -opaque-pointers mode (Producer: 'LLVM15.0.7' Reader: 'LLVM 14.0.6')
```
2023-08-04 02:34:15 -07:00
Ryan Houdek 617977357a X86Tables: Fixes CLZero destination address
This needs to default to 64-bit addresses, this was previously
defaulting to 32-bit which was meaning the destination address was
getting truncated. In a 32-bit process the address is still 32-bit.

I'm actually surprised this hasn't caused spurious SIGSEGV before this
point.

Adds a 32-bit test to ensure that side is tested as well.
2023-08-04 02:31:30 -07:00
Ryan Houdek 5a53c9231b Merge pull request #2855 from alyssarosenzweig/tst-instead-of-cmn
JIT: Use TST instead of CMN
2023-08-02 15:07:28 -07:00
Alyssa Rosenzweig a996e5300e JIT: Use TST instead of CMN
This is more obvious. llvm-mca says TST is half the cycle count of CMN
for whatever it's defaulting to. dougallj's reference shows both as the
same performance.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 17:51:33 -04:00
Ryan Houdek 25b2af14fd Merge pull request #2854 from alyssarosenzweig/flags/rotate-harder
OpcodeDispatcher: Optimize rotates
2023-08-02 14:09:40 -07:00
Alyssa Rosenzweig 76059949ea Merge pull request #2852 from Sonicadvance1/optimize_phsubsw
OpcodeDispatcher: Optimize phsubsw/phaddsw
2023-08-02 17:02:13 -04:00
Ryan Houdek 6e15c9c213 Merge pull request #2851 from Sonicadvance1/optimize_cas128_select
OpcodeDispatcher: Optimize CMPXCHG{8B,16B} final comparison
2023-08-02 14:01:58 -07:00
Alyssa Rosenzweig 01fcca884b OpcodeDispatcher: Optimize rotates
In the non-immediate cases, we can amortize some work between the two
flags to come out 1 instruction ahead.

In the immediate case, costs us an extra 2 instructions compared to
before we packed NZCV flags, but this mitigates a bigger instr count
regression that this PR would otherwise have. Coming out ahead will
require FlagM and smarter RA, but is doable.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 16:54:56 -04:00
Ryan Houdek 91bd3aa62a Merge pull request #2832 from alyssarosenzweig/flags/pack-nzcv
Pack NZCV flags
2023-08-02 13:42:56 -07:00
Alyssa Rosenzweig 7a0119b092 OpcodeDispatcher: Optimize right shifts
Same technique as the left shifts. Gets rid of all our COND_FLAG_SET
use, which is good because it's a performance footgun.

Overall saves 17 instructions (!!!!) from the flag calculation code for
`sar eax, cl`.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 14:45:44 -04:00
Alyssa Rosenzweig fa42c1616e OpcodeDispatcher: Preserve AF for non-immediate shift
The selection logic is expensive. Saves 5 instructions.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 14:45:10 -04:00
Alyssa Rosenzweig 18783948f7 OpcodeDispatcher: Optimize non-immediate shift
Similar to the immediate case, but now we select between the entire old
and new NZCV registers. This is faster than selecting each bit
independently. Saves 11 instructions for calculating flags for "shl eax,
cl".
2023-08-02 14:45:10 -04:00
Alyssa Rosenzweig 969d2e4b6a OpcodeDispatcher: Zero OF for shift > 1
It is undefined in this case. We prefer to zero (rather than preserve
the existing value) as it avoids a costly RMW of the NZCV register.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 14:14:08 -04:00
Alyssa Rosenzweig 7ceaf56407 OpcodeDispatcher: Use SetNZ_ZeroCV for immediate shifts
We need to be careful to preserve V if needed. For `shl 1` and `shr 1`,
saves 2 instruction overall compared to before the PR. For `sar 1`,
saves 3 overall.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 14:12:00 -04:00
Alyssa Rosenzweig 7c52375267 OpcodeDispatcher: Optimize (U)MUL flags calculation
csel the value we want directly. Saves 2 instructions. Could do better
still but hey, progress is progress. Currently looks like:

2995: 0x0000ffff6c600040  320407f5		orr w21, wzr, #0x30000000
2995: 0x0000ffff6c600044  f10000df		cmp x6, #0x0 (0)
2995: 0x0000ffff6c600048  9a950294		csel x20, x20, x21, eq
2995: 0x0000ffff6c60004c  b902db94		str w20, [x28, #728]

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:40:44 -04:00
Alyssa Rosenzweig a4ea792d03 OpcodeDispatcher: Optimize GetRFLAG of definitely-0 flag
We can just return zero, no need to do a pointless Bfe. Saves yet
another instruction for GetPackedRLAG in a test I'm looking at.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:18:39 -04:00
Alyssa Rosenzweig cec98637c9 OpcodeDispatcher: Avoid extra OR in GetPackedRFLAG
We know that bit 0 is CF, so we can do CF first and then avoid setting
Original to 2 (for reserved) with a silly `or xzr, #2` instruction.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:18:39 -04:00
Alyssa Rosenzweig d5026f5815 OpcodeDispatcher: Handle SF/ZF together for GetPackedRFLAG
They're together on both x86 and arm64, so this is faster if we're
getting both.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:18:39 -04:00
Alyssa Rosenzweig 1e4456ec40 OpcodeDispatcher: Use orlshl in GetPackedRFLAG
Saves some moves.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:18:39 -04:00
Alyssa Rosenzweig e285c7c9a0 OpcodeDispatcher: Use TEST when possible
Faster sign/negate testing for 32-bit/64-bit inputs. This could maybe be
extended to 8/16-bit if we have FlagM but that's for later.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:18:39 -04:00
Alyssa Rosenzweig 8244c7f2a6 OpcodeDispatcher: Use orlshl when possible
If we can prove that a flag bit could not possibly be set, we can use
orlshl rather than bfi, which can be more efficient.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:18:39 -04:00
Alyssa Rosenzweig 747c5e17f8 OpcodeDispatcher: Add ZeroNZCV helper
In some cases we just want to insert in one bit at a time, add a helper
to zero the 4 flags together so we can avoid the extra RMW cycle at the
beginning.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 13:18:39 -04:00
Alyssa Rosenzweig 52ce027e9b OpcodeDispatcher: Set N flag more efficiently
We can set N more efficiently with some bit math, and zero ZCV at the
same time. In the future we'll be able to use TST for this to make it
even faster.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 0fb3903889 OpcodeDispatcher: Pack NZCV flags together
Later, this will let us take advantage of the arm64 flags. For
now, this just turns some strb's into bfi's for dubious benefit.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 2832afd0f7 OpcodeDispatcher: Calculate deferred flags more
If we read or write NZCV flags we need to call CalculateDeferredFlags on
block boundaries, if only to flush out the cached copy.

Also, when leaving a block we call it to flush out. This is annoyingly
invasive but I don't know of a better way to do this that doesn't
involve rearchitecting the dispatcher.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 4af42477a7 OpcodeDispatcher: Use GetRFLAG more
We'll add an extra caching layer in a moment so can't call _LoadFlag
directly and expect correct results.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 4786aa479c OpcodeDispatcher: Unify SetRFLAG impls
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 8a049aa0c3 Context: Make BackendFeatures public
So that the OpcodeDispatcher can check the supported features and emit
code accordingly.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 64bac687b5 IR: Add TEST opcode
Maps to arm64 tst, except properly SSA. This will need some RA support
to avoid redundant mrs/msr sequences.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 6e05494ad0 IR: Add Orlshr
Similar to Orlshl. This will let us save an instruction in
GetPackedRFLAG.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 4d13f5d97d IR: Add Orlshl op
On arm64, orr (with a shifted register) is maybe fewer cycles and
definitely easier on the RA than bfi (=> fewer moves generated). So,
it's preferred when we know the corresponding bit is 0 in the
destination.

It's not useful on other targets, so it's gated behind a backend feature
bit.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Alyssa Rosenzweig 1ad928dc2c IR: Add special NZCV "flag"
Reserve 4 bytes of "flags" to model the 32-bit arm64 NZCV register, so
we can start porting FEX's flag handling code over to using NZCV without
needing the whole compiler to be aware of instructions that might
clobber host flags.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-08-02 12:38:00 -04:00
Ryan Houdek c235ab883d OpcodeDispatcher: Optimize phsubsw/phaddsw
Goes from 41 instructions down to 20(!) instructions.
The primary optimization here is removing a bunch of dull inserts and
instead using zip logic to get the elements where we want them.

The previous implementation was trying to retain semantics around how
the original instruction is implemented, which makes no sense at all.
Unzip the even and odd elements and just do the saturating operations
directly!

Huge shoutout to @dougallj again, showing that I was thinking about this implementation far too much like an x86 developer.

Before:
```asm
0x0000ffff8f300c50  10ffffe0    adr x0, #-0x4 (addr 0xffff8f300c4c)
0x0000ffff8f300c54  f9005f80    str x0, [x28, #184]
0x0000ffff8f300c58  3dc010c4    ldr q4, [x6, #64]
0x0000ffff8f300c5c  6e60bae5    neg v5.8h, v23.8h
0x0000ffff8f300c60  6e60b886    neg v6.8h, v4.8h
0x0000ffff8f300c64  4eb71ee0    mov v0.16b, v23.16b
0x0000ffff8f300c68  6e0614a0    mov v0.h[1], v5.h[1]
0x0000ffff8f300c6c  4ea01c07    mov v7.16b, v0.16b
0x0000ffff8f300c70  6e0614c4    mov v4.h[1], v6.h[1]
0x0000ffff8f300c74  6e0e34a7    mov v7.h[3], v5.h[3]
0x0000ffff8f300c78  6e0e34c4    mov v4.h[3], v6.h[3]
0x0000ffff8f300c7c  6e1654a7    mov v7.h[5], v5.h[5]
0x0000ffff8f300c80  6e1654c4    mov v4.h[5], v6.h[5]
0x0000ffff8f300c84  4ea71ce0    mov v0.16b, v7.16b
0x0000ffff8f300c88  6e1e74a0    mov v0.h[7], v5.h[7]
0x0000ffff8f300c8c  4ea01c05    mov v5.16b, v0.16b
0x0000ffff8f300c90  6e1e74c4    mov v4.h[7], v6.h[7]
0x0000ffff8f300c94  0f10a4a6    sxtl v6.4s, v5.4h
0x0000ffff8f300c98  4f10a4a5    sxtl2 v5.4s, v5.8h
0x0000ffff8f300c9c  0f10a487    sxtl v7.4s, v4.4h
0x0000ffff8f300ca0  4f10a484    sxtl2 v4.4s, v4.8h
0x0000ffff8f300ca4  4ea5bcc5    addp v5.4s, v6.4s, v5.4s
0x0000ffff8f300ca8  4ea4bce4    addp v4.4s, v7.4s, v4.4s
0x0000ffff8f300cac  0e6148a5    sqxtn v5.4h, v5.4s
0x0000ffff8f300cb0  4ea51ca0    mov v0.16b, v5.16b
0x0000ffff8f300cb4  4e614880    sqxtn2 v0.8h, v4.4s
0x0000ffff8f300cb8  4ea01c17    mov v23.16b, v0.16b
0x0000ffff8f300cbc  58000040    ldr x0, pc+8 (addr 0xffff8f300cc4)
0x0000ffff8f300cc0  d63f0000    blr x0
0x0000ffff8f300cc4  a4e97128    ldff1h {z8.d}, p4/z, [x9, x9, lsl #1]
0x0000ffff8f300cc8  0000ffff    udf #0xffff
0x0000ffff8f300ccc  000100dd    unallocated (Unallocated)
0x0000ffff8f300cd0  00000000    udf #0x0
[DEBUG] RIP: 0x100d7
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 41
[DEBUG] Blow-up Amt: 41x
```

After:
```asm
0x0000ffffe2500a04  10ffffe0            adr x0, #-0x4 (addr 0xffffe2500a00)
0x0000ffffe2500a08  f9005f80            str x0, [x28, #184]
0x0000ffffe2500a0c  3dc010c4            ldr q4, [x6, #64]
0x0000ffffe2500a10  4e441ae5            uzp1 v5.8h, v23.8h, v4.8h
0x0000ffffe2500a14  4e445ae4            uzp2 v4.8h, v23.8h, v4.8h
0x0000ffffe2500a18  4e642cb7            sqsub v23.8h, v5.8h, v4.8h
0x0000ffffe2500a1c  58000040            ldr x0, pc+8 (addr 0xffffe2500a24)
0x0000ffffe2500a20  d63f0000            blr x0
0x0000ffffe2500a24  f7fec128            unallocated (Unallocated)
0x0000ffffe2500a28  0000ffff            udf #0xffff
0x0000ffffe2500a2c  000100dd            unallocated (Unallocated)
0x0000ffffe2500a30  00000000            udf #0x0
[DEBUG] RIP: 0x100d7
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 20
[DEBUG] Blow-up Amt: 20x
```
2023-08-02 00:10:32 -07:00
Ryan Houdek 4bf3a0888b OpcodeDispatcher: Optimize CMPXCHG{8B,16B} final comparison
Optimizes the instruction blow-up from 36x to 34x.
The issue with this instruction is that AArch64 doesn't do something
like x86 where it sets a flag if the CAS was successful. This means we
need to do additional comparisons after the fact to see if it was
actually successful.

Previously this was implemented as eor+eor+orr+cmp+cset, Now it is
cmp+ccmp+cset, Saving two instructions.
Simple optimization but easy to do. This instruction is still mostly
killed by the overhead of moving registers all over.

Before:
```asm
0x0000ffffe25002ec  10ffffe0            adr x0, #-0x4 (addr 0xffffe25002e8)
0x0000ffffe25002f0  f9005f80            str x0, [x28, #184]
0x0000ffffe25002f4  aa0403f4            mov x20, x4
0x0000ffffe25002f8  aa0603f5            mov x21, x6
0x0000ffffe25002fc  aa0703f6            mov x22, x7
0x0000ffffe2500300  aa0503f7            mov x23, x5
0x0000ffffe2500304  aa1403e2            mov x2, x20
0x0000ffffe2500308  aa1503e3            mov x3, x21
0x0000ffffe250030c  4862ffb6            caspal x2, x3, x22, x23, [x29]
0x0000ffffe2500310  aa0203f4            mov x20, x2
0x0000ffffe2500314  aa0303f5            mov x21, x3
0x0000ffffe2500318  aa1403f6            mov x22, x20
0x0000ffffe250031c  aa1503f4            mov x20, x21
; ZF Flag + branch
0x0000ffffe2500320  ca0402d5            eor x21, x22, x4
0x0000ffffe2500324  ca060297            eor x23, x20, x6
0x0000ffffe2500328  aa1702b5            orr x21, x21, x23
0x0000ffffe250032c  f10002bf            cmp x21, #0x0 (0)
0x0000ffffe2500330  9a9f17f7            cset x23, eq
0x0000ffffe2500334  390b1b97            strb w23, [x28, #710]
0x0000ffffe2500338  b4000075            cbz x21, #+0xc (addr 0xffffe2500344)

0x0000ffffe250033c  aa1603e4            mov x4, x22
0x0000ffffe2500340  aa1403e6            mov x6, x20
0x0000ffffe2500344  58000040            ldr x0, pc+8 (addr 0xffffe250034c)
0x0000ffffe2500348  d63f0000            blr x0
0x0000ffffe250034c  f7fec128            unallocated (Unallocated)
0x0000ffffe2500350  0000ffff            udf #0xffff
0x0000ffffe2500354  00010053            unallocated (Unallocated)
0x0000ffffe2500358  00000000            udf #0x0
[DEBUG] RIP: 0x1004f
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 36
[DEBUG] Blow-up Amt: 36x
```

After:
```asm
0x0000ffffe25002ec  10ffffe0            adr x0, #-0x4 (addr 0xffffe25002e8)
0x0000ffffe25002f0  f9005f80            str x0, [x28, #184]
0x0000ffffe25002f4  aa0403f4            mov x20, x4
0x0000ffffe25002f8  aa0603f5            mov x21, x6
0x0000ffffe25002fc  aa0703f6            mov x22, x7
0x0000ffffe2500300  aa0503f7            mov x23, x5
0x0000ffffe2500304  aa1403e2            mov x2, x20
0x0000ffffe2500308  aa1503e3            mov x3, x21
0x0000ffffe250030c  4862ffb6            caspal x2, x3, x22, x23, [x29]
0x0000ffffe2500310  aa0203f6            mov x22, x2
0x0000ffffe2500314  aa0303f7            mov x23, x3
0x0000ffffe2500318  aa1603f8            mov x24, x22
0x0000ffffe250031c  aa1703f9            mov x25, x23
; ZF Flag + branch
0x0000ffffe2500320  eb1402df            cmp x22, x20
0x0000ffffe2500324  fa5502e0            ccmp x23, x21, #nzcv, eq
0x0000ffffe2500328  9a9f17f4            cset x20, eq
0x0000ffffe250032c  390b1b94            strb w20, [x28, #710]
0x0000ffffe2500330  b5000074            cbnz x20, #+0xc (addr 0xffffe250033c)

0x0000ffffe2500334  aa1803e4            mov x4, x24
0x0000ffffe2500338  aa1903e6            mov x6, x25
0x0000ffffe250033c  58000040            ldr x0, pc+8 (addr 0xffffe2500344)
0x0000ffffe2500340  d63f0000            blr x0
0x0000ffffe2500344  f7fec128            unallocated (Unallocated)
0x0000ffffe2500348  0000ffff            udf #0xffff
0x0000ffffe250034c  00010053            unallocated (Unallocated)
0x0000ffffe2500350  00000000            udf #0x0
[DEBUG] RIP: 0x1004f
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 34
[DEBUG] Blow-up Amt: 34x
```
2023-08-01 20:01:02 -07:00
Ryan Houdek 6834fe32e4 IR: Adds support for GPRPair to Select IR op
This IR operation was limited to GPR only previously for the values
getting compared.

This adds support for GPRPair (and technically FPR) so that it can be
used directly with GPR pairs. I say technically FPR because the
IREmitter disallowed FPRs for the comparison, but this was already
supported in all of the backends, we just didn't ever use it.

Some minor changes to the constant prop pass to ensure that we don't try
to propagate a select in to a CondJump, otherwise pair comparisons would
be duplicated. This code is expecting to be able to merge a simple
comparison in to a `cbnz`, which doesn't happen with GPR pairs.
2023-08-01 19:50:15 -07:00
Ryan Houdek d196709162 Merge pull request #2850 from Sonicadvance1/put_the_man_in_its_place
FEXCore: Ensure that the man page follows DESTDIR
2023-08-01 14:28:06 -07:00
Ryan Houdek 475a6a38fa FEXCore: Ensure that the man page follows DESTDIR
When cmake's `install` function is invoked with a relative path, then it
is interpreted as being relative to the `CMAKE_INSTALL_PREFIX` variable.

This variable follows both `DESTDIR` and `CMAKE_INSTALL_PREFIX` so it is
best to use relative addresses in the install path.

Thanks for the report Mike!
Fixes #2849
2023-08-01 13:28:21 -07:00
Mai 0f748bd724 Merge pull request #2829 from Sonicadvance1/mingw_runner
Github: Adds mingw build test workflow
2023-07-31 21:45:24 -04:00
Ryan Houdek 47bd331239 Github: Adds mingw build test workflow
Currently only does a build, doing a CI run means figuring out why
TestHarnessRunner doesn't find libraries correctly.

I want to ensure we don't break building at least while sorting out the
rest of this.
2023-07-31 17:55:24 -07:00
Ryan Houdek 173b70d191 Merge pull request #2817 from Sonicadvance1/psad_you_know
OpcodeDispatcher: Optimize PSAD* to use vuabdl{2,}
2023-07-31 14:54:57 -07:00
Ryan Houdek 96aa0a844e Merge pull request #2845 from Sonicadvance1/thunks_guestlibs
Thunks: Set bitness flags for 64-bit guests
2023-07-31 12:43:11 -07:00
Ryan Houdek 48121cd585 Thunks: Set bitness flags for 64-bit guests
This will help non-multiarch aware distros
2023-07-31 12:22:33 -07:00
Ryan Houdek 52d7efda10 Merge pull request #2839 from Sonicadvance1/shrdi_of
OpcodeDispatcher: Fixes SHRD by immediate OF flag calculation
2023-07-31 10:56:40 -07:00
Ryan Houdek 30ab4d3f58 Merge pull request #2848 from lioncash/aliases
ARMEmitter: Add cinc/cinv/csetm aliases
2023-07-30 20:18:29 -07:00
Lioncache f9fa0f25e2 ARMEmitter: Add cinc/cinv/csetm aliases
Also corrects a cneg test using CC_AL. The ARM ARM says this
shouldn't be using both AL and NV
2023-07-30 22:41:03 -04:00
Ryan Houdek eebcbfda96 Merge pull request #2847 from lioncash/aliases
ARMEmitter: Add ngc/ngcs aliases
2023-07-30 19:07:32 -07:00
Lioncache 1483ddb538 ARMEmitter: Add ngc/ngcs aliases 2023-07-30 21:55:54 -04:00
Ryan Houdek d2bca9b997 Merge pull request #2846 from lioncash/aliases
ARMEmitter: Add bfc/bfxil aliases
2023-07-30 18:49:01 -07:00
Lioncache 8f48021a63 ARMEmitter: Add bfxil alias 2023-07-30 21:18:20 -04:00
Lioncache 1da5d7f2e6 ARMEmitter: Add bfc alias 2023-07-30 21:18:17 -04:00
Ryan Houdek 0a5db6c404 Merge pull request #2843 from Sonicadvance1/implement_socketcall_recvsendmmsg
Linux: Implement {recv,send}mmsg inside of socketcall
2023-07-30 18:04:44 -07:00
Ryan Houdek 4681061011 Merge pull request #2844 from lioncash/aliases
ARMEmitter: Add sbfiz/tst/ubfiz aliases
2023-07-30 18:01:34 -07:00
Lioncache 4e9eeb1a16 ARMEmitter: Add SBFIZ/UBFIZ aliases 2023-07-30 20:19:37 -04:00
Lioncache 95d728b634 ARMEmitter: Add TST aliases 2023-07-30 19:36:56 -04:00
Ryan Houdek 187e551a0c Linux: Implement {recv,send}mmsg inside of socketcall
These are the last two missing socketcall operations. Implementing them
is mostly using the other implementation just in a helper function.
2023-07-30 15:45:01 -07:00
Ryan Houdek 7c4e4c4409 Merge pull request #2842 from Sonicadvance1/syscall_accept4
Linux: Implement accept4 inside of socketcall
2023-07-30 15:44:18 -07:00
Ryan Houdek bdd8df2100 Linux: Implement accept4 inside of socketcall
Steam started using this recently. So implement it.
Still missing recvmmsg and sendmmsg here.
2023-07-30 15:11:45 -07:00
Ryan Houdek 1d1bdfb96d OpcodeDispatcher: Fixes SHRD by immediate OF flag calculation
We were calculating this like the regular SHR instruction which isn't
correct.

With this resolved, Denuvo games get slightly farther.
2023-07-28 18:00:50 -07:00
Ryan Houdek 2fc6542d15 Merge pull request #2838 from lioncash/imm
ARMEmitter: Finish off remaining SVE Integer Wide Immediate - Unpredicated categories
2023-07-28 17:57:23 -07:00
Lioncache 5e88be3c99 ARMEmitter: Handle SVE MUL immediate (unpredicated) 2023-07-28 19:55:06 -04:00
Lioncache be52844ce0 ARMEmitter: Handle SVE MIN/MAX immediate (unpredicated) 2023-07-28 19:47:32 -04:00
Lioncache d629884147 ARMEmitter: Handle SVE ADD/SUB immediate (unpredicated) 2023-07-28 19:23:06 -04:00
Ryan Houdek 64c72430bf Merge pull request #2837 from lioncash/predicate
Arm64/Emitter: Add remaining missing SVE predicate range assertions
2023-07-28 14:44:43 -07:00
Lioncache 1b13e8db7d Arm64/Emitter: Simplify SVE constructive prefix
Moves the assert and op definition into the implementing function.
Also enforces valid use of predicate registers.
2023-07-28 17:17:51 -04:00
Ryan Houdek 1ce0ea8f3b Merge pull request #2835 from Sonicadvance1/cmn_alias
ARMEmitter: Implement cmn alias
2023-07-28 14:15:01 -07:00
Lioncache 8557656259 Arm64/Emitter: Simplify SVE FP unary operations predicated group
Moves the asserts into the implementation function and also enforces
valid use of predicates.
2023-07-28 17:07:15 -04:00
Ryan Houdek c87c04db59 ARMEmitter: Implement cmn alias
Inspired by #2832, since it was using adds directly.
cmn is an alias of adds with the destination register being zr.
2023-07-28 13:58:23 -07:00
Ryan Houdek f5262446a0 Merge pull request #2833 from Sonicadvance1/remove_erroneous_asserts
FEXCore: Remove erroneous asserts in the project
2023-07-28 13:53:42 -07:00
Ryan Houdek 0a1820d444 Merge pull request #2834 from lioncash/sveops
Arm64/Emitter: Deduplicate some more SVE implementations pt. 2
2023-07-28 13:50:12 -07:00
Ryan Houdek a255813e99 FEXCore: Remove erroneous asserts in the project
Inspired by #2832 by going through the source and removing uses of
`assert`.

assert doesn't work for us in debug builds because some games will
capture SIGABRT and continue running. So we need to use FEX's built in
assert handlers which call `FEX_TRAP_EXECUTION` which will take down the
FEX process as expected.

There wasn't too much usage of this in the source, so this is relatively
straightforward.
2023-07-28 13:36:05 -07:00
Lioncache 4c6336b560 Arm64/Emitter: Simplify load/store contiguous (scalar plus scalar) group
Lets us move the base opcode into the implementing function.
While we're at it, we can also add an assert to enforce predicate ranges.
2023-07-28 16:30:34 -04:00
Lioncache 145c7799a5 Arm64/Emitter: Simplify load/store contiguous (scalar plus immediate) group
We can move asserts and the base opcode into the implementing function.
While we're at it, we can add an assert to ensure predicate registers are in range.
2023-07-28 16:30:34 -04:00
Lioncache 91df503e55 Arm64/Emitter: Simplify load/store multiple contiguous (scalar plus immediate) group
We can centralize the base opcode and some of the asserts in the implementing function.
We can also add an assert that validates the predicate register range
and also make the offset assertions much more informative.
2023-07-28 16:30:30 -04:00
Lioncache 3c417cee84 Arm64/Emitter: Reduce qualifying in SVE FP/Int Convert To FP/Int groups
Since everything we need is already in the ARMEmitter namespace, we
don't need to qualify all type usages, which reduces the verbosity
a little during reading.
2023-07-28 14:57:43 -04:00
Lioncache 857780b46e Arm64/Emitter: Fix sorting of SVE integer to float group
This is actually already implemented but was grouped with the
SVE floating-point convert to integer group. We can extract this
out to be organized a little nicer.
2023-07-28 14:52:57 -04:00
Lioncache cb34aa3dee Arm64/Emitter: Simplify SVE FP/Int Convert To FP/Int groups
We can move the base opcode and asserts into the implementing function.
We can also add an assert to ensure the predicate is in a valid range.
2023-07-28 14:44:54 -04:00
Lioncache 0d74777954 Arm64/Emitter: Simplify SVE FP Unary Operations Predicated group
We can move the op into the implementing function and get rid of
an unnecessary function.

We can also add an assert to ensure the predicates are valid as well.
2023-07-28 14:24:28 -04:00
Ryan Houdek 092a023900 OpcodeDispatcher: Use VZip to make merge more optimal in PSAD
Thanks to @dougallj for noticing this.
Fixes #2818

New ASM
```asm
0x00007fffe2080808  10ffffe0            adr x0, #-0x4 (addr 0x7fffe2080804)
0x00007fffe208080c  f9005f80            str x0, [x28, #184]
0x00007fffe2080810  3dc03cc4            ldr q4, [x6, #240]
0x00007fffe2080814  2e247305            uabdl v5.8h, v24.8b, v4.8b
0x00007fffe2080818  6e247304            uabdl2 v4.8h, v24.16b, v4.16b
0x00007fffe208081c  4e71b8a5            addv h5, v5.8h
0x00007fffe2080820  4e71b884            addv h4, v4.8h
0x00007fffe2080824  4ec438b8            zip1 v24.2d, v5.2d, v4.2d
0x00007fffe2080828  58000040            ldr x0, pc+8 (addr 0x7fffe2080830)
0x00007fffe208082c  d63f0000            blr x0
```
2023-07-28 10:56:59 -07:00
Ryan Houdek b57dd5c3aa OpcodeDispatcher: Optimize PSAD* to use vuabdl{2,}
Optimizes this instruction from 18 down to 12 instructions.

Still has some extraneous moves at the tail of the operation that don't need to exist.

Before:
```asm
0x00007fffe2080908  10ffffe0            adr x0, #-0x4 (addr 0x7fffe2080904)
0x00007fffe208090c  f9005f80            str x0, [x28, #184]
0x00007fffe2080910  3dc03cc4            ldr q4, [x6, #240]
0x00007fffe2080914  2f08a705            uxtl v5.8h, v24.8b
0x00007fffe2080918  6f08a706            uxtl2 v6.8h, v24.16b
0x00007fffe208091c  2f08a487            uxtl v7.8h, v4.8b
0x00007fffe2080920  6f08a484            uxtl2 v4.8h, v4.16b
0x00007fffe2080924  6e6784a5            sub v5.8h, v5.8h, v7.8h
0x00007fffe2080928  6e6484c4            sub v4.8h, v6.8h, v4.8h
0x00007fffe208092c  4e60b8a5            abs v5.8h, v5.8h
0x00007fffe2080930  4e60b884            abs v4.8h, v4.8h
0x00007fffe2080934  4e71b8a5            addv h5, v5.8h
0x00007fffe2080938  4e71b884            addv h4, v4.8h
0x00007fffe208093c  4ea51ca0            mov v0.16b, v5.16b
0x00007fffe2080940  6e180480            mov v0.d[1], v4.d[0]
0x00007fffe2080944  4ea01c18            mov v24.16b, v0.16b
0x00007fffe2080948  58000040            ldr x0, pc+8 (addr 0x7fffe2080950)
0x00007fffe208094c  d63f0000            blr x0
```

After:
```asm
0x00007fffe2080848  10ffffe0            adr x0, #-0x4 (addr 0x7fffe2080844)
0x00007fffe208084c  f9005f80            str x0, [x28, #184]
0x00007fffe2080850  3dc03cc4            ldr q4, [x6, #240]
0x00007fffe2080854  2e247305            uabdl v5.8h, v24.8b, v4.8b
0x00007fffe2080858  6e247304            uabdl2 v4.8h, v24.16b, v4.16b
0x00007fffe208085c  4e71b8a5            addv h5, v5.8h
0x00007fffe2080860  4e71b884            addv h4, v4.8h
0x00007fffe2080864  4ea51ca0            mov v0.16b, v5.16b
0x00007fffe2080868  6e180480            mov v0.d[1], v4.d[0]
0x00007fffe208086c  4ea01c18            mov v24.16b, v0.16b
0x00007fffe2080870  58000040            ldr x0, pc+8 (addr 0x7fffe2080878)
0x00007fffe2080874  d63f0000            blr x0
```
2023-07-28 10:56:59 -07:00
Ryan Houdek 0bea508935 IR: Adds support for VUABDL2 operation
FEX already supported VUABDL, but we had missed VUABDL2.
2023-07-28 10:56:59 -07:00
Ryan Houdek 9c175da0e2 Merge pull request #2831 from lioncash/moreop
Arm64/Emitter: Deduplicate some more SVE implementations
2023-07-28 10:47:50 -07:00
Lioncache cc359f09ad Arm64/Emitter: Simplify SVE FP convert precision odd elements
We can move the base opcode into the implementation function.
We can also add an assert to ensure that the predicate is in a valid range.
2023-07-28 13:34:04 -04:00
Lioncache dedeba0575 Arm64/Emitter: Simplify SVE2 Character Match group
We can move the base opcode into the implementation function.
2023-07-28 13:34:04 -04:00
Lioncache dc3ccbcc43 Arm64/Emitter: Simplify SVE SEL instruction
We can move the base opcode and asserts into the implementing function.
We can also implement the mov alias in terms of sel()
2023-07-28 13:34:04 -04:00
Lioncache ebd9b49dbe Arm64/Emitter: Simplify SVE2 bitwise ternary operations category
We can move the asserts into the implementing function.
2023-07-28 13:34:04 -04:00
Lioncache 43e21ad749 Arm64/Emitter: Simplify SVE permute vector elements category
We can move the asserts and the base opcode into the implementing function.
2023-07-28 13:34:04 -04:00
Lioncache 1b3beec085 Arm64/Emitter: Add missing SVE TBL encoding
Adds the missing SVE2 double table encoding.

Crosses off a TODO.
2023-07-28 13:34:04 -04:00
Lioncache c88f2e0730 Arm64/Emitter: Simplify SVE table lookup category
We can move the base opcode into the implementing function.
2023-07-28 13:34:04 -04:00
Lioncache 1645f6e582 Arm64/Emitter: Simplify SVE integer add/sub vectors unpredicated category
We can move the base op encoding into the implementing function.
2023-07-28 13:34:04 -04:00
Mai af35e18979 Merge pull request #2830 from Sonicadvance1/fix_smc_race
FEXLinuxTests: Fixes race in smc-mt-2
2023-07-28 13:23:08 -04:00
Ryan Houdek 012c0ae062 FEXLinuxTests: Fixes race in smc-mt-2
This test was written to test SMC where one thread is doing execution
while the other thread is modifying.

According to the printf documentation it is supposed to "wait for code
to be modified" but actually it was testing a race between a printf on
one thread and the primary thread modifying the code.

Fix this test so it is actually waiting for code modification to happen
rather than testing a race condition. This is likely what the original
author intended.

CI is hitting this flake more frequently now because it is even faster
it seems, so fixing this test is necessary to resolve these flakes.
2023-07-27 19:24:57 -07:00
Ryan Houdek dc8f063a4b Merge pull request #2823 from Sonicadvance1/fix_mingw_build
Mingw: Fixes compiling again
2023-07-27 18:14:07 -07:00
Ryan Houdek 759747b3c5 Merge pull request #2828 from lioncash/unused
LongDivideRemovalPass: Remove unused variable
2023-07-27 16:27:56 -07:00
Lioncache 4c6d26f167 LongDivideRemovalPass: Remove unused variable 2023-07-27 19:13:29 -04:00
Ryan Houdek 7562ff2308 Merge pull request #2827 from lioncash/alias
Context: Pull out some long std::function declarations into aliases
2023-07-27 16:11:20 -07:00
Lioncache e2baa1106b Context: Make alias for code invalidation functor
Makes it so any changes wouldn't need to go across everything implementing the Context class.
2023-07-27 18:59:15 -04:00
Lioncache 68880fd66b Context: Make alias for custom IR handlers
Puts the type declaration in one place.
2023-07-27 18:43:19 -04:00
Lioncache 62eef6c548 AOTIR: std::move function instances where applicable
Ensures no unnecessary allocations occur.
2023-07-27 18:35:30 -04:00
Lioncache cfdc1bda47 AOTIR: Add aliases for long functors
Puts the really long declarations in one spot, which makes them
nicer to change over time.
2023-07-27 18:30:07 -04:00
Ryan Houdek f9a9645ef3 Merge pull request #2826 from lioncash/unused
Frontend: Remove unused ModRMDecoded instance
2023-07-27 14:30:19 -07:00
Lioncache 8fc903cc51 Frontend: Remove unused ModRMDecoded instance
This is assigned to but never used.
2023-07-27 17:18:16 -04:00
Ryan Houdek c81b1c3432 Merge pull request #2825 from lioncash/lookup
Frontend: Remove redundant lookups in BranchTargetInMultiblockRange()
2023-07-27 14:00:40 -07:00
Lioncache db9ba16a53 Frontend: Remove redundant lookups in BranchTargetInMultiblockRange()
insert() will only ever perform an insertion if the relevant element
doesn't exist within the set, so we were doing an unnecessary lookup
in two spots.

We don't use emplace() here because it will need to construct the
key for the element inside the allocated node (since the key may
be non-copyable/non-movable), causing an allocation even if an insert
doesn't actually occur.

Conversely, insert() will not need to allocate and construct a node
ahead of time if an element already exists in the set.
2023-07-27 16:28:37 -04:00
Ryan Houdek 0be68a54d0 Merge pull request #2824 from lioncash/opsimp
Arm64/Emitter: Reorganize some base opcode and assert locations
2023-07-27 13:18:23 -07:00
Lioncache 6e140e9ff2 Arm64/Emitter: Simplify SVE Integer Compare With Unsigned Immediate category
We can move the asserts into the implementation function.
2023-07-27 14:12:16 -04:00
Lioncache d793f7295a Arm64/Emitter: Add missing cmpne signed immediate op
Noticed while simplifying the signed immediate implementation.
2023-07-27 14:08:26 -04:00
Lioncache 8f6018a1b4 Arm64/Emitter: Simplify SVE Integer Compare With Signed Immediate category
We can move all asserts and handling into the implementation function.
2023-07-27 14:03:34 -04:00
Lioncache 0a97d4456d Arm64/Emitter: Simplify SVE Predicate Logical Operations category
We can move the base opcode into the implementation function.
2023-07-27 13:51:48 -04:00
Lioncache c2dbe8309b Arm64/Emitter: Simplify SVE Bitwise Logical Operations Predicated category
We can move the asserts and base opcode into the implementation function.
We can also add an assert to ensure a valid predicate range.
2023-07-27 13:41:49 -04:00
Lioncache 06d66a0188 Arm64/Emitter: Simplify SVE Integer Mul/Div Vectors Predicated category
We can move the base opcode into the implementation function.
We can also move the division assert into it as well.
2023-07-27 13:36:22 -04:00
Lioncache ef254da7ca Arm64/Emitter: Simplify SVE Integer Min/Max/Difference Predicated category
We can move the asserts into the implementation function and also add
another assert to validate the predicate range.
2023-07-27 13:29:15 -04:00
Lioncache 162dcf3cd0 Arm64/Emitter: Simplify SVE Integer Add/Sub Vectors Predicated category
We can move the opcode into the implementation function.
2023-07-27 13:19:56 -04:00
Lioncache dcc47074d1 Arm64/Emitter: Simplify SVE Integer Multiply-Add Predicated category
We can move the op definition into the implementation function.
2023-07-27 13:16:11 -04:00
Lioncache 4dfee31012 Arm64/Emitter: Simplify SVE FP Recursive Reduction category
We can move the op constant into the implementation function.
2023-07-27 13:10:12 -04:00
Lioncache 3749fa6569 Arm64/Emitter: Simplify SVE Float Arithmetic Unpredicated category
We can move the common assert and op value into the implementation function.
2023-07-27 13:07:46 -04:00
Mai 94273fbf4f Merge pull request #2760 from Sonicadvance1/switch_to_half_barriers
Arm64: Switch to using half barriers
2023-07-27 09:07:14 -04:00
Ryan Houdek 162bbf2937 Mingw: Fixes compiling again
Getting the CI machines setup to handle this so we can stop breaking it.
2023-07-26 16:52:19 -07:00
Mai 296adf1830 Merge pull request #2822 from Sonicadvance1/fix_struct_pack_verifier_cursor
StructPackVerifier: Fixes missing cursorkind again
2023-07-26 14:47:43 -04:00
Ryan Houdek a86109280a StructPackVerifier: Fixes missing cursorkind again
python-clang is getting pretty bad with missing cursor kinds these days.
This was hit from clang-15, will be required for new CI runners
2023-07-26 11:21:16 -07:00
Mai d83960d4dd Merge pull request #2821 from Sonicadvance1/rename_valuenode
RCLSE: Rename `Node` to `ValueNode`
2023-07-25 16:06:12 -04:00
Ryan Houdek bba97823a8 RCLSE: Rename Node to ValueNode
Every time I look at this I can never remember which node is the value
node and which one is the store node.

Rename the opaque `Node` to `ValueNode` and have documentation comments
to explain which one it is. This way I won't forget again.
2023-07-25 12:19:15 -07:00
Ryan Houdek abf5e8c6a5 Merge pull request #2820 from alyssarosenzweig/pf/shift
Preserve PF across zero shift
2023-07-25 08:43:16 -07:00
Alyssa Rosenzweig 8d288300c4 unittests: Add test for preserving PF across zero shift
Tests for the regression from 7e6bb04db ("OpcodeDispatcher: Extract
CalculatePF"). This fails on main but passes with this PR.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-25 11:21:52 -04:00
Alyssa Rosenzweig 0caf263c77 OpcodeDispatcher: Preserve PF across zero shifts
Reported by CATFELLA on Discord. uwu

Fixes: 7e6bb04db ("OpcodeDispatcher: Extract CalculatePF")
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-25 11:21:52 -04:00
Ryan Houdek 29dc77c44b Merge pull request #2819 from alyssarosenzweig/const/bfi
ConstProp: Handle constant Bfi
2023-07-25 07:34:57 -07:00
Alyssa Rosenzweig 4c1f53c1ff ConstProp: Handle constant Bfi
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-25 10:19:56 -04:00
Ryan Houdek 5821175ddb Merge pull request #2813 from Sonicadvance1/fix_ra_lr
Arm64: Fixes LR corruption in 128-bit divides
2023-07-24 15:08:45 -07:00
Ryan Houdek 003c88e537 Merge pull request #2816 from alyssarosenzweig/ir/bump-16
IR: Expand to 16-bit opcodes
2023-07-24 15:08:36 -07:00
Alyssa Rosenzweig 27a1ebc2f5 IR: Expand to 16-bit opcodes
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-24 17:21:11 -04:00
Ryan Houdek e689c6fcfa Merge pull request #2814 from lioncash/shift
Arm64/Emitter: Simplify SVE immediate shift helper
2023-07-24 12:28:53 -07:00
Ryan Houdek 61e905a339 Arm64: Fixes LR corruption in 128-bit divides
Need to save and restore LR before branching out to the helpers.
Confirmed that the rest of the JIT handles this correctly.
2023-07-24 12:04:16 -07:00
Lioncache 10fdcaa109 Arm64/Emitter: Simplify SVE immediate shift helper
We can collapse the entire if statement to be much simpler.
2023-07-24 15:04:14 -04:00
Ryan Houdek ba672f868c Arm64: Switch to using half barriers
Inspired from: https://github.com/dotnet/runtime/issues/8072

Currently FEX is /very/ heavy handed with our backpatching where we wrap
every backpatched loadstore with `dmb ish`.

This can be relaxed slightly according to the linked issue.

For TSO load instructions the instruction sequence changes to:
  ldr <args>;
  dmb ld; <-- Slightly less strict dmb

For TSO store instructions the instruction sequence changes to:
  dmb ish; <-- Still the all encompassing dmb
  str <args>;

For backpatching loadstores this does the same thing where only one side
needs the nop and it uses the same instruction sequence when
backpatched.

The minor change is that on load backpatching, we are no longer backing
up a single instruction, instead just re-executing the instruction we
patched directly.

Took a long time to come back to this (Last looked in August 2020).
Previously when I was implementing this idea it didn't work, but that
was because our CompareExchange operation was broken back then. With the
CAS now, it should just work.
2023-07-24 11:06:20 -07:00
Ryan Houdek d6697fce32 Merge pull request #2812 from lioncash/dup
Arm64/Emitter: Collapse encoding cases for indexed dup
2023-07-24 11:06:04 -07:00
Lioncache 9c3a843df7 Arm64/Emitter: Move indexed dup handling into SVEDup
SVEDup is only used by dup(), so we can move all the implementation
details into it instead of keeping it all in the public function.
2023-07-24 13:53:57 -04:00
Lioncache 8defa2b55f Arm64/Emitter: Collapse encoding cases for indexed dup
Lets us hoist out the asserts and also collapse all the
branching into one series of operations.
2023-07-24 13:53:42 -04:00
Ryan Houdek 77c88ffe53 Merge pull request #2804 from alyssarosenzweig/eor-zero
Optimize `xor %eax, %eax`
2023-07-24 09:01:53 -07:00
Ryan Houdek 5b261b0d2e Merge pull request #2809 from lioncash/assert
Arm64/VectorOps: Hoist asserts out of VInsElement cases
2023-07-24 09:00:41 -07:00
Alyssa Rosenzweig 2ce15ddc89 OpcodeDispatcher: Partially defer PF calculation
We expect that PF is written more often than it's read, so we want to
get the expensive popcount out of the hot path. (Thank you to Dougall
for suggesting that.)

There are two cases:

1. PF is written by an integer instruction. In this case, we calculate
   with the formula `popcount(x ^ 1) & 1`.
2. PF is written by a float instruction, copying a host flag.

What we really want is to defer the relatively expensive popcount. So,
to unify these cases, we have integer instructions write `x ^ 1` and
(unchanged) float instructions write the host flag. Then, when reading
PF, we do `popcount(value) & 1` on the byte read in.

If PF is written but not read, this saves the expensive popcount and
leaves only the cheap xor.

If PF is written by an integer op and read, this maybe shuffles some
code but does not materially change anything.

If PF is written by a float op and read, this is worse because now we're
doing an extra pointless popcount. This is a tradeoff... However, this
is only relevant to unordered float comparisons, which I expect to be
obscure for games. So this should be worth it over all (for games, if
not weird numerical computing workloads).

How does this connect to my register zeroing quest? The constant folding
code doesn't currently deal with FPRs and I'm not in a mood to change
this. So before, a block ending with `xor eax, eax` would still do a
popcount for PF. Now it just writes a constant 1 since the xor constant
folds and the popcount never happens at all.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-24 11:17:31 -04:00
Alyssa Rosenzweig 3d1b55383e ConstProp: Handle Select::EQ
For flag calculation after moving a constant. This cleans up the code
generated for zeroing at the end of a block.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-24 11:17:31 -04:00
Alyssa Rosenzweig 69deaa0976 LongDivideRemovalPass: Don't detect xor zero
It is now optimized out (canonicalized) in the frontend.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-24 11:17:31 -04:00
Alyssa Rosenzweig fc72fa9e5f OpcodeDispatcher: Optimize xor zeroing
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-24 11:17:31 -04:00
Mai 6baee3b7c1 Merge pull request #2808 from Sonicadvance1/timeout_github_upload
github-actions: Adds timeout to upload results
2023-07-24 11:14:06 -04:00
Lioncache 362a5a019a Arm64/VectorOps: Move VInsElement size assert checks to be first
Ensures the asserts will be hit first before emitting code.
2023-07-24 11:08:35 -04:00
Lioncache d529a58893 Arm64/VectorOps: Hoist asserts out of VInsElement cases
Lets us deduplicate the asserts and put them in one spot. We can also
improve the assert message to also indicate the valid range.

While we're in the area, we can collapse a few case paths
as a result of this assert movement.
2023-07-24 11:05:13 -04:00
Ryan Houdek c77ea3b392 github-actions: Adds timeout to upload results
Usually uploading of results takes about two seconds.
Sometimes github's connection to the runner flakes and it stalls out the
upload action for some reason.

Github's default timeout is SIX HOURS.
Change this to a one-minute timeout on the upload step so it quickly
goes away when Github's internet flakes out.
2023-07-21 15:32:36 -07:00
Ryan Houdek 20aaad15e4 Merge pull request #2806 from Sonicadvance1/for_2804
X86Tables: Adds spaceship operator to couple op types
2023-07-21 15:29:58 -07:00
Ryan Houdek 6f2452e2ea Merge pull request #2807 from Sonicadvance1/gvisor_socket_flakes
unittests/gvisor: Adds all socket tests to flakes
2023-07-21 15:26:51 -07:00
Ryan Houdek b34401bb33 Merge pull request #2805 from bylaws/win32-fix
FHU: Avoid calling faccessat on WIN32
2023-07-21 15:00:48 -07:00
Ryan Houdek dff9868e9a unittests/gvisor: Adds all socket tests to flakes
All of these flake so slam them all in the flake file.
2023-07-21 14:50:58 -07:00
Ryan Houdek 036a196984 X86Tables: Adds spaceship operator to couple op types
For #2804 so it can compare if GPRs match more easily.

Only adding to GPR and Literal types, since its ambiguous what this
would mean for the memory accessing types. Going to leave those other
ones alone for now.
2023-07-21 14:42:01 -07:00
Billy Laws a7ac4fa6e4 FHU: Avoid calling faccessat on WIN32 2023-07-21 22:21:15 +01:00
Ryan Houdek 82295b2943 Merge pull request #2763 from Sonicadvance1/fix_x11_variadic_thunks
Thunks/X11: Fixes variadic packing and callbacks.
2023-07-21 01:49:26 -07:00
Ryan Houdek eca80b6046 Remove inline assembly
64 should be enough for anybody.
2023-07-20 16:05:25 -07:00
Ryan Houdek 02fc02964b Thunks/LibX11: Removes variadic multiple argument copying
Just copy one at a time.
2023-07-20 13:52:30 -07:00
Ryan Houdek e8fcb070b3 Thunks/X11: Fixes variadic packing and callbacks.
Two bugs here that caused thunking X11 thunking in Wine/Proton to not
work.

The easier of the two. The various variadic functions that we thunk
actually take key:value pairs where the first is a string pointer, and
the value can be various things.

We need to handle these as true key:value pairs rather than finding the
first nullptr and dropping the remainder.

Additionally, there are 12 keys that specify a callback that FEX needs
to catch and convert to host callable. Wine is the first application
that I have seen that actually uses this. If these callbacks aren't
wired up then it it can miss events.

The harder of the two problems is the `libX11_Variadic_u64` function was
subtly incorrect. Nothing had previously truly exercised this and my
test program didn't notice anything wrong while writing it.

The first incorrect thing was that it was subtracting the nullptr ender
variable before the stack size calculation, causing the value to
overwrite the stack if the number of remaining elements was event.

Secondly the assembly that was storing two elements per step was
decrementing the counter by 8 instead of two. Didn't pick this up before
since I believe the code was only hitting the non-pair path before.

This gets Proton thunking working under FEX now.
2023-07-20 13:52:30 -07:00
Ryan Houdek 597da88035 Merge pull request #2803 from Sonicadvance1/xcb_x11_dependency
ThunksDB: Adds X11 dependency to XCB
2023-07-19 15:45:47 -07:00
Ryan Houdek 1e829a47fa ThunksDB: Adds X11 dependency to XCB
I was hitting an issue where thunking in Wine+Vulkan applications was breaking
unless both OpenGL and Vulkan was enabled.

Turns out this was because Vulkan enabled only XCB, which didn't enable
X11. So when XCB is thunked but X11 isn't, this causes weird issues
where X11 calls in to XCB functions and gets in desync'ed state. Causing
hangs to appear in xcb_take_socket.

Now we can enabled just `Vulkan` as a thunk and it'll work fine.

```
(gdb) bt
   from target:/usr/lib/fex-emu/HostThunks//libvulkan-host.so
```
2023-07-19 15:23:07 -07:00
Ryan Houdek e633ef7cf5 Merge pull request #2801 from lioncash/cvt
Arm64/Emitter: Handle SVE FP convert precision group
2023-07-19 13:45:56 -07:00
Ryan Houdek ff3b40400c Merge pull request #2795 from Sonicadvance1/remove_thunk_symbol
Thunks: Remove weak symbol definitions
2023-07-19 13:45:36 -07:00
Lioncache 801106faf4 Arm64/Emitter: Handle SVE FP convert precision group 2023-07-19 15:24:54 -04:00
Ryan Houdek 536b2ed495 Merge pull request #2783 from Sonicadvance1/optimize_loadstorecontextindexed
Arm64: Optimize {Load,Store}ContextIndexed address generation
2023-07-19 12:16:39 -07:00
Alyssa Rosenzweig 072f027885 Merge pull request #2784 from Sonicadvance1/optimize_small_rcr
OpcodeDispatcher: Optimize 8/16-bit RCR
2023-07-19 15:07:58 -04:00
Ryan Houdek 754bc18813 Merge pull request #2800 from lioncash/fpa
Arm64/Emitter: Handle SVE FP arithmetic with immediate (predicated) group
2023-07-19 11:01:02 -07:00
Lioncache ef257418d3 Arm64/Emitter: Handle SVE FP arithmetic with immediate (predicated) group 2023-07-19 13:37:33 -04:00
Ryan Houdek 842b71cf83 Merge pull request #2799 from lioncash/xar
Arm64/Emitter: Handle SVE XAR
2023-07-19 09:39:30 -07:00
Ryan Houdek 8d8b64d2b7 Merge pull request #2798 from bylaws/dealock
Jit: Add block links directly through the lookup cache on thread exit
2023-07-19 09:37:57 -07:00
Lioncache 85c6ef8097 Arm64/Emitter: Handle SVE XAR
Now that we have the helper for encoding immediate shifts,
we can trivially implement the remaining missing instruction
in the bitwise logical unpredicated group.
2023-07-19 12:26:05 -04:00
Billy Laws 2be16d9054 Jit: Add block links directly through the lookup cache on thread exit
Prevents the code invalidation mutex from being locked as shared recursively,
since it is locked before entering ThreadExitFunctionLink and would end up
being locked again by ThreadAddBlockLink.

This fixes a deadlock on Windows.
2023-07-19 17:13:17 +01:00
Ryan Houdek 41b3c52663 Merge pull request #2797 from lioncash/shift
Arm64/Emitter: Add helper for encoding SVE shift immediates
2023-07-19 09:10:21 -07:00
Lioncache f1f50b7a98 Arm64/Emitter: Add helper for encoding SVE shift immediates
This lets us deduplicate the behavior rather than open-coding it everywhere
and also makes it nicer to implement instructions that make use of this
encoding pattern.
2023-07-19 11:39:28 -04:00
Ryan Houdek d7200e2a1e Merge pull request #2796 from lioncash/unary
Arm64/Emitter: Deduplicate some opcode values
2023-07-19 06:56:12 -07:00
Lioncache 3f884fe2d0 Arm64/Emitter: Deduplicate ops in SVE Bitwise Logical - Unpredicated
Same behavior, with less code.
2023-07-19 09:27:13 -04:00
Lioncache 1d453f10a0 Arm64/Emitter: Remove unnecessary qualifiers from integer unary arith ops
While we're in the area, we can make these much quicker to read by
removing the unnecessary qualifiers.
2023-07-19 09:18:36 -04:00
Lioncache 5ebd21ca6a Arm64/Emitter: Deduplicate integer unary arithmetic instructions
We can move the opcodes into the underlying implementation along with
the asserts to deduplicate a bit of code.
2023-07-19 09:16:33 -04:00
Ryan Houdek fb34b507a1 Thunks: Remove weak symbol definitions
Fixes #2754

These panicking fallbacks are at times not ending up in as plt calls
for some reason that I haven't been able to reproduce locally.

So far the only way I can reproduce is building with Canonical's PPA
build system, since rebuilding locally didn't resolve the issue.

This will change the failure mode from these panicking asserts happening
at call time, to dlopen failing during relocation when loading the
thunk. Which LD_DEBUG=all can be used for debugging relocation failure
in that case.
2023-07-18 22:33:58 -07:00
Ryan Houdek 2c91b5cff6 Merge pull request #2788 from Sonicadvance1/enum_configs_disassembler
FEXCore/Config: Adds support for enum mask configuration array
2023-07-18 18:40:22 -07:00
Ryan Houdek 79f7dcbaa5 FEXCore/Config: Adds support for enum mask configuration array
Allows us to consume an array of strings and convert it to an mask of
enum values. This is a quality of life change that allows us to specify
a mask of options.

The first configuration option added to support this is to control the
vixl disassembler. Now by default the vixl disassembler doesn't
disassemble any blocks and needs to be enabled individually.

eg:
```
FEXLoader --disassemble=blocks <args>
FEXLoader --disassemble=dispatcher <args>
FEXLoader --disassemble=blocks,dispatcher <args>
```

Has the additional convenience option of just passing in numbers as
well.

```
FEXLoader --disassemble=2 <args>
FEXLoader --disassemble=1 <args>
FEXLoader --disassemble=3 <args>
```

Also of course all of this works through environment variables.
```
FEX_DISASSEMBLE=blocks FEXInterpreter <args>
FEX_DISASSEMBLE=dispatcher FEXInterpreter <args>
FEX_DISASSEMBLE=blocks,dispatcher FEXInterpreter <args>
```

While only used fairly sparingly now, this is likely to have some
additional configurations using this in the future. Since we already
have some configs that are basically using enums, but just by doing
string comparisons.

This was asked for by a developer, so I figured I would throw it
together quick.
2023-07-18 18:17:43 -07:00
Ryan Houdek f7b7997c77 Merge pull request #2786 from Sonicadvance1/minor_fcmov_opt2
OpcodeDispatcher: Another FCMov minor optimization
2023-07-18 18:14:17 -07:00
Ryan Houdek 0674dfab0a Merge pull request #2787 from Sonicadvance1/icache_only_code
Arm64: Only clear icache for code
2023-07-18 18:14:05 -07:00
Ryan Houdek 6979dc9c4e Merge pull request #2794 from lioncash/decvsib
OpcodeDispatcher: Handle VSIB byte
2023-07-18 11:28:32 -07:00
Lioncache fe5f17d92e OpcodeDispatcher: Handle VSIB byte
Ensures that we handle the AVX2 VSIB byte in a decent way.

As is, we can't compute the [index * scale] variant portion
of the entire address operand, since the scale needs to act
on every element of the vector after sign extension.

What we can do though, is compute the base address and add
the displacement to it ahead of time though.
2023-07-18 13:56:38 -04:00
Ryan Houdek be71886990 Merge pull request #2793 from lioncash/unusedvar
OpcodeDispatcher: Remove unused member variables and reorganize
2023-07-18 08:32:47 -07:00
Lioncache 5043e5fbc0 OpcodeDispatcher: Move ShouldDump member into private section
Like with HandledLock, we can move this into the private section
and put an API around it for consistency.
2023-07-18 11:03:25 -04:00
Lioncache 2acfde3cad OpcodeDispatcher: Move CTX member into private section
This isn't used outside of the class.
2023-07-18 11:00:04 -04:00
Lioncache c1eeeaf688 OpcodeDispatcher: Move flag-related variables into private section
These are only used within the opcode dispatcher, so they can be private.
2023-07-18 10:58:09 -04:00
Lioncache f2b3229a87 OpcodeDispatcher: Move JumpTargets into private section
This is only used within the class, so it can be made private.
2023-07-18 10:52:48 -04:00
Lioncache e20bfc0701 OpcodeDispatcher: Move HandledLock boolean into private class section
This can be trivially hidden and have an API put around it.
2023-07-18 10:43:52 -04:00
Lioncache 4caee5c9be OpcodeDispatcher: Remove unused Current_Header and Current_HeaderNode variables
These aren't used outside of being assigned to, so they can be removed.
2023-07-18 10:40:26 -04:00
Ryan Houdek 46d3f283b8 Merge pull request #2792 from lioncash/strun
Arm64/Emitter: Handle unsized contiguous STR variants
2023-07-18 05:54:32 -07:00
Lioncache cee5512a56 Arm64/Emitter: Handle unsized contiguous STR variants
We handle the unsized load variants, so we should do the same with the stores.
2023-07-18 08:38:23 -04:00
Mai 8c53a373bf Merge pull request #2791 from Sonicadvance1/fix_xcb_version_typo
Thunks/xcb: Fixes typo in version check.
2023-07-18 07:36:14 -04:00
Ryan Houdek f73176d5dc Thunks/xcb: Fixes typo in version check.
Somehow this 14 turned in to a 4.
2023-07-18 04:16:11 -07:00
Ryan Houdek 80ae3e632d Arm64: Optimize {Load,Store}ContextIndexed address generation
All of these IR operations were being fairly inefficient in their
address calculation. All of these are known using power of 2 stride
indexing. So all of these can be converted from three instructions to
one.

These are always used for x87 stack accesses so each one gets an
improvement.

Before:
```asm
0x0000ffff6a800248  d2800200    mov x0, #0x10
0x0000ffff6a80024c  9b007e80    mul x0, x20, x0
0x0000ffff6a800250  8b000380    add x0, x28, x0
0x0000ffff6a800254  fd417805    ldr d5, [x0, #752]
```

After:
```asm
0x0000ffff91e80240  8b141380    add x0, x28, x20, lsl #4
0x0000ffff91e80244  fd417805    ldr d5, [x0, #752]
```
2023-07-17 22:59:33 -07:00
Ryan Houdek ed75c19324 OpcodeDispatcher: Optimize 8/16-bit RCR
The BFI cascades in this particular instruction weren't optimal.
Biggest improvement is the 8-bit version, while the 16-bit version gets
a minor improvement.

8-bit instruction count reduced from 38 to 29.
16-bit instruction count reduced from 34 to 28.

RCL can have a similar optimization done to it.
```asm
Before 16-bit:
0x0000ffff80a801e0  10ffffe0    adr x0, #-0x4 (addr 0xffff80a801dc)
0x0000ffff80a801e4  f9005f80    str x0, [x28, #184]
0x0000ffff80a801e8  d3403cb4    uxth x20, w5
0x0000ffff80a801ec  d3403cf5    uxth x21, w7
0x0000ffff80a801f0  394b0396    ldrb w22, [x28, #704]
0x0000ffff80a801f4  12001294    and w20, w20, #0x1f
0x0000ffff80a801f8  d2800017    mov x23, #0x0
0x0000ffff80a801fc  b3403eb7    bfxil x23, x21, #0, #16
0x0000ffff80a80200  b37002d7    bfi x23, x22, #16, #1
0x0000ffff80a80204  b36f3eb7    bfi x23, x21, #17, #16
0x0000ffff80a80208  b35f02d7    bfi x23, x22, #33, #1
0x0000ffff80a8020c  aa1703e0    mov x0, x23
0x0000ffff80a80210  b35e3ea0    bfi x0, x21, #34, #16
0x0000ffff80a80214  aa0003f5    mov x21, x0
0x0000ffff80a80218  b34e02d5    bfi x21, x22, #50, #1
0x0000ffff80a8021c  9ad426b7    lsr x23, x21, x20
0x0000ffff80a80220  b3403ee7    bfxil x7, x23, #0, #16
0x0000ffff80a80224  51000698    sub w24, w20, #0x1 (1)
0x0000ffff80a80228  9ad826b5    lsr x21, x21, x24
0x0000ffff80a8022c  d34002b5    ubfx x21, x21, #0, #1
0x0000ffff80a80230  7100069f    cmp w20, #0x1 (1)
0x0000ffff80a80234  9a9622b4    csel x20, x21, x22, hs
0x0000ffff80a80238  390b0394    strb w20, [x28, #704]
0x0000ffff80a8023c  d34f3ef4    ubfx x20, x23, #15, #1
0x0000ffff80a80240  d34e3af5    ubfx x21, x23, #14, #1
0x0000ffff80a80244  ca150294    eor x20, x20, x21
0x0000ffff80a80248  390b2f94    strb w20, [x28, #715]
0x0000ffff80a8024c  58000040    ldr x0, pc+8 (addr 0xffff80a80254)
0x0000ffff80a80250  d63f0000    blr x0
0x0000ffff80a80254  967da128    bl #-0x6097b60 (addr 0xffff7a9e86f4)
0x0000ffff80a80258  0000ffff    udf #0xffff
0x0000ffff80a8025c  00010023    unallocated (Unallocated)
0x0000ffff80a80260  00000000    udf #0x0
[DEBUG] RIP: 0x10020
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 34
[DEBUG] Blow-up Amt: 34x

After 16-bit:
0x0000ffffa7c801e0  10ffffe0            adr x0, #-0x4 (addr 0xffffa7c801dc)
0x0000ffffa7c801e4  f9005f80            str x0, [x28, #184]
0x0000ffffa7c801e8  d3403cb4            uxth x20, w5
0x0000ffffa7c801ec  d3403cf5            uxth x21, w7
0x0000ffffa7c801f0  394b0396            ldrb w22, [x28, #704]
0x0000ffffa7c801f4  12001294            and w20, w20, #0x1f
0x0000ffffa7c801f8  b37002d5            bfi x21, x22, #16, #1
0x0000ffffa7c801fc  b36f42b5            bfi x21, x21, #17, #17
0x0000ffffa7c80200  b35e42b5            bfi x21, x21, #34, #17
0x0000ffffa7c80204  9ad426b7            lsr x23, x21, x20
0x0000ffffa7c80208  b3403ee7            bfxil x7, x23, #0, #16
0x0000ffffa7c8020c  51000698            sub w24, w20, #0x1 (1)
0x0000ffffa7c80210  9ad826b5            lsr x21, x21, x24
0x0000ffffa7c80214  d34002b5            ubfx x21, x21, #0, #1
0x0000ffffa7c80218  7100069f            cmp w20, #0x1 (1)
0x0000ffffa7c8021c  9a9622b4            csel x20, x21, x22, hs
0x0000ffffa7c80220  390b0394            strb w20, [x28, #704]
0x0000ffffa7c80224  d34f3ef4            ubfx x20, x23, #15, #1
0x0000ffffa7c80228  d34e3af5            ubfx x21, x23, #14, #1
0x0000ffffa7c8022c  ca150294            eor x20, x20, x21
0x0000ffffa7c80230  390b2f94            strb w20, [x28, #715]
0x0000ffffa7c80234  58000040            ldr x0, pc+8 (addr 0xffffa7c8023c)
0x0000ffffa7c80238  d63f0000            blr x0
0x0000ffffa7c8023c  bd9cc128            unallocated (Unallocated)
0x0000ffffa7c80240  0000ffff            udf #0xffff
0x0000ffffa7c80244  00010023            unallocated (Unallocated)
0x0000ffffa7c80248  00000000            udf #0x0
[DEBUG] RIP: 0x10020
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 28
[DEBUG] Blow-up Amt: 28x

Before 8-bit:
0x0000ffffa92801e0  10ffffe0            adr x0, #-0x4 (addr 0xffffa92801dc)
0x0000ffffa92801e4  f9005f80            str x0, [x28, #184]
0x0000ffffa92801e8  d3401cb4            uxtb x20, w5
0x0000ffffa92801ec  d3401cf5            uxtb x21, w7
0x0000ffffa92801f0  394b0396            ldrb w22, [x28, #704]
0x0000ffffa92801f4  12001294            and w20, w20, #0x1f
0x0000ffffa92801f8  d2800017            mov x23, #0x0
0x0000ffffa92801fc  b3401eb7            bfxil x23, x21, #0, #8
0x0000ffffa9280200  b37802d7            bfi x23, x22, #8, #1
0x0000ffffa9280204  b3771eb7            bfi x23, x21, #9, #8
0x0000ffffa9280208  b36f02d7            bfi x23, x22, #17, #1
0x0000ffffa928020c  b36e1eb7            bfi x23, x21, #18, #8
0x0000ffffa9280210  b36602d7            bfi x23, x22, #26, #1
0x0000ffffa9280214  b3651eb7            bfi x23, x21, #27, #8
0x0000ffffa9280218  b35d02d7            bfi x23, x22, #35, #1
0x0000ffffa928021c  aa1703e0            mov x0, x23
0x0000ffffa9280220  b35c1ea0            bfi x0, x21, #36, #8
0x0000ffffa9280224  aa0003f5            mov x21, x0
0x0000ffffa9280228  b35402d5            bfi x21, x22, #44, #1
0x0000ffffa928022c  9ad426b7            lsr x23, x21, x20
0x0000ffffa9280230  b3401ee7            bfxil x7, x23, #0, #8
0x0000ffffa9280234  51000698            sub w24, w20, #0x1 (1)
0x0000ffffa9280238  9ad826b5            lsr x21, x21, x24
0x0000ffffa928023c  d34002b5            ubfx x21, x21, #0, #1
0x0000ffffa9280240  7100069f            cmp w20, #0x1 (1)
0x0000ffffa9280244  9a9622b4            csel x20, x21, x22, hs
0x0000ffffa9280248  390b0394            strb w20, [x28, #704]
0x0000ffffa928024c  d3471ef4            ubfx x20, x23, #7, #1
0x0000ffffa9280250  d3461af5            ubfx x21, x23, #6, #1
0x0000ffffa9280254  ca150294            eor x20, x20, x21
0x0000ffffa9280258  390b2f94            strb w20, [x28, #715]
0x0000ffffa928025c  58000040            ldr x0, pc+8 (addr 0xffffa9280264)
0x0000ffffa9280260  d63f0000            blr x0
0x0000ffffa9280264  bf062128            unallocated (Unallocated)
0x0000ffffa9280268  0000ffff            udf #0xffff
0x0000ffffa928026c  00010022            unallocated (Unallocated)
0x0000ffffa9280270  00000000            udf #0x0
[DEBUG] RIP: 0x10020
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 38
[DEBUG] Blow-up Amt: 38x

After 8-bit:
0x0000ffff9cc801e0  10ffffe0    adr x0, #-0x4 (addr 0xffff9cc801dc)
0x0000ffff9cc801e4  f9005f80    str x0, [x28, #184]
0x0000ffff9cc801e8  d3401cb4    uxtb x20, w5
0x0000ffff9cc801ec  d3401cf5    uxtb x21, w7
0x0000ffff9cc801f0  394b0396    ldrb w22, [x28, #704]
0x0000ffff9cc801f4  12001294    and w20, w20, #0x1f
0x0000ffff9cc801f8  b37802d5    bfi x21, x22, #8, #1
0x0000ffff9cc801fc  b37722b5    bfi x21, x21, #9, #9
0x0000ffff9cc80200  b36e46b5    bfi x21, x21, #18, #18
0x0000ffff9cc80204  b3778eb5    bfi x21, x21, #9, #36
0x0000ffff9cc80208  9ad426b7    lsr x23, x21, x20
0x0000ffff9cc8020c  b3401ee7    bfxil x7, x23, #0, #8
0x0000ffff9cc80210  51000698    sub w24, w20, #0x1 (1)
0x0000ffff9cc80214  9ad826b5    lsr x21, x21, x24
0x0000ffff9cc80218  d34002b5    ubfx x21, x21, #0, #1
0x0000ffff9cc8021c  7100069f    cmp w20, #0x1 (1)
0x0000ffff9cc80220  9a9622b4    csel x20, x21, x22, hs
0x0000ffff9cc80224  390b0394    strb w20, [x28, #704]
0x0000ffff9cc80228  d3471ef4    ubfx x20, x23, #7, #1
0x0000ffff9cc8022c  d3461af5    ubfx x21, x23, #6, #1
0x0000ffff9cc80230  ca150294    eor x20, x20, x21
0x0000ffff9cc80234  390b2f94    strb w20, [x28, #715]
0x0000ffff9cc80238  58000040    ldr x0, pc+8 (addr 0xffff9cc80240)
0x0000ffff9cc8023c  d63f0000    blr x0
0x0000ffff9cc80240  b2a75128    unallocated (Unallocated)
0x0000ffff9cc80244  0000ffff    udf #0xffff
0x0000ffff9cc80248  00010022    unallocated (Unallocated)
0x0000ffff9cc8024c  00000000    udf #0x0
[DEBUG] RIP: 0x10020
[DEBUG] Guest Code instructions: 1
[DEBUG] Host Code instructions: 29
[DEBUG] Blow-up Amt: 29x
```
2023-07-17 19:13:23 -07:00
Ryan Houdek 4a7fa7f2bc Merge pull request #2790 from lioncash/vsib
Frontend: Handle VSIB byte
2023-07-17 18:41:03 -07:00
Lioncache 98f51c47fa Frontend: Handle VSIB byte
Extends handling of the SIB byte to also handle the AVX2 VSIB byte.

While we're in the area, we can set up the gather instruction flags as well.
2023-07-17 16:16:21 -04:00
Ryan Houdek 724a8e13bf Merge pull request #2789 from lioncash/scatter
Arm64/Emitter: Handle ST1{*} scatter store variants
2023-07-17 09:20:06 -07:00
Mai d9b52fd67d Merge pull request #2785 from Sonicadvance1/32bit_mov_bitmask
ArmEmitter: Support 32-bit bitmask moves
2023-07-17 12:13:27 -04:00
Mai d3a2795106 Merge pull request #2781 from Sonicadvance1/optimize_phminposuw
OpcodeDispatcher: Minor optimization to phminposuw
2023-07-17 12:12:25 -04:00
Mai 3cd6c2d91a Merge pull request #2779 from Sonicadvance1/optimize_shiftd
OpcodeDispatcher: Optimize 32/64-bit SH{L,R}D with extr
2023-07-17 12:11:51 -04:00
Mai b86abfbccf Merge pull request #2778 from Sonicadvance1/move_tls_signal_frontend
SignalDelegator: Moves last TLS variable to the frontend
2023-07-17 12:10:58 -04:00
Mai ee66985ae0 Merge pull request #2777 from Sonicadvance1/deadstore_elimination
IR/Passes: Fixes DeadStoreElimination pass
2023-07-17 12:09:54 -04:00
Mai daeba0625f Merge pull request #2776 from Sonicadvance1/fix_constprop_mask
ConstProp: Fix shift mask in const-prop
2023-07-17 12:08:33 -04:00
Lioncache 5e6af25194 Arm64/Emitter: Handle ST1{*} Vector + Imm scatter stores 2023-07-17 12:05:27 -04:00
Lioncache 7f4528a6b0 Arm64/Emitter: Handle ST1{*} Scalar + Vector scatter stores 2023-07-17 11:11:30 -04:00
Ryan Houdek 776b7674e4 Arm64: Only clear icache for code
Currently we're clearing icache including the data that lives on the
tail of the block. Instead only clear the code that the was emitted and
not tail data.

Additionally only disasm the code rather than all the tail data as well,
as it gets unwieldy if viewing.
2023-07-16 22:24:01 -07:00
Ryan Houdek 24cb2610a2 OpcodeDispatcher: Another FCMov minor optimization
If we are loading exactly the flags we need from the RFLAGS (ensuring we
don't load the reserved flag in bit 1) then we don't need to do a mask
on the result.

Additionally there is some bad code-motion around selects that was
causing SBFE operations to occur on constants. Ensure that we const-prop
any SBFE operations to clean this up.

This PR along with #2783 causes FMOV blow-up to go from 41 instruction
to 31 instructions.
2023-07-16 21:37:54 -07:00
Ryan Houdek 54b7a43b95 ArmEmitter: Support 32-bit bitmask moves
Noticed this when inspecting some code that was moving constant
`0x80808080` in to a register. Was using two move instructions when it
could have used a single bitmask move.

This now checks to see if a constant can be 32-bit encoded in a logical
bitmask move and uses that.
2023-07-16 18:52:34 -07:00
Mai 1b1e9e0fb5 Merge pull request #2782 from Sonicadvance1/minor_fild_opt
OpcodeDispatcher: Minor optimization to FILD
2023-07-16 04:49:50 -04:00
Ryan Houdek ead43c6a51 OpcodeDispatcher: Minor optimization to FILD
Removes one instruction from FILD, or two instructions if the CPU
supports the CSSC extension.

Going from 47 instructions to 46/45.
2023-07-16 01:28:57 -07:00
Ryan Houdek e49de77225 IR: Implements a 2's complement Integer absolute
Supports CSSC extension.
2023-07-16 01:28:57 -07:00
Ryan Houdek 8f4fe39b7d Emitter: Adds support for CNEG instruction alias
tests included.
2023-07-16 00:25:52 -07:00
Ryan Houdek f250509718 OpcodeDispatcher: Minor optimization to phminposuw
This instruction doesn't match ARM semantics very well since it returns
the position of the minimum element.

But at the very least the insert in to the final instruction can be a
bit more optimal, Converts an 5 inst eor+mov+mov+mov+mov in to 2 inst
mov+mov.

This works because `VUMinV` already zero extends the vector so the
position only needs to be inserted at the end.
2023-07-15 23:23:32 -07:00
Ryan Houdek 6179c5a13e OpcodeDispatcher: Optimize 32/64-bit SH{L,R}D with extr
32-bit and 64-bit SH{L,R}D matches behaviour of EXTR. Optimize to using
this op in that case.
This converts the lsl+lsr+orr sequence in to a single extr instruction.

16-bit still goes down the old path.

Weirdly this code manages to have a bad insert for no reason? But
unrelated since this happens in the old code as well.

```
  %4(GPRFixed3) i64 = LoadRegister #0x0, #0x20, GPR, GPRFixed, u8:Tmp:Size
  %5(GPR0) i64 = LoadRegister #0x0, #0x8, GPR, GPRFixed, u8:Tmp:Size
  %6(GPRFixed0) i64 = Extr %5(GPR0) i64, %4(GPRFixed3) i64, #0x3e
```

Not sure why the SRA fails on that second LoadRegister.
2023-07-15 22:05:39 -07:00
Ryan Houdek 9e14a83442 SignalDelegator: Moves last TLS variable to the frontend
There was one holdout variable that was in a TLS object in FEXCore. Move
it to the frontend with the rest of the TLS variables.

Allows us to remove "Frontend" TLS management to be the only TLS
management.
2023-07-15 20:28:58 -07:00
Ryan Houdek 95bfd003d2 IR/Passes: Fixes DeadStoreElimination pass
This pass is currently doing nothing in main.
Ever since we have enforced that LoadContext/StoreContext doesn't touch
GPRs and FPRs, this has only been eliminating flags.

Remove that usage of LoadContext/StoreContext and replace with their
their replacement of LoadRegister/StoreRegister for tracking GPR and FPR
accesses.

Stripped from #2700 since this is safe to merge.
2023-07-14 22:22:26 -07:00
Ryan Houdek 08ca43c3c4 ConstProp: Fix shift mask in const-prop
Noticed while looking at #2700.

Testing doesn't currently see this as a bug but will once #2700 starts
optimizing StoreRegister+LoadRegister pairs.

Doesn't fix the issues in that PR, but this is one.
2023-07-14 18:14:57 -07:00
Ryan Houdek 96b428dcaa Merge pull request #2774 from Sonicadvance1/xcb_version
CMake: Fix pkg version extraction for xcb
2023-07-14 17:18:29 -07:00
Ryan Houdek 421214e723 Merge pull request #2775 from lioncash/vecimm
Arm64: Emitter: Handle LD1{*}/LDFF1{*} Vector + Immediate encodings
2023-07-13 22:18:00 -07:00
Lioncache 1a18bbb966 Arm64: Emitter: Handle LD1{*}/LDFF1{*} Vector + Immediate encodings
While we're in the area implementing the Scalar + Vector variants,
we may as well cross off the Vector + Immediate variants and
complete all of the load variants for the regular LD1{*} loads
2023-07-14 00:50:48 -04:00
Ryan Houdek 699c3f5762 Merge pull request #2773 from lioncash/memop
Arm64/Emitter: Simplify SVEMemOperand data union
2023-07-13 18:24:44 -07:00
Ryan Houdek da0a1710c0 CMake: Fix pkg version extraction
Our regex would only ever capture a single digit, so versions that had
more than one digit per section would lose additional digits.

Fixes and moves the helper to a cmake file to be shared between
GuestLibs and HostLibs.

Uses the fix in xcb because Fedora ships an older version that doesn't
have some of FEX's newer symbols.
2023-07-13 16:01:14 -07:00
Lioncache c1205eb809 Arm64/Emitter: Mark SVEMemOperand Type enum as enum class
Now that we have helpers to make querying a little less verbose, we can mark
the enum as an enum class to get rid of implicit conversions.
2023-07-13 15:28:17 -04:00
Lioncache 31b7cd77e9 Arm64/Emitter: Simplify SVEMemOperand data union
We can just move the header out of the union, since it's present in all cases.
2023-07-13 15:28:14 -04:00
Ryan Houdek 9def04c705 Merge pull request #2747 from Sonicadvance1/non-multiarch-thunks
Thunks: Fixes thunks in non-multiarch
2023-07-13 12:22:30 -07:00
Ryan Houdek 68a2441e65 Merge pull request #2772 from lioncash/insrem
OpcodeDispatcher: Narrow use of LoadXMMRegister in StoreResult_WithOpSize
2023-07-13 12:07:05 -07:00
Ryan Houdek 4ac0dec568 Thunks: Fixes thunks in non-multiarch
Removes the @PREFIX_ARCH@ replacement  string in the thunks path.

The library prefix paths now get generated upfront and everything gets
replaced to handle the differences between multiarch distros.

Fixes Thunks on Arch and Fedora.
2023-07-13 11:59:13 -07:00
Ryan Houdek 1d7b4bb522 Merge pull request #2768 from alyssarosenzweig/fix/pf
OpcodeDispatcher: Fix and optimize PF calculation
2023-07-13 11:54:30 -07:00
Ryan Houdek 22f95e627d Merge pull request #2769 from random415/main
fix spelling errors
2023-07-13 11:48:39 -07:00
Ryan Houdek 599b64e975 Merge pull request #2771 from alyssarosenzweig/print/de-ssa
IR: Print SSA values as %123 instead of %ssa123
2023-07-13 11:48:28 -07:00
Ryan Houdek 5c27febbc2 Merge pull request #2770 from lioncash/gather
Arm64/Emitter: Handle LD1{*}/LDFF1{*} scalar + vector load variants
2023-07-13 11:35:22 -07:00
Lioncache 58c93568f6 OpcodeDispatcher: Narrow use of LoadXMMRegister in StoreResult_WithOpSize
This only needs to be loaded when a partial insert needs to be performed,
so we can narrow it's scope instead of always loading it in the AVX case.
2023-07-13 14:23:34 -04:00
Alyssa Rosenzweig 491e4e2c23 IR: Print SSA values as %123 instead of %ssa123
This is less noisy with no loss of clarity, and follows the notation
used by both LLVM IR and NIR. (So, it should be familiar.)

Change done with:

    sed -i -e 's/%ssa/%/g' $(git grep -l '%ssa')

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-13 11:52:36 -04:00
Lioncache 1ed2e24fba Arm64/Emitter: Handle LDFF1{*} scalar plus vector variants
These can use the same handling code that we introduced for the normal
LD1{*} gathers, so we may as well expose support for them.
2023-07-13 11:25:44 -04:00
Lioncache 1fbb8bd78f Arm64/Emitter: Handle LD1{*} Scalar + Vector variants 2023-07-13 11:25:41 -04:00
Elias James Howell b953433404 fix spelling errors
Fixing some minor spelling errors which should not affect functionality but improve the overall quality of documentation.
2023-07-13 11:23:59 -04:00
Alyssa Rosenzweig eae950be16 OpcodeDispatcher: Use vector ops for PF calculation
On current targets, popcount is a vector op. By using VPopcount
ourselves when calculating, we can reduce some pointless masking.
Before:

    and x22, x4, #0xff
    fmov d0, x22
    cnt v0.8b, v0.8b
    addv b0, v0.8b
    umov w22, v0.b[0]
    eor x22, x22, #0x1
    strb w22, [x28, #706]

After:

    eor x22, x4, #0x1
    fmov s4, w22
    cnt v4.16b, v4.16b
    umov w22, v4.b[0]
    strb w22, [x28, #706]

llvm-mca before:

    Iterations:        100
    Instructions:      700
    Total Cycles:      2002
    Total uOps:        700

    Dispatch Width:    2
    uOps Per Cycle:    0.35
    IPC:               0.35
    Block RThroughput: 3.5

llvm-mca after:

    Iterations:        100
    Instructions:      500
    Total Cycles:      1402
    Total uOps:        500

    Dispatch Width:    2
    uOps Per Cycle:    0.36
    IPC:               0.36
    Block RThroughput: 2.5

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-13 11:08:47 -04:00
Alyssa Rosenzweig 7e6bb04db1 OpcodeDispatcher: Extract CalculatePF
This does duplicate the _Constant(1) but it doesn't matter because it
gets inlined into the eor anyway. There is no functional change here.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-13 10:05:18 -04:00
Alyssa Rosenzweig 68555546bc unittests: Add more PF coverage
FEX bugs folder of shame. This test fails on main, but passes with the
bug fix.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-13 08:39:10 -04:00
Alyssa Rosenzweig 716cac35a8 OpcodeDispatcher: Fix PF calculation
We store garbage in the upper bits. That's ok, but it means we need to
mask on read for correct behaviour.

Closes #2767

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-13 08:38:46 -04:00
Ryan Houdek 9722c4c5a4 Merge pull request #2766 from alyssarosenzweig/flags/add-of
OpcodeDispatcher: Optimize ADD/ADC OF flag packing
2023-07-12 15:47:21 -07:00
Alyssa Rosenzweig e8c0e19afc OpcodeDispatcher: "Calculcate" -> "Calculate"
Typofix.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-12 18:07:04 -04:00
Alyssa Rosenzweig c559fec959 OpcodeDispatcher: Optimize ADD/ADC OF flag packing
We can fold the Not into the And. This requires flipping the arguments
to Andn, but we do not flip the order of the assignments since that
requires an extra register in a test I'm looking at.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-12 18:06:36 -04:00
Alyssa Rosenzweig 8d2fabe705 OpcodeDispatcher: Deduplicate ADD/ADC OF generation
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
2023-07-12 18:06:36 -04:00
Mai 6455c4817a Merge pull request #2764 from Sonicadvance1/remove_unused_tls
FEXCore: Removes unused TLS variable
2023-07-12 17:25:08 -04:00
Ryan Houdek 5dbd1b8dc2 FEXCore: Removes unused TLS variable
Not sure why this still existed.
2023-07-12 13:05:47 -07:00
Mai 7765bbc7b8 Merge pull request #2762 from Sonicadvance1/FEXRootFSFetcherPercent
FEXRootFSFetcher: Make verification percent easier to read
2023-07-12 15:34:07 -04:00
Mai 2283c73fae Merge pull request #2742 from Sonicadvance1/fix_win32
FEXCore: Fixes WIN32 compiling again
2023-07-12 15:33:46 -04:00
Ryan Houdek 5fef0c29aa FEXCore: Rename Telemetry helper function GetObject
WIN32 has a define already called `GetObject` and will cause our
symbol to have an A appended to it and break linking.

Just rename it to `GetTelemetryValue`
2023-07-12 11:53:13 -07:00
Ryan Houdek d387c46aab FEXCore: Fixes WIN32 compiling again
Mostly a quick bandage while I'm setting getting ready to setup the
runners to test this for us.
2023-07-12 11:53:13 -07:00
Ryan Houdek 3bb7f9d6b5 FEXRootFSFetcher: Make verification percent easier to read
Multiply it by 100 to actually show as a percentage, only show two
digits past the decimal, and update every second to be more responsive.
2023-07-11 20:03:06 -07:00
Ryan Houdek 70d54122b2 Merge pull request #2761 from bylaws/main
FHU: Fix WIN32 getcpu implementation
2023-07-11 17:37:06 -07:00
Billy Laws 4e266cf9fd FHU: Fix WIN32 getcpu implementation
Both parameters to getcpu are nullable and FEX relies upon this for
CPUID.
2023-07-12 00:02:49 +01:00
Mai ddd6dbfdcc Merge pull request #2759 from Sonicadvance1/redundant_bfe_flags
OpcodeDispatcher: Remove spurious bfe with flag storing
2023-07-10 22:19:21 -04:00
Mai 7f2557e322 Merge pull request #2757 from Sonicadvance1/optimize_movss_reg
OpcodeDispatcher: Optimize MOVSS to register
2023-07-10 21:22:47 -04:00
Mai 810c7d926c Merge pull request #2758 from Sonicadvance1/optimize_tso_vector_loadstores
IR: Optimize vector TSO loadstore address calculation
2023-07-10 21:21:46 -04:00
Ryan Houdek 04c325661c OpcodeDispatcher: Remove spurious bfe with flag storing
Noticed during introspection that we were generating zero constants
redundantly. Bunch of single cycle hits or zero-register renames.

Every time a `SetRFLAG` helper was called, it was /always/ doing a BFE
on everything passed in to extract the lowest bit. In nearly all cases
the data getting passed in is already only the lowest bit.

Instead, stop the helper from doing this BFE, and ensure the
OpcodeDispatcher does BFE in the couple of cases it still needs to do.

As I was skimming through all these to ensure BFE isn't necessary, I did
notice that some of the BCD instructions are wrong or questionable. So I
left a comment on those so we can come back to it.
2023-07-10 18:03:23 -07:00
Mai 0121e858f2 Merge pull request #2756 from Sonicadvance1/movss_optimize
OpcodeDispatcher: Optimize MOVSS to memory destination
2023-07-10 18:48:13 -04:00
Mai 2c3361be9e Merge pull request #2755 from Sonicadvance1/stop_installing_fmt
CMake: Stop installing fmt
2023-07-10 18:47:30 -04:00
Ryan Houdek 2d800b2627 IR: Optimize vector TSO loadstore address calculation
These address calculations were failing to understand that they can be
optimized. When TSO emulation is disabled these were fine, but with TSO
we were eating one more instruction.

Before:
```
add x20, x12, #0x4 (4)
dmb ish
ldr s16, [x20]
dmb ish
```

After:
```
dmb ish
ldr s16, [x12, #4]
dmb ish
```

Also left a note that once LRCPC3 is supported in hardware that we can do a similar optimization there.
2023-07-10 15:21:46 -07:00
Ryan Houdek 55ed3e0549 OpcodeDispatcher: Optimize MOVSS to register
Easily fixed. Found through inspection.

Before:
```
eor v0.16b, v0.16b, v0.16b
mov v0.s[0], v17.s[0]
mov v4.16b, v0.16b
mov v16.s[0], v4.s[0]
```

After:
```
mov v16.s[0], v17.s[0]
```
2023-07-10 14:36:27 -07:00
Ryan Houdek 55d084ebb0 OpcodeDispatcher: Optimize MOVSS to memory destination
Easy fixed. Found through inspection.

Before:
```
eor v0.16b, v0.16b, v0.16b
mov v0.s[0], v16.s[0]
mov v4.16b, v0.16b
str s4, [x11]
```

After:
```
str s16, [x11]
```
2023-07-10 14:25:01 -07:00
Ryan Houdek 457dc5dd90 CMake: Stop installing fmt
Fixes #2751

Luckily fmt provides an option to disable this.
2023-07-10 12:26:39 -07:00
Mai 98eda5e163 Merge pull request #2749 from Sonicadvance1/optimize_away_redundant_masks
OpcodeDispatcher: Optimize some shifts size masking
2023-07-10 08:08:57 -04:00
Ryan Houdek 592935790e Merge pull request #2750 from Sonicadvance1/fix_pcmpestri
OpcodeDispatcher: Fixes bug with pcmpestri
2023-07-08 18:47:38 -07:00
Ryan Houdek 92d0344d6a OpcodeDispatcher: Fixes bug with pcmpestri
When this instruction returns the index in to the ecx register, this is
defined as a 32-bit result. This means it actually gets zero-extended to
the full 64-bit GPR size on 64-bit processes.
Previously FEX was doing a 32-bit insert which leaves garbage data in
the upper 32-bits of the RCX register.

Adds a unit test to ensure the result is zero extended.
Fixes running Java games under FEX now that SSE4.2 is exposed.
2023-07-08 18:08:47 -07:00
Ryan Houdek 9327435f97 OpcodeDispatcher: Optimize some shifts size masking
Inspired from #2561, these shifts  don't need to be masked if we know
their operating size up front.

Causes a handful of these to become more optimal.
2023-07-08 16:41:15 -07:00
Mai 573f339647 Merge pull request #2748 from Sonicadvance1/fix_missing_header
unittests: Adds missing header
2023-07-08 18:11:33 -04:00
Ryan Houdek c1f18951ab unittests: Adds missing header
Newer libstdc++ moved an internal header include and now this failed to
compile.
2023-07-08 14:50:10 -07:00
Mai 8a4bfba47c Merge pull request #2745 from Sonicadvance1/optimize_fcmov
OpcodeDispatcher: Optimize GetPackedRFLAG
2023-07-07 22:29:52 -04:00
Mai 69ea03f0eb Merge pull request #2746 from Sonicadvance1/optimize_maskmov
OpcodeDispatcher: Optimize MASKMOVDQU and MASKMOVQ
2023-07-07 22:29:37 -04:00
Mai 462feec2a6 Merge pull request #2743 from Sonicadvance1/minor_cleanup
FEXCore: Minor cleanup
2023-07-07 22:25:53 -04:00
Ryan Houdek 15f5fe658b OpcodeDispatcher: Optimize MASKMOVDQU and MASKMOVQ
This previous implementation was particularly gnarly. Because these
instructions are both weackly ordered and have implementation dependent
exception and trap behaviour these can actually be fairly conveniently
converted over to a load + cmlt + bsl + str instruction.

For the XMM variant this reduces code blowup from 80x to 15x!
For the MMX variant this reduces code blowup from 46x to 17x!

Both of these improvements are significant wins! There's still some
minor improvement that could be done with bsl that requires some
redundant moves, but since we don't have constraint support for this we
still eat two additional instructions

Before:
```asm
0x0000ffff7b800718  10ffffe0    adr x0, #-0x4 (addr 0xffff7b800714)
0x0000ffff7b80071c  f9005f80    str x0, [x28, #184]
0x0000ffff7b800720  4eb11e24    mov v4.16b, v17.16b
0x0000ffff7b800724  4eb01e05    mov v5.16b, v16.16b
0x0000ffff7b800728  aa0b03f4    mov x20, x11
0x0000ffff7b80072c  4e083c95    mov x21, v4.d[0]
0x0000ffff7b800730  4e083cb6    mov x22, v5.d[0]
0x0000ffff7b800734  d3471eb7    ubfx x23, x21, #7, #1
0x0000ffff7b800738  b4000077    cbz x23, #+0xc (addr 0xffff7b800744)
0x0000ffff7b80073c  d3401ed7    uxtb x23, w22
0x0000ffff7b800740  39000297    strb w23, [x20]
0x0000ffff7b800744  d34f3eb7    ubfx x23, x21, #15, #1
0x0000ffff7b800748  b4000077    cbz x23, #+0xc (addr 0xffff7b800754)
0x0000ffff7b80074c  d3483ed7    ubfx x23, x22, #8, #8
0x0000ffff7b800750  39000697    strb w23, [x20, #1]
0x0000ffff7b800754  d3575eb7    ubfx x23, x21, #23, #1
0x0000ffff7b800758  b4000077    cbz x23, #+0xc (addr 0xffff7b800764)
0x0000ffff7b80075c  d3505ed7    ubfx x23, x22, #16, #8
0x0000ffff7b800760  39000a97    strb w23, [x20, #2]
0x0000ffff7b800764  d35f7eb7    ubfx x23, x21, #31, #1
0x0000ffff7b800768  b4000077    cbz x23, #+0xc (addr 0xffff7b800774)
0x0000ffff7b80076c  d3587ed7    ubfx x23, x22, #24, #8
0x0000ffff7b800770  39000e97    strb w23, [x20, #3]
0x0000ffff7b800774  d3679eb7    ubfx x23, x21, #39, #1
0x0000ffff7b800778  b4000077    cbz x23, #+0xc (addr 0xffff7b800784)
0x0000ffff7b80077c  d3609ed7    ubfx x23, x22, #32, #8
0x0000ffff7b800780  39001297    strb w23, [x20, #4]
0x0000ffff7b800784  d36fbeb7    ubfx x23, x21, #47, #1
0x0000ffff7b800788  b4000077    cbz x23, #+0xc (addr 0xffff7b800794)
0x0000ffff7b80078c  d368bed7    ubfx x23, x22, #40, #8
0x0000ffff7b800790  39001697    strb w23, [x20, #5]
0x0000ffff7b800794  d377deb7    ubfx x23, x21, #55, #1
0x0000ffff7b800798  b4000077    cbz x23, #+0xc (addr 0xffff7b8007a4)
0x0000ffff7b80079c  d370ded7    ubfx x23, x22, #48, #8
0x0000ffff7b8007a0  39001a97    strb w23, [x20, #6]
0x0000ffff7b8007a4  d37ffeb5    lsr x21, x21, #63
0x0000ffff7b8007a8  b4000075    cbz x21, #+0xc (addr 0xffff7b8007b4)
0x0000ffff7b8007ac  d378fed5    lsr x21, x22, #56
0x0000ffff7b8007b0  39001e95    strb w21, [x20, #7]
0x0000ffff7b8007b4  4e183c95    mov x21, v4.d[1]
0x0000ffff7b8007b8  4e183cb6    mov x22, v5.d[1]
0x0000ffff7b8007bc  d3471eb7    ubfx x23, x21, #7, #1
0x0000ffff7b8007c0  b4000077    cbz x23, #+0xc (addr 0xffff7b8007cc)
0x0000ffff7b8007c4  d3401ed7    uxtb x23, w22
0x0000ffff7b8007c8  39002297    strb w23, [x20, #8]
0x0000ffff7b8007cc  d34f3eb7    ubfx x23, x21, #15, #1
0x0000ffff7b8007d0  b4000077    cbz x23, #+0xc (addr 0xffff7b8007dc)
0x0000ffff7b8007d4  d3483ed7    ubfx x23, x22, #8, #8
0x0000ffff7b8007d8  39002697    strb w23, [x20, #9]
0x0000ffff7b8007dc  d3575eb7    ubfx x23, x21, #23, #1
0x0000ffff7b8007e0  b4000077    cbz x23, #+0xc (addr 0xffff7b8007ec)
0x0000ffff7b8007e4  d3505ed7    ubfx x23, x22, #16, #8
0x0000ffff7b8007e8  39002a97    strb w23, [x20, #10]
0x0000ffff7b8007ec  d35f7eb7    ubfx x23, x21, #31, #1
0x0000ffff7b8007f0  b4000077    cbz x23, #+0xc (addr 0xffff7b8007fc)
0x0000ffff7b8007f4  d3587ed7    ubfx x23, x22, #24, #8
0x0000ffff7b8007f8  39002e97    strb w23, [x20, #11]
0x0000ffff7b8007fc  d3679eb7    ubfx x23, x21, #39, #1
0x0000ffff7b800800  b4000077    cbz x23, #+0xc (addr 0xffff7b80080c)
0x0000ffff7b800804  d3609ed7    ubfx x23, x22, #32, #8
0x0000ffff7b800808  39003297    strb w23, [x20, #12]
0x0000ffff7b80080c  d36fbeb7    ubfx x23, x21, #47, #1
0x0000ffff7b800810  b4000077    cbz x23, #+0xc (addr 0xffff7b80081c)
0x0000ffff7b800814  d368bed7    ubfx x23, x22, #40, #8
0x0000ffff7b800818  39003697    strb w23, [x20, #13]
0x0000ffff7b80081c  d377deb7    ubfx x23, x21, #55, #1
0x0000ffff7b800820  b4000077    cbz x23, #+0xc (addr 0xffff7b80082c)
0x0000ffff7b800824  d370ded7    ubfx x23, x22, #48, #8
0x0000ffff7b800828  39003a97    strb w23, [x20, #14]
0x0000ffff7b80082c  d37ffeb5    lsr x21, x21, #63
0x0000ffff7b800830  b4000075    cbz x21, #+0xc (addr 0xffff7b80083c)
0x0000ffff7b800834  d378fed5    lsr x21, x22, #56
0x0000ffff7b800838  39003e95    strb w21, [x20, #15]
0x0000ffff7b80083c  58000040    ldr x0, pc+8 (addr 0xffff7b800844)
0x0000ffff7b800840  d63f0000    blr x0
```

After:
```asm
0x0000ffff7ac00718  10ffffe0            adr x0, #-0x4 (addr 0xffff7ac00714)
0x0000ffff7ac0071c  f9005f80            str x0, [x28, #184]
0x0000ffff7ac00720  4e20aa24            cmlt v4.16b, v17.16b, #0
0x0000ffff7ac00724  3dc00165            ldr q5, [x11]
0x0000ffff7ac00728  4ea41c80            mov v0.16b, v4.16b
0x0000ffff7ac0072c  6e651e00            bsl v0.16b, v16.16b, v5.16b
0x0000ffff7ac00730  4ea01c04            mov v4.16b, v0.16b
0x0000ffff7ac00734  3d800164            str q4, [x11]
0x0000ffff7ac00738  58000040            ldr x0, pc+8 (addr 0xffff7ac00740)
0x0000ffff7ac0073c  d63f0000            blr x0
```
2023-07-07 18:37:17 -07:00
Ryan Houdek 052aa4317b OpcodeDispatcher: Optimize GetPackedRFLAG
Only return the particular flags that are being requested in the moment
since compacting them all when requested is fairly slow.

x87 fcmov in particular was requesting all the flags when it only needs
a couple.
This reduces a `fcmovb` instruction count blowup from 103x to 38x. Still
more room to go but this one stood out as being particularly bad.

Old:
```asm
0x0000000265a002bc  10ffffe0    adr x0, #-0x4 (addr 0x265a002b8)
0x0000000265a002c0  f9005f80    str x0, [x28, #184]
0x0000000265a002c4  d2800014    mov x20, #0x0
0x0000000265a002c8  d2800035    mov x21, #0x1
0x0000000265a002cc  d2800056    mov x22, #0x2
0x0000000265a002d0  394b0397    ldrb w23, [x28, #704]
0x0000000265a002d4  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a002d8  aa1702d6    orr x22, x22, x23
0x0000000265a002dc  394b0b97    ldrb w23, [x28, #706]
0x0000000265a002e0  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a002e4  531e76f7    lsl w23, w23, #2
0x0000000265a002e8  aa1702d6    orr x22, x22, x23
0x0000000265a002ec  394b1397    ldrb w23, [x28, #708]
0x0000000265a002f0  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a002f4  531c6ef7    lsl w23, w23, #4
0x0000000265a002f8  aa1702d6    orr x22, x22, x23
0x0000000265a002fc  394b1b97    ldrb w23, [x28, #710]
0x0000000265a00300  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00304  531a66f7    lsl w23, w23, #6
0x0000000265a00308  aa1702d6    orr x22, x22, x23
0x0000000265a0030c  394b1f97    ldrb w23, [x28, #711]
0x0000000265a00310  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00314  531962f7    lsl w23, w23, #7
0x0000000265a00318  aa1702d6    orr x22, x22, x23
0x0000000265a0031c  394b2397    ldrb w23, [x28, #712]
0x0000000265a00320  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00324  53185ef7    lsl w23, w23, #8
0x0000000265a00328  aa1702d6    orr x22, x22, x23
0x0000000265a0032c  394b2797    ldrb w23, [x28, #713]
0x0000000265a00330  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00334  53175af7    lsl w23, w23, #9
0x0000000265a00338  aa1702d6    orr x22, x22, x23
0x0000000265a0033c  394b2b97    ldrb w23, [x28, #714]
0x0000000265a00340  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00344  531656f7    lsl w23, w23, #10
0x0000000265a00348  aa1702d6    orr x22, x22, x23
0x0000000265a0034c  394b2f97    ldrb w23, [x28, #715]
0x0000000265a00350  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00354  531552f7    lsl w23, w23, #11
0x0000000265a00358  aa1702d6    orr x22, x22, x23
0x0000000265a0035c  394b3397    ldrb w23, [x28, #716]
0x0000000265a00360  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00364  53144ef7    lsl w23, w23, #12
0x0000000265a00368  aa1702d6    orr x22, x22, x23
0x0000000265a0036c  394b3b97    ldrb w23, [x28, #718]
0x0000000265a00370  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00374  531246f7    lsl w23, w23, #14
0x0000000265a00378  aa1702d6    orr x22, x22, x23
0x0000000265a0037c  394b4397    ldrb w23, [x28, #720]
0x0000000265a00380  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00384  53103ef7    lsl w23, w23, #16
0x0000000265a00388  aa1702d6    orr x22, x22, x23
0x0000000265a0038c  394b4797    ldrb w23, [x28, #721]
0x0000000265a00390  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a00394  530f3af7    lsl w23, w23, #17
0x0000000265a00398  aa1702d6    orr x22, x22, x23
0x0000000265a0039c  394b4b97    ldrb w23, [x28, #722]
0x0000000265a003a0  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a003a4  530e36f7    lsl w23, w23, #18
0x0000000265a003a8  aa1702d6    orr x22, x22, x23
0x0000000265a003ac  394b4f97    ldrb w23, [x28, #723]
0x0000000265a003b0  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a003b4  530d32f7    lsl w23, w23, #19
0x0000000265a003b8  aa1702d6    orr x22, x22, x23
0x0000000265a003bc  394b5397    ldrb w23, [x28, #724]
0x0000000265a003c0  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a003c4  530c2ef7    lsl w23, w23, #20
0x0000000265a003c8  aa1702d6    orr x22, x22, x23
0x0000000265a003cc  394b5797    ldrb w23, [x28, #725]
0x0000000265a003d0  d3407ef7    ubfx x23, x23, #0, #32
0x0000000265a003d4  530b2af7    lsl w23, w23, #21
0x0000000265a003d8  aa1702d6    orr x22, x22, x23
0x0000000265a003dc  924002d6    and x22, x22, #0x1
0x0000000265a003e0  93400294    sbfx x20, x20, #0, #1
0x0000000265a003e4  934002b5    sbfx x21, x21, #0, #1
0x0000000265a003e8  f10002df    cmp x22, #0x0 (0)
0x0000000265a003ec  9a950294    csel x20, x20, x21, eq
0x0000000265a003f0  4e080e84    dup v4.2d, x20
0x0000000265a003f4  394baf94    ldrb w20, [x28, #747]
0x0000000265a003f8  91000695    add x21, x20, #0x1 (1)
0x0000000265a003fc  92400ab5    and x21, x21, #0x7
0x0000000265a00400  d2800200    mov x0, #0x10
0x0000000265a00404  9b007e80    mul x0, x20, x0
0x0000000265a00408  8b000380    add x0, x28, x0
0x0000000265a0040c  3dc0bc05    ldr q5, [x0, #752]
0x0000000265a00410  d2800200    mov x0, #0x10
0x0000000265a00414  9b007ea0    mul x0, x21, x0
0x0000000265a00418  8b000380    add x0, x28, x0
0x0000000265a0041c  3dc0bc06    ldr q6, [x0, #752]
0x0000000265a00420  4ea41c80    mov v0.16b, v4.16b
0x0000000265a00424  6e651cc0    bsl v0.16b, v6.16b, v5.16b
0x0000000265a00428  4ea01c04    mov v4.16b, v0.16b
0x0000000265a0042c  d2800200    mov x0, #0x10
0x0000000265a00430  9b007e80    mul x0, x20, x0
0x0000000265a00434  8b000380    add x0, x28, x0
0x0000000265a00438  3d80bc04    str q4, [x0, #752]
0x0000000265a0043c  58000040    ldr x0, pc+8 (addr 0x265a00444)
0x0000000265a00440  d63f0000    blr x0
```

New:
```asm
0x0000000265a002bc  10ffffe0    adr x0, #-0x4 (addr 0x265a002b8)
0x0000000265a002c0  f9005f80    str x0, [x28, #184]
0x0000000265a002c4  d2800014    mov x20, #0x0
0x0000000265a002c8  d2800035    mov x21, #0x1
0x0000000265a002cc  d2800056    mov x22, #0x2
0x0000000265a002d0  394b0397    ldrb w23, [x28, #704]
0x0000000265a002d4  330002f6    bfxil w22, w23, #0, #1
0x0000000265a002d8  924002d6    and x22, x22, #0x1
0x0000000265a002dc  93400294    sbfx x20, x20, #0, #1
0x0000000265a002e0  934002b5    sbfx x21, x21, #0, #1
0x0000000265a002e4  f10002df    cmp x22, #0x0 (0)
0x0000000265a002e8  9a950294    csel x20, x20, x21, eq
0x0000000265a002ec  4e080e84    dup v4.2d, x20
0x0000000265a002f0  394baf94    ldrb w20, [x28, #747]
0x0000000265a002f4  91000695    add x21, x20, #0x1 (1)
0x0000000265a002f8  92400ab5    and x21, x21, #0x7
0x0000000265a002fc  d2800200    mov x0, #0x10
0x0000000265a00300  9b007e80    mul x0, x20, x0
0x0000000265a00304  8b000380    add x0, x28, x0
0x0000000265a00308  3dc0bc05    ldr q5, [x0, #752]
0x0000000265a0030c  d2800200    mov x0, #0x10
0x0000000265a00310  9b007ea0    mul x0, x21, x0
0x0000000265a00314  8b000380    add x0, x28, x0
0x0000000265a00318  3dc0bc06    ldr q6, [x0, #752]
0x0000000265a0031c  4ea41c80    mov v0.16b, v4.16b
0x0000000265a00320  6e651cc0    bsl v0.16b, v6.16b, v5.16b
0x0000000265a00324  4ea01c04    mov v4.16b, v0.16b
0x0000000265a00328  d2800200    mov x0, #0x10
0x0000000265a0032c  9b007e80    mul x0, x20, x0
0x0000000265a00330  8b000380    add x0, x28, x0
0x0000000265a00334  3d80bc04    str q4, [x0, #752]
0x0000000265a00338  58000040    ldr x0, pc+8 (addr 0x265a00340)
0x0000000265a0033c  d63f0000    blr x0
```
2023-07-07 17:01:59 -07:00
Ryan Houdek debcb0e047 Arm64: Optimize BFI in the case that Dst == srcDst
ARM64 BFI doesn't allow you to encode two source registers here to match
our SSA semantics. Also since we don't support RA constraints to ensure
that these match, just do the optimal case in the backend.

Leave a comment for future RA contraint excavators to make this more
optimal
2023-07-07 16:43:41 -07:00
Ryan Houdek baf04b6a41 FEXCore: Minor cleanup
This isn't required anymore since we are exposing the virtual class
directly.
2023-07-07 15:06:14 -07:00
834 changed files with 172350 additions and 35040 deletions

No files matched your search

+62 -50
View File
@@ -17,11 +17,11 @@ env:
FEX_ENABLEAVX: 1
jobs:
build:
build_plus_test:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
arch: [[self-hosted, x64], [self-hosted, ARMv8.0], [self-hosted, ARMv8.2], [self-hosted, ARMv8.4]]
arch: [[self-hosted, ARMv8.0], [self-hosted, ARMv8.2], [self-hosted, ARMv8.4]]
fail-fast: false
steps:
@@ -65,7 +65,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
- name: Build
working-directory: ${{runner.workspace}}/build
@@ -78,18 +78,6 @@ jobs:
shell: bash
run: cmake --build . --config $BUILD_TYPE --target install
- name: ASM Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: IR Tests
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -102,30 +90,6 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_IR.log || true
- name: Posix Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the posixtest
run: cmake --build . --config $BUILD_TYPE --target posix_tests
- name: Posix Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_Posix.log || true
- name: gvisor tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the gvisor tests
run: cmake --build . --config $BUILD_TYPE --target gvisor_tests
- name: GVisor Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GVisor.log || true
- name: gcc target tests 64
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -150,17 +114,6 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GCC32.log || true
- name: Struct verifier tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target struct_verifier
- name: Struct verifier Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_StructVerifier.log || true
- name: APITest tests
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -172,6 +125,17 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_APITests.log || true
- name: FEXCore APITest tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target fexcore_apitests
- name: FEXCore APITest Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXCoreAPITests.log || true
- name: ARMEmitter tests
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -233,6 +197,53 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkResults.log || true
- name: ASM Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: Posix Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the posixtest
run: cmake --build . --config $BUILD_TYPE --target posix_tests
- name: Posix Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_Posix.log || true
- name: gvisor tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the gvisor tests
run: cmake --build . --config $BUILD_TYPE --target gvisor_tests
- name: GVisor Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GVisor.log || true
- name: Struct verifier tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target struct_verifier
- name: Struct verifier Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_StructVerifier.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
@@ -254,6 +265,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v3'
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
+39 -28
View File
@@ -24,12 +24,11 @@ env:
FEX_ENABLEAVX: 1
jobs:
build:
glibc_fault_test:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
# Run on an x86 device and any ARM runner.
arch: [[self-hosted, x64], [self-hosted, ARM64]]
arch: [[self-hosted, ARM64]]
fail-fast: false
steps:
@@ -73,7 +72,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DENABLE_GLIBC_ALLOCATOR_HOOK_FAULT=True -DENABLE_JEMALLOC_GLIBC_ALLOC=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_FEX_LINUX_TESTS=True -DENABLE_GLIBC_ALLOCATOR_HOOK_FAULT=True -DENABLE_JEMALLOC_GLIBC_ALLOC=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
- name: Build
working-directory: ${{runner.workspace}}/build
@@ -86,18 +85,6 @@ jobs:
shell: bash
run: cmake --build . --config $BUILD_TYPE --target install
- name: ASM Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: IR Tests
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -110,18 +97,6 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_IR.log || true
- name: Posix Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the posixtest
run: cmake --build . --config $BUILD_TYPE --target posix_tests
- name: Posix Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_Posix.log || true
- name: gcc target tests 64
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -157,6 +132,17 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_APITests.log || true
- name: FEXCore APITest tests
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --target fexcore_apitests
- name: FEXCore APITest Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXCoreAPITests.log || true
- name: FEXLinuxTests
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -168,6 +154,30 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXLinuxTests.log || true
- name: ASM Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: Posix Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the posixtest
run: cmake --build . --config $BUILD_TYPE --target posix_tests
- name: Posix Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_Posix.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
@@ -189,6 +199,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v3'
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
+107
View File
@@ -0,0 +1,107 @@
name: Hostrunner tests
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
CC: clang
CXX: clang++
FEX_ENABLEAVX: 1
jobs:
hostrunner_tests:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
arch: [[self-hosted, x64]]
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Set runner label
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
- name: Set rootfs paths
run: |
echo "FEX_ROOTFS_MOUNT=/mnt/AutoNFS/rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS_PATH=$HOME/Rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
echo "ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
- name: Update RootFS cache
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
- name : submodule checkout
# Need to update submodules
run: |
git submodule sync --recursive
git submodule update --init --depth 1
- name: Clean Build Environment
run: rm -Rf ${{runner.workspace}}/build
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure CMake
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
- name: Build
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE
- name: ASM Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: Truncate test results
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
# Cap out the log files at 20M in case something crash spins and dumps fault text
# ASM tests get quite close to 10MB
run: truncate --size=<20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
- name: Set runner name
if: ${{ always() }}
run: echo "runner_name=$(hostname)" >> $GITHUB_ENV
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v3'
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
retention-days: 3
+147
View File
@@ -0,0 +1,147 @@
name: Instruction Count CI run
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
CC: clang
CXX: clang++
FEX_ENABLEAVX: 1
jobs:
instcountci_tests:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
arch: [[self-hosted, x64], [self-hosted, ARM64]]
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Set runner label
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
- name: Set rootfs paths
run: |
echo "FEX_ROOTFS_MOUNT=/mnt/AutoNFS/rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS_PATH=$HOME/Rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
echo "ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
- name: Update RootFS cache
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
- name : submodule checkout
# Need to update submodules
run: |
git submodule sync --recursive
git submodule update --init --depth 1
- name: Clean Build Environment
run: rm -Rf ${{runner.workspace}}/build
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Set vixl_sim x86
if: matrix.arch[1] == 'x64'
run: |
echo "VIXL_SIM_ENABLED=True" >> $GITHUB_ENV
- name: Set vixl_sim Arm64
if: matrix.arch[1] == 'ARM64'
run: |
echo "VIXL_SIM_ENABLED=False" >> $GITHUB_ENV
- name: Configure CMake
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=$VIXL_SIM_ENABLED -DENABLE_VIXL_DISASSEMBLER=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
- name: Build
working-directory: ${{runner.workspace}}/build
shell: bash
env:
FEX_DISABLETELEMETRY: 1
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE --target CodeSizeValidation instcountci_test_files
- name: Instruction Count Tests
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target instcountci_tests
- name: Instruction Count Test Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_InstCountCI.log || true
- name: Update local repo instcount
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: cmake --build . --config $BUILD_TYPE --target instcountci_update_tests
- name: Get instcountCI diff
if: ${{ always() }}
shell: bash
working-directory: ${{github.workspace}}/
run: git diff --output=${{runner.workspace}}/build/InstCountCI.diff
- name: Check if InstCountCI Diff exists
if: ${{ always() }}
shell: bash
working-directory: ${{github.workspace}}/
# Check if the file is empty
run: sh -c "! test -s ${{runner.workspace}}/build/InstCountCI.diff"
- name: Truncate test results
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
# Cap out the log files at 20M in case something crash spins and dumps fault text
# ASM tests get quite close to 10MB
run: truncate --size=<20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
- name: Set runner name
if: ${{ always() }}
run: echo "runner_name=$(hostname)" >> $GITHUB_ENV
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v3'
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
retention-days: 3
- name: Upload results InstCountCI
if: ${{ always() }}
uses: 'actions/upload-artifact@v3'
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}-instcountci
path: ${{runner.workspace}}/build/InstCountCI.diff
retention-days: 3
+97
View File
@@ -0,0 +1,97 @@
name: Mingw build
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
BUILD_TYPE: Debug
FEX_ENABLEAVX: 1
jobs:
mingw_build:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
arch: [[self-hosted, ARM64, mingw]]
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Set runner label
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
- name: Add MingGW to PATH
run: echo "$HOME/llvm-mingw/build/bin/" >> $GITHUB_PATH
- name: Set CC x86
if: matrix.arch[1] == 'x64'
run: |
echo "MINGW_TRIPLE=x86_64-w64-mingw32" >> $GITHUB_ENV
- name: Set CC Arm64
if: matrix.arch[1] == 'ARM64'
run: |
echo "MINGW_TRIPLE=aarch64-w64-mingw32" >> $GITHUB_ENV
- name: Set rootfs paths
run: |
echo "FEX_ROOTFS_MOUNT=/mnt/AutoNFS/rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS_PATH=$HOME/Rootfs/" >> $GITHUB_ENV
echo "FEX_ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
echo "ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
- name: Update RootFS cache
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
- name : submodule checkout
# Need to update submodules
run: |
git submodule sync --recursive
git submodule update --init --depth 1
- name: Clean Build Environment
run: rm -Rf ${{runner.workspace}}/build
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure CMake
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/toolchain_mingw.cmake -DMINGW_TRIPLE=$MINGW_TRIPLE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_TESTS=False -DENABLE_JEMALLOC=False -DENABLE_JEMALLOC_GLIBC_ALLOC=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
- name: Build
working-directory: ${{runner.workspace}}/build
shell: bash
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE
- name: Set runner name
if: ${{ always() }}
run: echo "runner_name=$(hostname)" >> $GITHUB_ENV
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v3'
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
retention-days: 3
+18 -2
View File
@@ -16,7 +16,7 @@ env:
FEX_ENABLEAVX: 1
jobs:
build:
vixl_simulator:
runs-on: ${{ matrix.arch }}
strategy:
matrix:
@@ -65,7 +65,7 @@ jobs:
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_VIXL_SIMULATOR=True -DENABLE_VIXL_DISASSEMBLER=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
- name: Build
working-directory: ${{runner.workspace}}/build
@@ -85,6 +85,21 @@ jobs:
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
- name: ASM Tests 128-bit
working-directory: ${{runner.workspace}}/build
shell: bash
env:
FEX_HOSTFEATURES: "disableavx"
FEX_FORCESVEWIDTH: "128"
# Execute the unit tests
run: cmake --build . --config $BUILD_TYPE --target asm_tests
- name: ASM Test 128-bit Results move
if: ${{ always() }}
shell: bash
working-directory: ${{runner.workspace}}/build
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM128bit.log || true
- name: IR Tests
working-directory: ${{runner.workspace}}/build
shell: bash
@@ -112,6 +127,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: 'actions/upload-artifact@v3'
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
+5
View File
@@ -0,0 +1,5 @@
{
"ThunksDB": {
"fex_thunk_test": 1
}
}
+30
View File
@@ -0,0 +1,30 @@
# Extracts a version from the passed in version string in the form of "<Major>.<Minor>.<Patch>".
# If a part of the version is missing then it gets set as zero.
# Version variables returned in:
# ${Package}_VERSION_MAJOR
# ${Package}_VERSION_MINOR
# ${Package}_VERSION_PATCH
function(version_to_variables VERSION _Package)
string(REPLACE "." ";" VERSION_LIST "${VERSION}")
list (LENGTH VERSION_LIST VERSION_LEN)
if (${VERSION_LEN} GREATER 0)
list(GET VERSION_LIST 0 VERSION_MAJOR)
set(${_Package}_VERSION_MAJOR ${VERSION_MAJOR} PARENT_SCOPE)
else()
set(${_Package}_VERSION_MAJOR 0 PARENT_SCOPE)
endif()
if (${VERSION_LEN} GREATER 1)
list(GET VERSION_LIST 1 VERSION_MINOR)
set(${_Package}_VERSION_MINOR ${VERSION_MINOR} PARENT_SCOPE)
else()
set(${_Package}_VERSION_MINOR 0 PARENT_SCOPE)
endif()
if (${VERSION_LEN} GREATER 2)
list(GET VERSION_LIST 2 VERSION_PATCH)
set(${_Package}_VERSION_PATCH ${VERSION_PATCH} PARENT_SCOPE)
else()
set(${_Package}_VERSION_PATCH 0 PARENT_SCOPE)
endif()
endfunction()
+16 -32
View File
@@ -13,8 +13,7 @@ option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
option(ENABLE_IWYU "Enables include what you use program" FALSE)
option(ENABLE_LTO "Enable LTO with compilation" TRUE)
option(ENABLE_XRAY "Enable building with LLVM X-Ray" FALSE)
option(ENABLE_LLD "Enable linking with lld" FALSE)
option(ENABLE_MOLD "Enable linking with mold" FALSE)
set(USE_LINKER "" CACHE STRING "Allow overriding the linker path directly")
option(ENABLE_ASAN "Enables Clang ASAN" FALSE)
option(ENABLE_TSAN "Enables Clang TSAN" FALSE)
option(ENABLE_ASSERTIONS "Enables assertions in build" FALSE)
@@ -26,7 +25,6 @@ option(ENABLE_JEMALLOC_GLIBC_ALLOC "Enables jemalloc glibc allocator" TRUE)
option(ENABLE_OFFLINE_TELEMETRY "Enables FEX offline telemetry" TRUE)
option(ENABLE_COMPILE_TIME_TRACE "Enables time trace compile option" FALSE)
option(ENABLE_LIBCXX "Enables LLVM libc++" FALSE)
option(ENABLE_INTERPRETER "Enables FEX's Interpreter" FALSE)
option(ENABLE_CCACHE "Enables ccache for compile caching" TRUE)
option(ENABLE_TERMUX_BUILD "Forces building for Termux on a non-Termux build machine" FALSE)
option(ENABLE_VIXL_SIMULATOR "Forces the FEX JIT to use the VIXL simulator" FALSE)
@@ -98,11 +96,6 @@ if (ENABLE_GDB_SYMBOLS)
endif()
if (ENABLE_INTERPRETER)
message(STATUS "Interpreter enabled")
add_definitions(-DINTERPRETER_ENABLED=1)
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Bin)
@@ -119,14 +112,6 @@ else()
endif()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
option(ENABLE_X86_HOST_DEBUG "Enables compiling on x86_64 host" FALSE)
if (NOT ENABLE_X86_HOST_DEBUG)
message(FATAL_ERROR
" Be warned: FEX isn't optimized for x86_64 hosts!\n"
" Support for x86_64 hosts is only for debugging and convenience!\n"
" Don't expect amazing performance or optimal code generation!\n"
" Pass -DENABLE_X86_HOST_DEBUG=True to bypass this message!")
endif()
set(_M_X86_64 1)
add_definitions(-D_M_X86_64=1)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
@@ -157,13 +142,9 @@ endif()
set (PTHREAD_LIB pthread)
if (ENABLE_LLD AND ENABLE_MOLD)
message (FATAL_ERROR "Cannot enable both lld and mold")
elseif (ENABLE_LLD)
set (LD_OVERRIDE "-fuse-ld=lld")
add_link_options(${LD_OVERRIDE})
elseif (ENABLE_MOLD)
add_link_options("-fuse-ld=mold")
if (USE_LINKER)
message(STATUS "Overriding linker to: ${USE_LINKER}")
add_link_options("-fuse-ld=${USE_LINKER}")
endif()
if (ENABLE_LIBCXX)
@@ -241,7 +222,7 @@ if (BUILD_TESTS)
endif()
add_subdirectory(External/vixl/)
include_directories(External/vixl/src/)
include_directories(SYSTEM External/vixl/src/)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# This means we were attempted to get compiled with GCC
@@ -267,6 +248,8 @@ if (BUILD_TESTS)
include(Catch)
endif()
# Disable fmt install
set(FMT_INSTALL OFF)
add_subdirectory(External/fmt/)
add_subdirectory(External/imgui/)
@@ -310,10 +293,11 @@ if(ENABLE_WERROR OR ENABLE_STRICT_WERROR)
endif()
endif()
set(FEX_TUNE_COMPILE_FLAGS)
if (NOT TUNE_ARCH STREQUAL "generic")
check_cxx_compiler_flag("-march=${TUNE_ARCH}" COMPILER_SUPPORTS_ARCH_TYPE)
if(COMPILER_SUPPORTS_ARCH_TYPE)
add_compile_options("-march=${TUNE_ARCH}")
list(APPEND FEX_TUNE_COMPILE_FLAGS "-march=${TUNE_ARCH}")
else()
message(FATAL_ERROR "Trying to compile arch type '${TUNE_ARCH}' but the compiler doesn't support this")
endif()
@@ -326,7 +310,7 @@ if (TUNE_CPU STREQUAL "native")
# Clang can not currently check for native Apple M1 type in hypervisor. Currently disabled
check_cxx_compiler_flag("-mcpu=native" COMPILER_SUPPORTS_CPU_TYPE)
if(COMPILER_SUPPORTS_CPU_TYPE)
add_compile_options("-mcpu=native")
list(APPEND FEX_TUNE_COMPILE_FLAGS "-mcpu=native")
endif()
else()
# Due to an oversight in llvm, it declares any reasonably new Kryo CPU to only be ARMv8.0
@@ -340,19 +324,19 @@ if (TUNE_CPU STREQUAL "native")
check_cxx_compiler_flag("-mcpu=${AARCH64_CPU}" COMPILER_SUPPORTS_CPU_TYPE)
if(COMPILER_SUPPORTS_CPU_TYPE)
add_compile_options("-mcpu=${AARCH64_CPU}")
list(APPEND FEX_TUNE_COMPILE_FLAGS "-mcpu=${AARCH64_CPU}")
endif()
endif()
else()
check_cxx_compiler_flag("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
add_compile_options("-march=native")
list(APPEND FEX_TUNE_COMPILE_FLAGS "-march=native")
endif()
endif()
else()
check_cxx_compiler_flag("-mcpu=${TUNE_CPU}" COMPILER_SUPPORTS_CPU_TYPE)
if(COMPILER_SUPPORTS_CPU_TYPE)
add_compile_options("-mcpu=${TUNE_CPU}")
list(APPEND FEX_TUNE_COMPILE_FLAGS "-mcpu=${TUNE_CPU}")
else()
message(FATAL_ERROR "Trying to compile cpu type '${TUNE_CPU}' but the compiler doesn't support this")
endif()
@@ -430,7 +414,7 @@ if (BUILD_TESTS)
endif()
add_subdirectory(FEXHeaderUtils/)
add_subdirectory(External/FEXCore)
add_subdirectory(FEXCore/)
# Binfmt_misc files must be installed prior to Source/ installs
add_subdirectory(Data/binfmts/)
@@ -470,10 +454,10 @@ if (BUILD_THUNKS)
CMAKE_ARGS
"-DBITNESS=64"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DBUILD_FEX_LINUX_TESTS=${BUILD_FEX_LINUX_TESTS}"
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_64_TOOLCHAIN_FILE}"
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
"-DGENERATOR_EXE=$<TARGET_FILE:thunkgen>"
INSTALL_COMMAND ""
@@ -488,10 +472,10 @@ if (BUILD_THUNKS)
CMAKE_ARGS
"-DBITNESS=32"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DBUILD_FEX_LINUX_TESTS=${BUILD_FEX_LINUX_TESTS}"
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_32_TOOLCHAIN_FILE}"
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
"-DGENERATOR_EXE=$<TARGET_FILE:thunkgen>"
INSTALL_COMMAND ""
-5
View File
@@ -1,5 +0,0 @@
{
"Config": {
"Env": "STEAM_GAME_LAUNCH_SHELL=@CMAKE_INSTALL_PREFIX@/bin/FEXBash"
}
}
+72 -63
View File
@@ -6,10 +6,10 @@
"X11"
],
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libGL.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libGL.so.1",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libGL.so.1.2.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libGL.so.1.7.0"
"@PREFIX_LIB@/libGL.so",
"@PREFIX_LIB@/libGL.so.1",
"@PREFIX_LIB@/libGL.so.1.2.0",
"@PREFIX_LIB@/libGL.so.1.7.0"
]
},
"GLESv2": {
@@ -18,17 +18,17 @@
"X11"
],
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libGLESv2.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libGLESv2.so.2",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libGLESv2.so.2.0.0"
"@PREFIX_LIB@/libGLESv2.so",
"@PREFIX_LIB@/libGLESv2.so.2",
"@PREFIX_LIB@/libGLESv2.so.2.0.0"
]
},
"X11": {
"Library": "libX11-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libX11.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libX11.so.6",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libX11.so.6.4.0"
"@PREFIX_LIB@/libX11.so",
"@PREFIX_LIB@/libX11.so.6",
"@PREFIX_LIB@/libX11.so.6.4.0"
]
},
"Vulkan": {
@@ -37,8 +37,8 @@
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libvulkan.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libvulkan.so.1",
"@PREFIX_LIB@/libvulkan.so",
"@PREFIX_LIB@/libvulkan.so.1",
"@HOME@/.local/share/Steam/ubuntu12_32/steam-runtime/pinned_libs_64/libvulkan.so.1"
],
"Comment": [
@@ -46,139 +46,148 @@
]
},
"xcb": {
"Depends": [
"X11"
],
"Library": "libxcb-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb.so.1",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb.so.1.1.0"
"@PREFIX_LIB@/libxcb.so",
"@PREFIX_LIB@/libxcb.so.1",
"@PREFIX_LIB@/libxcb.so.1.1.0"
]
},
"xcb-dri2": {
"Library": "libxcb-dri2-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-dri2.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-dri2.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-dri2.so.0.0.0"
"@PREFIX_LIB@/libxcb-dri2.so",
"@PREFIX_LIB@/libxcb-dri2.so.0",
"@PREFIX_LIB@/libxcb-dri2.so.0.0.0"
]
},
"xcb-dri3": {
"Library": "libxcb-dri3-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-dri3.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-dri3.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-dri3.so.0.0.0"
"@PREFIX_LIB@/libxcb-dri3.so",
"@PREFIX_LIB@/libxcb-dri3.so.0",
"@PREFIX_LIB@/libxcb-dri3.so.0.0.0"
]
},
"xcb-xfixes": {
"Library": "libxcb-xfixes-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-xfixes.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-xfixes.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-xfixes.so.0.0.0"
"@PREFIX_LIB@/libxcb-xfixes.so",
"@PREFIX_LIB@/libxcb-xfixes.so.0",
"@PREFIX_LIB@/libxcb-xfixes.so.0.0.0"
]
},
"xcb-shm": {
"Library": "libxcb-shm-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-shm.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-shm.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-shm.so.0.0.0"
"@PREFIX_LIB@/libxcb-shm.so",
"@PREFIX_LIB@/libxcb-shm.so.0",
"@PREFIX_LIB@/libxcb-shm.so.0.0.0"
]
},
"xcb-sync": {
"Library": "libxcb-sync-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-sync.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-sync.so.1",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-sync.so.1.0.0"
"@PREFIX_LIB@/libxcb-sync.so",
"@PREFIX_LIB@/libxcb-sync.so.1",
"@PREFIX_LIB@/libxcb-sync.so.1.0.0"
]
},
"xcb-randr": {
"Library": "libxcb-randr-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-randr.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-randr.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-randr.so.0.1.0"
"@PREFIX_LIB@/libxcb-randr.so",
"@PREFIX_LIB@/libxcb-randr.so.0",
"@PREFIX_LIB@/libxcb-randr.so.0.1.0"
]
},
"xcb-present": {
"Library": "libxcb-present-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-present.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-present.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-present.so.0.0.0"
"@PREFIX_LIB@/libxcb-present.so",
"@PREFIX_LIB@/libxcb-present.so.0",
"@PREFIX_LIB@/libxcb-present.so.0.0.0"
]
},
"xcb-glx": {
"Library": "libxcb-glx-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-glx.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-glx.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxcb-glx.so.0.0.0"
"@PREFIX_LIB@/libxcb-glx.so",
"@PREFIX_LIB@/libxcb-glx.so.0",
"@PREFIX_LIB@/libxcb-glx.so.0.0.0"
]
},
"xshmfence": {
"Library": "libxshmfence-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxshmfence.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxshmfence.so.1",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libxshmfence.so.1.0.0"
"@PREFIX_LIB@/libxshmfence.so",
"@PREFIX_LIB@/libxshmfence.so.1",
"@PREFIX_LIB@/libxshmfence.so.1.0.0"
]
},
"drm": {
"Library": "libdrm-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libdrm.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libdrm.so.2",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libdrm.so.2.4.0"
"@PREFIX_LIB@/libdrm.so",
"@PREFIX_LIB@/libdrm.so.2",
"@PREFIX_LIB@/libdrm.so.2.4.0"
]
},
"asound": {
"Library": "libasound-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libasound.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libasound.so.2",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libasound.so.2.0.0"
"@PREFIX_LIB@/libasound.so",
"@PREFIX_LIB@/libasound.so.2",
"@PREFIX_LIB@/libasound.so.2.0.0"
]
},
"fex_thunk_test": {
"Library": "libfex_thunk_test-guest.so",
"Overlay": [
"@PREFIX_LIB@/libfex_thunk_test.so"
]
},
"Xrender": {
"Library": "libXrender-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXrender.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXrender.so.1",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXrender.so.1.3.0"
"@PREFIX_LIB@/libXrender.so",
"@PREFIX_LIB@/libXrender.so.1",
"@PREFIX_LIB@/libXrender.so.1.3.0"
]
},
"Xext": {
"Library": "libXext-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXext.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXext.so.6",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXext.so.6.4.0"
"@PREFIX_LIB@/libXext.so",
"@PREFIX_LIB@/libXext.so.6",
"@PREFIX_LIB@/libXext.so.6.4.0"
]
},
"Xfixes": {
"Library": "libXfixes-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXfixes.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXfixes.so.3",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libXfixes.so.3.1.0"
"@PREFIX_LIB@/libXfixes.so",
"@PREFIX_LIB@/libXfixes.so.3",
"@PREFIX_LIB@/libXfixes.so.3.1.0"
]
},
"OpenCL": {
"Library" : "libOpenCL-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libOpenCL.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libOpenCL.so.1",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libOpenCL.so.1.0.0"
"@PREFIX_LIB@/libOpenCL.so",
"@PREFIX_LIB@/libOpenCL.so.1",
"@PREFIX_LIB@/libOpenCL.so.1.0.0"
]
},
"WaylandClient": {
"Library" : "libwayland-client-guest.so",
"Overlay": [
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libwayland-client.so",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libwayland-client.so.0",
"@PREFIX_LIB@/@PREFIX_ARCH@-linux-gnu/libwayland-client.so.0.20.0"
"@PREFIX_LIB@/libwayland-client.so",
"@PREFIX_LIB@/libwayland-client.so.0",
"@PREFIX_LIB@/libwayland-client.so.0.20.0"
]
},
"":{}
+1
View File
@@ -6,3 +6,4 @@ mask \xff\xff\xff\xff\xff\xfe\xfe\x00\x00\x00\x00\xff\xff\xff\xff\xff\xfe\xff\xf
credentials yes
fix_binary yes
preserve yes
expose_interpreter optional
+1
View File
@@ -6,3 +6,4 @@ mask \xff\xff\xff\xff\xff\xfe\xfe\x00\x00\x00\x00\xff\xff\xff\xff\xff\xfe\xff\xf
credentials yes
fix_binary yes
preserve yes
expose_interpreter optional
+1 -1
-89
View File
@@ -1,89 +0,0 @@
#include <FEXCore/fextl/fmt.h>
#include "Common/JitSymbols.h"
#include <fcntl.h>
#include <unistd.h>
namespace FEXCore {
JITSymbols::JITSymbols() {
}
JITSymbols::~JITSymbols() {
if (fd != -1) {
close(fd);
}
}
void JITSymbols::InitFile() {
// We can't use FILE here since we must be robust against forking processes closing our FD from under us.
#ifdef __ANDROID__
// Android simpleperf looks in /data/local/tmp instead of /tmp
const auto PerfMap = fextl::fmt::format("/data/local/tmp/perf-{}.map", getpid());
#else
const auto PerfMap = fextl::fmt::format("/tmp/perf-{}.map", getpid());
#endif
fd = open(PerfMap.c_str(), O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
}
void JITSymbols::Register(const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto Buffer = fextl::fmt::format("{} {:x} JIT_0x{:x}_{}\n", HostAddr, CodeSize, GuestAddr, HostAddr);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto Buffer = fextl::fmt::format("{} {:x} {}_{}\n", HostAddr, CodeSize, Name, HostAddr);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset) {
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto Buffer = fextl::fmt::format("{} {:x} {}+0x{:x} ({})\n", HostAddr, CodeSize, Name, Offset, HostAddr);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::RegisterNamedRegion(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto Buffer = fextl::fmt::format("{} {:x} {}\n", HostAddr, CodeSize, Name);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::RegisterJITSpace(const void *HostAddr, uint32_t CodeSize) {
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto Buffer = fextl::fmt::format("{} {:x} FEXJIT\n", HostAddr, CodeSize);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
} // namespace FEXCore
-24
View File
@@ -1,24 +0,0 @@
#pragma once
#include <cstdint>
#include <cstdio>
#include <memory>
#include <string_view>
namespace FEXCore {
class JITSymbols final {
public:
JITSymbols();
~JITSymbols();
void InitFile();
void Register(const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize);
void Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name);
void Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset);
void RegisterNamedRegion(const void *HostAddr, uint32_t CodeSize, std::string_view Name);
void RegisterJITSpace(const void *HostAddr, uint32_t CodeSize);
private:
int fd{-1};
};
}
@@ -1,563 +0,0 @@
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "FEXCore/Utils/AllocatorHooks.h"
#include "Interface/Core/ArchHelpers/CodeEmitter/Emitter.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Context/Context.h"
#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>
#include <aarch64/cpu-aarch64.h>
#include <aarch64/instructions-aarch64.h>
#include <cpu-features.h>
#include <utils-vixl.h>
#include <array>
#include <tuple>
#include <utility>
namespace FEXCore::CPU {
// Register x18 is unused in the current configuration.
// This is due to it being a platform register on wine platforms.
// TODO: Allow x18 register allocation in the future to gain one more register.
namespace x64 {
// All but x19 and x29 are caller saved
constexpr std::array<FEXCore::ARMEmitter::Register, 16> SRA = {
FEXCore::ARMEmitter::Reg::r4, FEXCore::ARMEmitter::Reg::r5,
FEXCore::ARMEmitter::Reg::r6, FEXCore::ARMEmitter::Reg::r7,
FEXCore::ARMEmitter::Reg::r8, FEXCore::ARMEmitter::Reg::r9,
FEXCore::ARMEmitter::Reg::r10, FEXCore::ARMEmitter::Reg::r11,
FEXCore::ARMEmitter::Reg::r12, FEXCore::ARMEmitter::Reg::r13,
FEXCore::ARMEmitter::Reg::r14, FEXCore::ARMEmitter::Reg::r15,
FEXCore::ARMEmitter::Reg::r16, FEXCore::ARMEmitter::Reg::r17,
FEXCore::ARMEmitter::Reg::r19, FEXCore::ARMEmitter::Reg::r29
};
constexpr std::array<FEXCore::ARMEmitter::Register, 9> RA = {
// All these callee saved
FEXCore::ARMEmitter::Reg::r20, FEXCore::ARMEmitter::Reg::r21,
FEXCore::ARMEmitter::Reg::r22, FEXCore::ARMEmitter::Reg::r23,
FEXCore::ARMEmitter::Reg::r24, FEXCore::ARMEmitter::Reg::r25,
FEXCore::ARMEmitter::Reg::r26, FEXCore::ARMEmitter::Reg::r27,
FEXCore::ARMEmitter::Reg::r30,
};
constexpr std::array<std::pair<FEXCore::ARMEmitter::Register, FEXCore::ARMEmitter::Register>, 4> RAPair = {{
{FEXCore::ARMEmitter::Reg::r20, FEXCore::ARMEmitter::Reg::r21},
{FEXCore::ARMEmitter::Reg::r22, FEXCore::ARMEmitter::Reg::r23},
{FEXCore::ARMEmitter::Reg::r24, FEXCore::ARMEmitter::Reg::r25},
{FEXCore::ARMEmitter::Reg::r26, FEXCore::ARMEmitter::Reg::r27},
}};
// All are caller saved
constexpr std::array<FEXCore::ARMEmitter::VRegister, 16> SRAFPR = {
FEXCore::ARMEmitter::VReg::v16, FEXCore::ARMEmitter::VReg::v17,
FEXCore::ARMEmitter::VReg::v18, FEXCore::ARMEmitter::VReg::v19,
FEXCore::ARMEmitter::VReg::v20, FEXCore::ARMEmitter::VReg::v21,
FEXCore::ARMEmitter::VReg::v22, FEXCore::ARMEmitter::VReg::v23,
FEXCore::ARMEmitter::VReg::v24, FEXCore::ARMEmitter::VReg::v25,
FEXCore::ARMEmitter::VReg::v26, FEXCore::ARMEmitter::VReg::v27,
FEXCore::ARMEmitter::VReg::v28, FEXCore::ARMEmitter::VReg::v29,
FEXCore::ARMEmitter::VReg::v30, FEXCore::ARMEmitter::VReg::v31
};
// v8..v15 = (lower 64bits) Callee saved
constexpr std::array<FEXCore::ARMEmitter::VRegister, 12> RAFPR = {
// v0 ~ v3 are used as temps.
// FEXCore::ARMEmitter::VReg::v0, FEXCore::ARMEmitter::VReg::v1,
// FEXCore::ARMEmitter::VReg::v2, FEXCore::ARMEmitter::VReg::v3,
FEXCore::ARMEmitter::VReg::v4, FEXCore::ARMEmitter::VReg::v5,
FEXCore::ARMEmitter::VReg::v6, FEXCore::ARMEmitter::VReg::v7,
FEXCore::ARMEmitter::VReg::v8, FEXCore::ARMEmitter::VReg::v9,
FEXCore::ARMEmitter::VReg::v10, FEXCore::ARMEmitter::VReg::v11,
FEXCore::ARMEmitter::VReg::v12, FEXCore::ARMEmitter::VReg::v13,
FEXCore::ARMEmitter::VReg::v14, FEXCore::ARMEmitter::VReg::v15,
};
}
namespace x32 {
// All but x19 and x29 are caller saved
constexpr std::array<FEXCore::ARMEmitter::Register, 8> SRA = {
FEXCore::ARMEmitter::Reg::r4, FEXCore::ARMEmitter::Reg::r5,
FEXCore::ARMEmitter::Reg::r6, FEXCore::ARMEmitter::Reg::r7,
FEXCore::ARMEmitter::Reg::r8, FEXCore::ARMEmitter::Reg::r9,
FEXCore::ARMEmitter::Reg::r10, FEXCore::ARMEmitter::Reg::r11,
};
constexpr std::array<FEXCore::ARMEmitter::Register, 17> RA = {
// All these callee saved
FEXCore::ARMEmitter::Reg::r20, FEXCore::ARMEmitter::Reg::r21,
FEXCore::ARMEmitter::Reg::r22, FEXCore::ARMEmitter::Reg::r23,
FEXCore::ARMEmitter::Reg::r24, FEXCore::ARMEmitter::Reg::r25,
FEXCore::ARMEmitter::Reg::r26, FEXCore::ARMEmitter::Reg::r27,
// Registers only available on 32-bit
// All these are caller saved (except for r19).
FEXCore::ARMEmitter::Reg::r12, FEXCore::ARMEmitter::Reg::r13,
FEXCore::ARMEmitter::Reg::r14, FEXCore::ARMEmitter::Reg::r15,
FEXCore::ARMEmitter::Reg::r16, FEXCore::ARMEmitter::Reg::r17,
FEXCore::ARMEmitter::Reg::r29, FEXCore::ARMEmitter::Reg::r30,
FEXCore::ARMEmitter::Reg::r19,
};
constexpr std::array<std::pair<FEXCore::ARMEmitter::Register, FEXCore::ARMEmitter::Register>, 8> RAPair = {{
{FEXCore::ARMEmitter::Reg::r20, FEXCore::ARMEmitter::Reg::r21},
{FEXCore::ARMEmitter::Reg::r22, FEXCore::ARMEmitter::Reg::r23},
{FEXCore::ARMEmitter::Reg::r24, FEXCore::ARMEmitter::Reg::r25},
{FEXCore::ARMEmitter::Reg::r26, FEXCore::ARMEmitter::Reg::r27},
{FEXCore::ARMEmitter::Reg::r12, FEXCore::ARMEmitter::Reg::r13},
{FEXCore::ARMEmitter::Reg::r14, FEXCore::ARMEmitter::Reg::r15},
{FEXCore::ARMEmitter::Reg::r16, FEXCore::ARMEmitter::Reg::r17},
{FEXCore::ARMEmitter::Reg::r29, FEXCore::ARMEmitter::Reg::r30},
}};
// All are caller saved
constexpr std::array<FEXCore::ARMEmitter::VRegister, 8> SRAFPR = {
FEXCore::ARMEmitter::VReg::v16, FEXCore::ARMEmitter::VReg::v17,
FEXCore::ARMEmitter::VReg::v18, FEXCore::ARMEmitter::VReg::v19,
FEXCore::ARMEmitter::VReg::v20, FEXCore::ARMEmitter::VReg::v21,
FEXCore::ARMEmitter::VReg::v22, FEXCore::ARMEmitter::VReg::v23,
};
// v8..v15 = (lower 64bits) Callee saved
constexpr std::array<FEXCore::ARMEmitter::VRegister, 20> RAFPR = {
// v0 ~ v3 are used as temps.
// FEXCore::ARMEmitter::VReg::v0, FEXCore::ARMEmitter::VReg::v1,
// FEXCore::ARMEmitter::VReg::v2, FEXCore::ARMEmitter::VReg::v3,
FEXCore::ARMEmitter::VReg::v4, FEXCore::ARMEmitter::VReg::v5,
FEXCore::ARMEmitter::VReg::v6, FEXCore::ARMEmitter::VReg::v7,
FEXCore::ARMEmitter::VReg::v8, FEXCore::ARMEmitter::VReg::v9,
FEXCore::ARMEmitter::VReg::v10, FEXCore::ARMEmitter::VReg::v11,
FEXCore::ARMEmitter::VReg::v12, FEXCore::ARMEmitter::VReg::v13,
FEXCore::ARMEmitter::VReg::v14, FEXCore::ARMEmitter::VReg::v15,
FEXCore::ARMEmitter::VReg::v24, FEXCore::ARMEmitter::VReg::v25,
FEXCore::ARMEmitter::VReg::v26, FEXCore::ARMEmitter::VReg::v27,
FEXCore::ARMEmitter::VReg::v28, FEXCore::ARMEmitter::VReg::v29,
FEXCore::ARMEmitter::VReg::v30, FEXCore::ARMEmitter::VReg::v31
};
}
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
Arm64Emitter::Arm64Emitter(FEXCore::Context::ContextImpl *ctx, size_t size)
: Emitter(size ? (uint8_t*)FEXCore::Allocator::VirtualAlloc(size, true) : nullptr, size)
, EmitterCTX {ctx} {
CPU.SetUp();
// Number of register available is dependent on what operating mode the proccess is in.
if (EmitterCTX->Config.Is64BitMode()) {
StaticRegisters = x64::SRA;
GeneralRegisters = x64::RA;
GeneralPairRegisters = x64::RAPair;
StaticFPRegisters = x64::SRAFPR;
GeneralFPRegisters = x64::RAFPR;
}
else {
ConfiguredDynamicRegisterBase = std::span(x32::RA.begin() + 8, 8);
StaticRegisters = x32::SRA;
GeneralRegisters = x32::RA;
GeneralPairRegisters = x32::RAPair;
StaticFPRegisters = x32::SRAFPR;
GeneralFPRegisters = x32::RAFPR;
}
}
Arm64Emitter::~Arm64Emitter() {
auto BufferSize = GetBufferSize();
if (BufferSize) {
FEXCore::Allocator::VirtualFree(GetBufferBase(), BufferSize);
}
}
void Arm64Emitter::LoadConstant(ARMEmitter::Size s, ARMEmitter::Register Reg, uint64_t Constant, bool NOPPad) {
bool Is64Bit = s == ARMEmitter::Size::i64Bit;
int Segments = Is64Bit ? 4 : 2;
if (Is64Bit && ((~Constant)>> 16) == 0) {
movn(s, Reg, (~Constant) & 0xFFFF);
if (NOPPad) {
nop(); nop(); nop();
}
return;
}
int NumMoves = 1;
int RequiredMoveSegments{};
// Count the number of move segments
// We only want to use ADRP+ADD if we have more than 1 segment
for (size_t i = 0; i < Segments; ++i) {
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
if (Part != 0) {
++RequiredMoveSegments;
}
}
// ADRP+ADD is specifically optimized in hardware
// Check if we can use this
auto PC = GetCursorAddress<uint64_t>();
// PC aligned to page
uint64_t AlignedPC = PC & ~0xFFFULL;
// Offset from aligned PC
int64_t AlignedOffset = static_cast<int64_t>(Constant) - static_cast<int64_t>(AlignedPC);
// If the aligned offset is within the 4GB window then we can use ADRP+ADD
// and the number of move segments more than 1
if (RequiredMoveSegments > 1 && vixl::IsInt32(AlignedOffset)) {
// If this is 4k page aligned then we only need ADRP
if ((AlignedOffset & 0xFFF) == 0) {
adrp(Reg, AlignedOffset >> 12);
}
else {
// If the constant is within 1MB of PC then we can still use ADR to load in a single instruction
// 21-bit signed integer here
int64_t SmallOffset = static_cast<int64_t>(Constant) - static_cast<int64_t>(PC);
if (vixl::IsInt21(SmallOffset)) {
adr(Reg, SmallOffset);
}
else {
// Need to use ADRP + ADD
adrp(Reg, AlignedOffset >> 12);
add(s, Reg, Reg, Constant & 0xFFF);
NumMoves = 2;
}
}
}
else {
movz(s, Reg, (Constant) & 0xFFFF, 0);
for (int i = 1; i < Segments; ++i) {
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
if (Part) {
movk(s, Reg, Part, i * 16);
++NumMoves;
}
}
}
if (NOPPad) {
for (int i = NumMoves; i < Segments; ++i) {
nop();
}
}
}
void Arm64Emitter::PushCalleeSavedRegisters() {
// We need to save pairs of registers
// We save r19-r30
const fextl::vector<std::pair<ARMEmitter::XRegister, ARMEmitter::XRegister>> CalleeSaved = {{
#ifdef _WIN32
// Platform register, Just save it twice to make logic easy.
{ARMEmitter::XReg::x18, ARMEmitter::XReg::x18},
#endif
{ARMEmitter::XReg::x19, ARMEmitter::XReg::x20},
{ARMEmitter::XReg::x21, ARMEmitter::XReg::x22},
{ARMEmitter::XReg::x23, ARMEmitter::XReg::x24},
{ARMEmitter::XReg::x25, ARMEmitter::XReg::x26},
{ARMEmitter::XReg::x27, ARMEmitter::XReg::x28},
{ARMEmitter::XReg::x29, ARMEmitter::XReg::x30},
}};
for (auto &RegPair : CalleeSaved) {
stp<ARMEmitter::IndexType::PRE>(RegPair.first, RegPair.second, ARMEmitter::Reg::rsp, -16);
}
// Additionally we need to store the lower 64bits of v8-v15
// Here's a fun thing, we can use two ST4 instructions to store everything
// We just need a single sub to sp before that
const std::array<
std::tuple<ARMEmitter::DRegister,
ARMEmitter::DRegister,
ARMEmitter::DRegister,
ARMEmitter::DRegister>, 2> FPRs = {{
{ARMEmitter::DReg::d8, ARMEmitter::DReg::d9, ARMEmitter::DReg::d10, ARMEmitter::DReg::d11},
{ARMEmitter::DReg::d12, ARMEmitter::DReg::d13, ARMEmitter::DReg::d14, ARMEmitter::DReg::d15},
}};
uint32_t VectorSaveSize = sizeof(uint64_t) * 8;
sub(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, VectorSaveSize);
// SP supporting move
// We just saved x19 so it is safe
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r19, ARMEmitter::Reg::rsp, 0);
for (auto &RegQuad : FPRs) {
st4(ARMEmitter::SubRegSize::i64Bit,
std::get<0>(RegQuad),
std::get<1>(RegQuad),
std::get<2>(RegQuad),
std::get<3>(RegQuad),
0,
ARMEmitter::Reg::r19,
32);
}
}
void Arm64Emitter::PopCalleeSavedRegisters() {
const std::array<
std::tuple<ARMEmitter::DRegister,
ARMEmitter::DRegister,
ARMEmitter::DRegister,
ARMEmitter::DRegister>, 2> FPRs = {{
{ARMEmitter::DReg::d12, ARMEmitter::DReg::d13, ARMEmitter::DReg::d14, ARMEmitter::DReg::d15},
{ARMEmitter::DReg::d8, ARMEmitter::DReg::d9, ARMEmitter::DReg::d10, ARMEmitter::DReg::d11},
}};
for (auto &RegQuad : FPRs) {
ld4(ARMEmitter::SubRegSize::i64Bit,
std::get<0>(RegQuad),
std::get<1>(RegQuad),
std::get<2>(RegQuad),
std::get<3>(RegQuad),
0,
ARMEmitter::Reg::rsp,
32);
}
const fextl::vector<std::pair<ARMEmitter::XRegister, ARMEmitter::XRegister>> CalleeSaved = {{
{ARMEmitter::XReg::x29, ARMEmitter::XReg::x30},
{ARMEmitter::XReg::x27, ARMEmitter::XReg::x28},
{ARMEmitter::XReg::x25, ARMEmitter::XReg::x26},
{ARMEmitter::XReg::x23, ARMEmitter::XReg::x24},
{ARMEmitter::XReg::x21, ARMEmitter::XReg::x22},
{ARMEmitter::XReg::x19, ARMEmitter::XReg::x20},
#ifdef _WIN32
// Platform register.
{ARMEmitter::XReg::x18, ARMEmitter::XReg::zr},
#endif
}};
for (auto &RegPair : CalleeSaved) {
ldp<ARMEmitter::IndexType::POST>(RegPair.first, RegPair.second, ARMEmitter::Reg::rsp, 16);
}
}
void Arm64Emitter::SpillStaticRegs(FEXCore::ARMEmitter::Register TmpReg, bool FPRs, uint32_t GPRSpillMask, uint32_t FPRSpillMask) {
if (!StaticRegisterAllocation()) {
return;
}
for (size_t i = 0; i < StaticRegisters.size(); i+=2) {
auto Reg1 = StaticRegisters[i];
auto Reg2 = StaticRegisters[i+1];
if (((1U << Reg1.Idx()) & GPRSpillMask) &&
((1U << Reg2.Idx()) & GPRSpillMask)) {
stp<ARMEmitter::IndexType::OFFSET>(Reg1.X(), Reg2.X(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i]));
}
else if (((1U << Reg1.Idx()) & GPRSpillMask)) {
str(Reg1.X(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i]));
}
else if (((1U << Reg2.Idx()) & GPRSpillMask)) {
str(Reg2.X(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i+1]));
}
}
if (FPRs) {
if (EmitterCTX->HostFeatures.SupportsAVX) {
for (size_t i = 0; i < StaticFPRegisters.size(); i++) {
const auto Reg = StaticFPRegisters[i];
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.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
// 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]));
for (size_t i = 0; i < StaticFPRegisters.size(); i += 4) {
const auto Reg1 = StaticFPRegisters[i];
const auto Reg2 = StaticFPRegisters[i + 1];
const auto Reg3 = StaticFPRegisters[i + 2];
const auto Reg4 = StaticFPRegisters[i + 3];
st1<ARMEmitter::SubRegSize::i64Bit>(Reg1.Q(), Reg2.Q(), Reg3.Q(), Reg4.Q(), TmpReg, 64);
}
}
else {
for (size_t i = 0; i < StaticFPRegisters.size(); i += 2) {
const auto Reg1 = StaticFPRegisters[i];
const auto Reg2 = StaticFPRegisters[i + 1];
if (((1U << Reg1.Idx()) & FPRSpillMask) &&
((1U << Reg2.Idx()) & FPRSpillMask)) {
stp<ARMEmitter::IndexType::OFFSET>(Reg1.Q(), Reg2.Q(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0]));
}
else if (((1U << Reg1.Idx()) & FPRSpillMask)) {
str(Reg1.Q(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0]));
}
else if (((1U << Reg2.Idx()) & FPRSpillMask)) {
str(Reg2.Q(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i+1][0]));
}
}
}
}
}
}
void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRFillMask) {
if (!StaticRegisterAllocation()) {
return;
}
if (FPRs) {
if (EmitterCTX->HostFeatures.SupportsAVX) {
// Set up predicate registers.
// We don't bother spilling these in SpillStaticRegs,
// since all that matters is we restore them on a fill.
// It's not a concern if they get trounced by something else.
ptrue<ARMEmitter::SubRegSize::i8Bit>(PRED_TMP_16B, ARMEmitter::PredicatePattern::SVE_VL16);
ptrue<ARMEmitter::SubRegSize::i8Bit>(PRED_TMP_32B, ARMEmitter::PredicatePattern::SVE_VL32);
for (size_t i = 0; i < StaticFPRegisters.size(); i++) {
const auto Reg = StaticFPRegisters[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.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 = StaticRegisters[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]));
for (size_t i = 0; i < StaticFPRegisters.size(); i += 4) {
const auto Reg1 = StaticFPRegisters[i];
const auto Reg2 = StaticFPRegisters[i + 1];
const auto Reg3 = StaticFPRegisters[i + 2];
const auto Reg4 = StaticFPRegisters[i + 3];
ld1<ARMEmitter::SubRegSize::i64Bit>(Reg1.Q(), Reg2.Q(), Reg3.Q(), Reg4.Q(), TmpReg, 64);
}
}
else {
for (size_t i = 0; i < StaticFPRegisters.size(); i += 2) {
const auto Reg1 = StaticFPRegisters[i];
const auto Reg2 = StaticFPRegisters[i + 1];
if (((1U << Reg1.Idx()) & FPRFillMask) &&
((1U << Reg2.Idx()) & FPRFillMask)) {
ldp<ARMEmitter::IndexType::OFFSET>(Reg1.Q(), Reg2.Q(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0]));
}
else if (((1U << Reg1.Idx()) & FPRFillMask)) {
ldr(Reg1.Q(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0]));
}
else if (((1U << Reg2.Idx()) & FPRFillMask)) {
ldr(Reg2.Q(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i+1][0]));
}
}
}
}
}
for (size_t i = 0; i < StaticRegisters.size(); i+=2) {
auto Reg1 = StaticRegisters[i];
auto Reg2 = StaticRegisters[i+1];
if (((1U << Reg1.Idx()) & GPRFillMask) &&
((1U << Reg2.Idx()) & GPRFillMask)) {
ldp<ARMEmitter::IndexType::OFFSET>(Reg1.X(), Reg2.X(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i]));
}
else if ((1U << Reg1.Idx()) & GPRFillMask) {
ldr(Reg1.X(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i]));
}
else if ((1U << Reg2.Idx()) & GPRFillMask) {
ldr(Reg2.X(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i+1]));
}
}
}
void Arm64Emitter::PushDynamicRegsAndLR(FEXCore::ARMEmitter::Register TmpReg) {
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
const auto GPRSize = (ConfiguredDynamicRegisterBase.size() + 1) * Core::CPUState::GPR_REG_SIZE;
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE
: Core::CPUState::XMM_SSE_REG_SIZE;
const auto FPRSize = GeneralFPRegisters.size() * FPRRegSize;
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
sub(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset);
// rsp capable move
add(ARMEmitter::Size::i64Bit, TmpReg, ARMEmitter::Reg::rsp, 0);
if (CanUseSVE) {
for (size_t i = 0; i < GeneralFPRegisters.size(); i += 4) {
const auto Reg1 = GeneralFPRegisters[i];
const auto Reg2 = GeneralFPRegisters[i + 1];
const auto Reg3 = GeneralFPRegisters[i + 2];
const auto Reg4 = GeneralFPRegisters[i + 3];
st4b(Reg1.Z(), Reg2.Z(), Reg3.Z(), Reg4.Z(), PRED_TMP_32B, TmpReg, 0);
add(ARMEmitter::Size::i64Bit, TmpReg, TmpReg, 32 * 4);
}
} else {
LOGMAN_THROW_A_FMT(GeneralFPRegisters.size() % 4 == 0, "Needs to have multiple of 4 FPRs for RA");
for (size_t i = 0; i < GeneralFPRegisters.size(); i += 4) {
const auto Reg1 = GeneralFPRegisters[i];
const auto Reg2 = GeneralFPRegisters[i + 1];
const auto Reg3 = GeneralFPRegisters[i + 2];
const auto Reg4 = GeneralFPRegisters[i + 3];
st1<ARMEmitter::SubRegSize::i64Bit>(Reg1.Q(), Reg2.Q(), Reg3.Q(), Reg4.Q(), TmpReg, 64);
}
}
for (size_t i = 0; i < ConfiguredDynamicRegisterBase.size(); i += 2) {
const auto Reg1 = ConfiguredDynamicRegisterBase[i];
const auto Reg2 = ConfiguredDynamicRegisterBase[i + 1];
stp<ARMEmitter::IndexType::POST>(Reg1.X(), Reg2.X(), TmpReg, 16);
}
str(ARMEmitter::XReg::lr, TmpReg, 0);
}
void Arm64Emitter::PopDynamicRegsAndLR() {
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
if (CanUseSVE) {
for (size_t i = 0; i < GeneralFPRegisters.size(); i += 4) {
const auto Reg1 = GeneralFPRegisters[i];
const auto Reg2 = GeneralFPRegisters[i + 1];
const auto Reg3 = GeneralFPRegisters[i + 2];
const auto Reg4 = GeneralFPRegisters[i + 3];
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 {
for (size_t i = 0; i < GeneralFPRegisters.size(); i += 4) {
const auto Reg1 = GeneralFPRegisters[i];
const auto Reg2 = GeneralFPRegisters[i + 1];
const auto Reg3 = GeneralFPRegisters[i + 2];
const auto Reg4 = GeneralFPRegisters[i + 3];
ld1<ARMEmitter::SubRegSize::i64Bit>(Reg1.Q(), Reg2.Q(), Reg3.Q(), Reg4.Q(), ARMEmitter::Reg::rsp, 64);
}
}
for (size_t i = 0; i < ConfiguredDynamicRegisterBase.size(); i += 2) {
const auto Reg1 = ConfiguredDynamicRegisterBase[i];
const auto Reg2 = ConfiguredDynamicRegisterBase[i + 1];
ldp<ARMEmitter::IndexType::POST>(Reg1.X(), Reg2.X(), ARMEmitter::Reg::rsp, 16);
}
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
}
void Arm64Emitter::Align16B() {
uint64_t CurrentOffset = GetCursorAddress<uint64_t>();
for (uint64_t i = (16 - (CurrentOffset & 0xF)); i != 0; i -= 4) {
nop();
}
}
}
-88
View File
@@ -1,88 +0,0 @@
#include "FEXCore/Utils/AllocatorHooks.h"
#include "Interface/Context/Context.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include <FEXCore/Core/CPUBackend.h>
namespace FEXCore {
namespace CPU {
CPUBackend::CPUBackend(FEXCore::Core::InternalThreadState *ThreadState, size_t InitialCodeSize, size_t MaxCodeSize)
: ThreadState(ThreadState), InitialCodeSize(InitialCodeSize), MaxCodeSize(MaxCodeSize) {}
CPUBackend::~CPUBackend() {
for (auto CodeBuffer : CodeBuffers) {
FreeCodeBuffer(CodeBuffer);
}
CodeBuffers.clear();
}
auto CPUBackend::GetEmptyCodeBuffer() -> CodeBuffer * {
if (ThreadState->CurrentFrame->SignalHandlerRefCounter == 0) {
if (CodeBuffers.empty()) {
auto NewCodeBuffer = AllocateNewCodeBuffer(InitialCodeSize);
EmplaceNewCodeBuffer(NewCodeBuffer);
} else {
if (CodeBuffers.size() > 1) {
// If we have more than one code buffer we are tracking then walk them and delete
// This is a cleanup step
for (size_t i = 1; i < CodeBuffers.size(); i++) {
FreeCodeBuffer(CodeBuffers[i]);
}
CodeBuffers.resize(1);
}
// Set the current code buffer to the initial
CurrentCodeBuffer = &CodeBuffers[0];
if (CurrentCodeBuffer->Size != MaxCodeSize) {
FreeCodeBuffer(*CurrentCodeBuffer);
// Resize the code buffer and reallocate our code size
CurrentCodeBuffer->Size *= 1.5;
CurrentCodeBuffer->Size = std::min(CurrentCodeBuffer->Size, MaxCodeSize);
*CurrentCodeBuffer = AllocateNewCodeBuffer(CurrentCodeBuffer->Size);
}
}
} else {
// We have signal handlers that have generated code
// This means that we can not safely clear the code at this point in time
// Allocate some new code buffers that we can switch over to instead
auto NewCodeBuffer = AllocateNewCodeBuffer(InitialCodeSize);
EmplaceNewCodeBuffer(NewCodeBuffer);
}
return CurrentCodeBuffer;
}
auto CPUBackend::AllocateNewCodeBuffer(size_t Size) -> CodeBuffer {
CodeBuffer Buffer;
Buffer.Size = Size;
Buffer.Ptr = static_cast<uint8_t *>(
FEXCore::Allocator::VirtualAlloc(Buffer.Size, true));
LOGMAN_THROW_AA_FMT(!!Buffer.Ptr, "Couldn't allocate code buffer");
if (static_cast<Context::ContextImpl*>(ThreadState->CTX)->Config.GlobalJITNaming()) {
static_cast<Context::ContextImpl*>(ThreadState->CTX)->Symbols.RegisterJITSpace(Buffer.Ptr, Buffer.Size);
}
return Buffer;
}
void CPUBackend::FreeCodeBuffer(CodeBuffer Buffer) {
FEXCore::Allocator::VirtualFree(Buffer.Ptr, Buffer.Size);
}
bool CPUBackend::IsAddressInCodeBuffer(uintptr_t Address) const {
for (auto &Buffer: CodeBuffers) {
auto start = (uintptr_t)Buffer.Ptr;
auto end = start + Buffer.Size;
if (Address >= start && Address < end) {
return true;
}
}
return false;
}
}
}
-306
View File
@@ -1,306 +0,0 @@
#pragma once
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/Config/Config.h>
#include <FEXCore/fextl/vector.h>
#include <cstdint>
#include <unordered_map>
#include <utility>
namespace FEXCore {
namespace Context {
class ContextImpl;
}
// Debugging define to switch what family of CPU we execute as.
// Might be useful if an application makes an assumption about a CPU.
// #define CPUID_AMD
class CPUIDEmu final {
private:
constexpr static uint32_t CPUID_VENDOR_INTEL1 = 0x756E6547; // "Genu"
constexpr static uint32_t CPUID_VENDOR_INTEL2 = 0x49656E69; // "ineI"
constexpr static uint32_t CPUID_VENDOR_INTEL3 = 0x6C65746E; // "ntel"
constexpr static uint32_t CPUID_VENDOR_AMD1 = 0x68747541; // "Auth"
constexpr static uint32_t CPUID_VENDOR_AMD2 = 0x69746E65; // "enti"
constexpr static uint32_t CPUID_VENDOR_AMD3 = 0x444D4163; // "cAMD"
public:
// X86 cacheline size effectively has to be hardcoded to 64
// if we report anything differently then applications are likely to break
constexpr static uint64_t CACHELINE_SIZE = 64;
void Init(FEXCore::Context::ContextImpl *ctx);
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, uint32_t Leaf) {
if (Function < Primary.size()) {
const auto Handler = Primary[Function];
return (this->*Handler)(Leaf);
}
constexpr uint32_t HypervisorBase = 0x4000'0000;
if (Function >= HypervisorBase && Function < (HypervisorBase + Hypervisor.size())) {
const auto Handler = Hypervisor[Function - HypervisorBase];
return (this->*Handler)(Leaf);
}
constexpr uint32_t ExtendedBase = 0x8000'0000;
if (Function >= ExtendedBase && Function < (ExtendedBase + Extended.size())) {
const auto Handler = Extended[Function - ExtendedBase];
return (this->*Handler)(Leaf);
}
return Function_Reserved(Leaf);
}
FEXCore::CPUID::FunctionResults RunFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) {
if (Function == 0x8000'0002U)
return Function_8000_0002h(Leaf, CPU % PerCPUData.size());
else if (Function == 0x8000'0003U)
return Function_8000_0003h(Leaf, CPU % PerCPUData.size());
else
return Function_8000_0004h(Leaf, CPU % PerCPUData.size());
}
FEXCore::CPUID::XCRResults RunXCRFunction(uint32_t Function) {
if (Function >= 1) {
// XCR function 1 is not yet supported.
return {};
}
return XCRFunction_0h();
}
private:
FEXCore::Context::ContextImpl *CTX;
bool Hybrid{};
FEX_CONFIG_OPT(Cores, THREADS);
FEX_CONFIG_OPT(HideHypervisorBit, HIDEHYPERVISORBIT);
// XFEATURE_ENABLED_MASK
// Mask that configures what features are enabled on the CPU.
// Affects XSAVE and XRSTOR when modified.
// Bit layout is as follows.
// [0] - x87 enabled
// [1] - SSE enabled
// [2] - YMM enabled (256-bit SSE)
// [8:3] - Reserved. MBZ.
// [9] - MPK
// [10] - Reserved. MBZ.
// [11] - CET_U
// [12] - CET_S
// [61:13] - Reserved. MBZ.
// [62] - LWP (Lightweight profiling)
// [63] - Reserved for XCR bit vector expansion. MBZ.
// Always enable x87 and SSE by default.
constexpr static uint64_t XCR0_X87 = 1ULL << 0;
constexpr static uint64_t XCR0_SSE = 1ULL << 1;
constexpr static uint64_t XCR0_AVX = 1ULL << 2;
uint64_t XCR0 {
XCR0_X87 |
XCR0_SSE
};
uint32_t SupportsAVX() const {
return (XCR0 & XCR0_AVX) ? 1 : 0;
}
using FunctionHandler = FEXCore::CPUID::FunctionResults (CPUIDEmu::*)(uint32_t Leaf);
struct CPUData {
const char *ProductName{};
#ifdef _M_ARM_64
uint32_t MIDR{};
#endif
bool IsBig{};
};
fextl::vector<CPUData> PerCPUData{};
// Functions
FEXCore::CPUID::FunctionResults Function_0h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_01h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_02h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_04h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_06h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_07h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_0Dh(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_15h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_1Ah(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_4000_0000h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_4000_0001h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0000h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0001h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0002h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0003h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0004h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0002h(uint32_t Leaf, uint32_t CPU);
FEXCore::CPUID::FunctionResults Function_8000_0003h(uint32_t Leaf, uint32_t CPU);
FEXCore::CPUID::FunctionResults Function_8000_0004h(uint32_t Leaf, uint32_t CPU);
FEXCore::CPUID::FunctionResults Function_8000_0005h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0006h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0007h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0008h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_0019h(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_8000_001Dh(uint32_t Leaf);
FEXCore::CPUID::FunctionResults Function_Reserved(uint32_t Leaf);
FEXCore::CPUID::XCRResults XCRFunction_0h();
void SetupHostHybridFlag();
static constexpr std::array<FunctionHandler, 27> Primary = {
// 0: Highest function parameter and ID
&CPUIDEmu::Function_0h,
// 1: Processor info
&CPUIDEmu::Function_01h,
// 2: Cache and TLB info
&CPUIDEmu::Function_02h,
// 3: Serial Number(previously), now reserved
&CPUIDEmu::Function_Reserved,
#ifndef CPUID_AMD
// 4: Deterministic cache parameters for each level
&CPUIDEmu::Function_04h,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 5: Monitor/mwait
&CPUIDEmu::Function_Reserved,
// 6: Thermal and power management
&CPUIDEmu::Function_06h,
// 7: Extended feature flags
&CPUIDEmu::Function_07h,
// 0x08: Reserved?
&CPUIDEmu::Function_Reserved,
// 9: Direct Cache Access information
&CPUIDEmu::Function_Reserved,
// 0x0A: Architectural performance monitoring
&CPUIDEmu::Function_Reserved,
// 0x0B: Extended topology enumeration
&CPUIDEmu::Function_Reserved,
// 0x0C: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x0D: Processor extended state enumeration
&CPUIDEmu::Function_0Dh,
// 0x0E: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x0F: Intel RDT monitoring
&CPUIDEmu::Function_Reserved,
// 0x10: Intel RDT allocation enumeration
&CPUIDEmu::Function_Reserved,
// 0x12: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x12: Intel SGX capability enumeration
&CPUIDEmu::Function_Reserved,
// 0x13: Reserved
&CPUIDEmu::Function_Reserved,
// 0x14: Intel Processor trace
&CPUIDEmu::Function_Reserved,
#ifndef CPUID_AMD
// Timestamp counter information
// Doesn't exist on AMD hardware
&CPUIDEmu::Function_15h,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 0x16: Processor frequency information
&CPUIDEmu::Function_Reserved,
// 0x17: SoC vendor attribute enumeration
&CPUIDEmu::Function_Reserved,
// 0x18: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x19: Reserved?
&CPUIDEmu::Function_Reserved,
#ifndef CPUID_AMD
// 0x1A: Hybrid Information Sub-leaf
&CPUIDEmu::Function_1Ah,
#else
&CPUIDEmu::Function_Reserved,
#endif
};
static constexpr std::array<FunctionHandler, 2> Hypervisor = {
// Hypervisor CPUID information leaf
&CPUIDEmu::Function_4000_0000h,
// FEX-Emu specific leaf
&CPUIDEmu::Function_4000_0001h,
};
static constexpr std::array<FunctionHandler, 32> Extended = {
// Largest extended function number
&CPUIDEmu::Function_8000_0000h,
// Processor vendor
&CPUIDEmu::Function_8000_0001h,
// Processor brand string
&CPUIDEmu::Function_8000_0002h,
// Processor brand string continued
&CPUIDEmu::Function_8000_0003h,
// Processor brand string continued
&CPUIDEmu::Function_8000_0004h,
#ifdef CPUID_AMD
// 0x8000'0005: L1 Cache and TLB identifiers
&CPUIDEmu::Function_8000_0005h,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 0x8000'0006: L2 Cache identifiers
&CPUIDEmu::Function_8000_0006h,
// 0x8000'0007: Advanced power management information
&CPUIDEmu::Function_8000_0007h,
// 0x8000'0008: Virtual and physical address sizes
&CPUIDEmu::Function_8000_0008h,
// 0x8000'0009: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000A: SVM Revision
&CPUIDEmu::Function_Reserved,
// 0x8000'000B: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000C: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000D: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000E: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'000F: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0010: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0011: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0012: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0013: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0014: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0015: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0016: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0017: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0018: Reserved?
&CPUIDEmu::Function_Reserved,
// 0x8000'0019: TLB 1GB page identifiers
&CPUIDEmu::Function_8000_0019h,
// 0x8000'001A: Performance optimization identifiers
&CPUIDEmu::Function_Reserved,
// 0x8000'001B: Instruction based sampling identifiers
&CPUIDEmu::Function_Reserved,
// 0x8000'001C: Lightweight profiling capabilities
&CPUIDEmu::Function_Reserved,
#ifdef CPUID_AMD
// 0x8000'001D: Cache properties
&CPUIDEmu::Function_8000_001Dh,
#else
&CPUIDEmu::Function_Reserved,
#endif
// 0x8000'001E: Extended APIC ID
&CPUIDEmu::Function_Reserved,
// 0x8000'001F: AMD Secure Encryption
&CPUIDEmu::Function_Reserved,
};
};
}
-23
View File
@@ -1,23 +0,0 @@
#pragma once
namespace FEXCore {
class CodeLoader;
}
namespace FEXCore::Context {
class ContextImpl;
}
namespace FEXCore::CPU {
/**
* @brief Create the CPU core backend for the context passed in
*
* @param CTX
*
* @return true if core was able to be create
*/
bool CreateCPUCore(FEXCore::Context::ContextImpl *CTX);
bool LoadCode(FEXCore::Context::ContextImpl *CTX, FEXCore::CodeLoader *Loader);
}
@@ -1,66 +0,0 @@
#pragma once
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#ifdef VIXL_SIMULATOR
#include <aarch64/simulator-aarch64.h>
#endif
namespace FEXCore::Core {
struct InternalThreadState;
}
#define STATE_PTR(STATE_TYPE, FIELD) \
STATE.R(), offsetof(FEXCore::Core::STATE_TYPE, FIELD)
namespace FEXCore::CPU {
class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
public:
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;
#ifdef VIXL_SIMULATOR
void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) override;
void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) override;
#endif
void EmitDispatcher();
uint16_t GetSRAGPRCount() const override {
return StaticRegisters.size();
}
uint16_t GetSRAFPRCount() const override {
return StaticFPRegisters.size();
}
void GetSRAGPRMapping(uint8_t Mapping[16]) const override {
for (size_t i = 0; i < StaticRegisters.size(); ++i) {
Mapping[i] = StaticRegisters[i].Idx();
}
}
void GetSRAFPRMapping(uint8_t Mapping[16]) const override {
for (size_t i = 0; i < StaticFPRegisters.size(); ++i) {
Mapping[i] = StaticFPRegisters[i].Idx();
}
}
private:
// Long division helpers
uint64_t LUDIVHandlerAddress{};
uint64_t LDIVHandlerAddress{};
uint64_t LUREMHandlerAddress{};
uint64_t LREMHandlerAddress{};
#ifdef VIXL_SIMULATOR
vixl::aarch64::Decoder Decoder;
vixl::aarch64::Simulator Simulator;
#endif
};
}
@@ -1,53 +0,0 @@
#include "Interface/Context/Context.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/X86HelperGen.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/SignalDelegator.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/Event.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
#include <atomic>
#include <condition_variable>
#include <csignal>
#include <cstring>
#include <signal.h>
namespace FEXCore::CPU {
void Dispatcher::SleepThread(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::CpuStateFrame *Frame) {
auto Thread = Frame->Thread;
--ctx->IdleWaitRefCount;
ctx->IdleWaitCV.notify_all();
Thread->RunningEvents.ThreadSleeping = true;
// Go to sleep
Thread->StartRunning.Wait();
Thread->RunningEvents.Running = true;
++ctx->IdleWaitRefCount;
Thread->RunningEvents.ThreadSleeping = false;
ctx->IdleWaitCV.notify_all();
}
uint64_t Dispatcher::GetCompileBlockPtr() {
using ClassPtrType = void (FEXCore::Context::ContextImpl::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast CompileBlockPtr;
CompileBlockPtr.ClassPtr = &FEXCore::Context::ContextImpl::CompileBlockJit;
return CompileBlockPtr.Data;
}
}
@@ -1,438 +0,0 @@
#include "FEXCore/Utils/AllocatorHooks.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/X86HelperGen.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/fextl/memory.h>
#include <FEXCore/fextl/string.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <cmath>
#include <memory>
#include <stddef.h>
#include <stdint.h>
#define STATE_PTR(STATE_TYPE, FIELD) \
[STATE + offsetof(FEXCore::Core::STATE_TYPE, FIELD)]
namespace FEXCore::CPU {
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
#define STATE r14
X86Dispatcher::X86Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config)
: Dispatcher(ctx, config)
, Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE,
FEXCore::Allocator::VirtualAlloc(MAX_DISPATCHER_CODE_SIZE, true),
nullptr) {
LOGMAN_THROW_AA_FMT(!config.StaticRegisterAllocation, "X86 dispatcher does not support SRA");
using namespace Xbyak;
using namespace Xbyak::util;
DispatchPtr = getCurr<AsmDispatch>();
// Temp registers
// rax, rcx, rdx, rsi, r8, r9,
// r10, r11
//
// Callee Saved
// rbx, rbp, r12, r13, r14, r15
//
// 1St Argument: rdi <ThreadState>
// XMM:
// All temp
// while (true) {
// Ptr = FindBlock(RIP)
// if (!Ptr)
// Ptr = CTX->CompileBlock(RIP);
//
// if (Ptr)
// Ptr();
// else
// {
// Ptr = FallbackCore->CompileBlock()
// if (Ptr)
// Ptr()
// else {
// ShouldStop = true;
// }
// }
// }
// Bunch of exit state stuff
// x86-64 ABI has the stack aligned when /call/ happens
// Which means the destination has a misaligned stack at that point
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
mov(STATE, rdi);
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
// regardless of where we were in the stack
mov(qword STATE_PTR(CpuStateFrame, ReturningStackLocation), rsp);
Label LoopTop;
Label FullLookup;
Label NoBlock;
Label ExitBlock;
Label ThreadPauseHandler;
L(LoopTop);
AbsoluteLoopTopAddressFillSRA = AbsoluteLoopTopAddress = getCurr<uint64_t>();
{
// Load our RIP
mov(rdx, qword STATE_PTR(CPUState, rip));
// L1 Cache
mov(r13, qword STATE_PTR(CpuStateFrame, Pointers.Common.L1Pointer));
mov(rax, rdx);
and_(rax, LookupCache::L1_ENTRIES_MASK);
shl(rax, 4);
cmp(qword[r13 + rax + offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode)], rdx);
jne(FullLookup);
jmp(qword[r13 + rax + offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode)]);
L(FullLookup);
mov(r13, qword STATE_PTR(CpuStateFrame, Pointers.Common.L2Pointer));
// Full lookup
uint64_t VirtualMemorySize = CTX->Config.VirtualMemSize;
mov(rax, rdx);
mov(rbx, VirtualMemorySize - 1);
and_(rax, rbx);
shr(rax, 12);
// Load page pointer
mov(rdi, qword [r13 + rax * 8]);
cmp(rdi, 0);
je(NoBlock);
mov (rax, rdx);
and_(rax, 0x0FFF);
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
// check for aliasing
mov(rcx, qword [rdi + rax + 8]);
cmp(rcx, rdx);
jne(NoBlock);
// Load the block pointer
mov(rax, qword [rdi + rax]);
cmp(rax, 0);
je(NoBlock);
// Update L1
mov(r13, qword STATE_PTR(CpuStateFrame, Pointers.Common.L1Pointer));
mov(rcx, rdx);
and_(rcx, LookupCache::L1_ENTRIES_MASK);
shl(rcx, 1);
mov(qword[r13 + rcx*8 + 8], rdx);
mov(qword[r13 + rcx*8 + 0], rax);
// Real block if we made it here
jmp(rax);
}
{
L(ExitBlock);
ThreadStopHandlerAddress = getCurr<uint64_t>();
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
// Block creation
{
L(NoBlock);
inc(qword [STATE + offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount)]);
// {rdi, rsi, rdx}
mov(rdi, reinterpret_cast<uint64_t>(CTX));
mov(rsi, STATE);
mov(rax, GetCompileBlockPtr());
call(rax);
dec(qword [STATE + offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount)]);
Label AfterStore;
// Skip the deferred fault address if the refcount isn't zero
jne(AfterStore);
mov(rax, qword [STATE + offsetof(FEXCore::Core::CPUState, DeferredSignalFaultAddress)]);
mov(rax, qword [rax]);
L(AfterStore);
// rdx already contains RIP here
jmp(LoopTop);
}
{
ExitFunctionLinkerAddress = getCurr<uint64_t>();
inc(qword [STATE + offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount)]);
// {rdi, rsi}
mov(rdi, STATE);
mov(rsi, rax); // rax is set at the block end
call(qword STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionLink));
dec(qword [STATE + offsetof(FEXCore::Core::CPUState, DeferredSignalRefCount)]);
Label AfterStore;
// Skip the deferred fault address if the refcount isn't zero
jne(AfterStore);
mov(rbx, qword [STATE + offsetof(FEXCore::Core::CPUState, DeferredSignalFaultAddress)]);
mov(qword [rbx], rbx);
L(AfterStore);
jmp(rax);
}
{
// Pause handler
ThreadPauseHandlerAddress = getCurr<uint64_t>();
L(ThreadPauseHandler);
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
mov(rsi, STATE);
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
call(rax);
// XXX: Unsupported atm
PauseReturnInstruction = getCurr<uint64_t>();
ud2();
}
{
CallbackPtr = getCurr<JITCallback>();
push(rbx);
push(rbp);
push(r12);
push(r13);
push(r14);
push(r15);
sub(rsp, 8);
// First thing we need to move the thread state pointer back in to our register
mov(STATE, rdi);
// XXX: XMM?
// Make sure to adjust the refcounter so we don't clear the cache now
add(qword STATE_PTR(CpuStateFrame, SignalHandlerRefCounter), 1);
// Now push the callback return trampoline to the guest stack
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
mov(rax, CTX->X86CodeGen.CallbackReturn);
// Store the trampoline to the guest stack
// Guest stack is now correctly misaligned after a regular call instruction
sub(qword STATE_PTR(CpuStateFrame, State.gregs[X86State::REG_RSP]), 16);
mov(rbx, qword STATE_PTR(CpuStateFrame, State.gregs[X86State::REG_RSP]));
mov(qword [rbx], rax);
// Store RIP to the context state
mov(qword STATE_PTR(CpuStateFrame, State.rip), rsi);
// Back to the loop top now
jmp(LoopTop);
}
{
// Signal return handler
SignalHandlerReturnAddress = getCurr<uint64_t>();
ud2();
}
{
// RT Signal return handler
SignalHandlerReturnAddressRT = getCurr<uint64_t>();
ud2();
}
{
// Guest SIGILL handler
// Needs to be distinct from the SignalHandlerReturnAddress
GuestSignal_SIGILL = getCurr<uint64_t>();
ud2();
}
{
// Guest SIGTRAP handler
// Needs to be distinct from the SignalHandlerReturnAddress
GuestSignal_SIGTRAP = getCurr<uint64_t>();
// ud2 = SIGILL
// int3 = SIGTRAP
// hlt = SIGSEGV
int3();
}
{
// Guest SIGSEGV handler
// Needs to be distinct from the SignalHandlerReturnAddress
GuestSignal_SIGSEGV = getCurr<uint64_t>();
// ud2 = SIGILL
// int3 = SIGTRAP
// hlt = SIGSEGV
hlt();
}
{
IntCallbackReturnAddress = getCurr<uint64_t>();
// using CallbackReturn = FEX_NAKED void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// rdi = thread
// rsi = rsp
mov(rsp, rsi);
// Now jump back to the thunk
// XXX: XMM?
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
ready();
Start = reinterpret_cast<uint64_t>(getCode());
End = Start + getSize();
if (CTX->Config.BlockJITNaming()) {
fextl::string Name = fextl::fmt::format("Dispatch_{}", FHU::Syscalls::gettid());
CTX->Symbols.Register(reinterpret_cast<void*>(Start), End-Start, Name);
}
if (CTX->Config.GlobalJITNaming()) {
CTX->Symbols.RegisterJITSpace(reinterpret_cast<void*>(Start), End-Start);
}
}
size_t X86Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) {
using namespace Xbyak;
using namespace Xbyak::util;
Xbyak::CodeGenerator emit(1, &emit); // actual emit target set with setNewBuffer
emit.setNewBuffer(CodeBuffer, MaxGDBPauseCheckSize);
Label RunBlock;
// 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(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
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::ContextImpl, Config.RunningMode))], 0);
emit.je(RunBlock);
{
// Make sure RIP is syncronized to the context
emit.mov(rax, GuestRIP);
emit.mov(qword STATE_PTR(CpuStateFrame, State.rip), rax);
// Stop the thread
emit.mov(rax, qword STATE_PTR(CpuStateFrame, Pointers.Common.ThreadPauseHandlerSpillSRA));
emit.jmp(rax);
}
emit.L(RunBlock);
emit.ready();
return emit.getSize();
}
size_t X86Dispatcher::GenerateInterpreterTrampoline(uint8_t *CodeBuffer) {
using namespace Xbyak;
using namespace Xbyak::util;
Xbyak::CodeGenerator emit(1, &emit); // actual emit target set with setNewBuffer
emit.setNewBuffer(CodeBuffer, MaxInterpreterTrampolineSize);
Label InlineIRData;
emit.mov(rdi, STATE);
emit.lea(rsi, ptr[rip + InlineIRData]);
emit.call(qword STATE_PTR(CpuStateFrame, Pointers.Interpreter.FragmentExecuter));
emit.jmp(qword STATE_PTR(CpuStateFrame, Pointers.Common.DispatcherLoopTop));
emit.L(InlineIRData);
emit.ready();
return emit.getSize();
}
X86Dispatcher::~X86Dispatcher() {
FEXCore::Allocator::VirtualFree(top_, MAX_DISPATCHER_CODE_SIZE);
}
void X86Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) {
// Setup dispatcher specific pointers that need to be accessed from JIT code
{
auto &Common = Thread->CurrentFrame->Pointers.Common;
Common.DispatcherLoopTop = AbsoluteLoopTopAddress;
Common.DispatcherLoopTopFillSRA = AbsoluteLoopTopAddressFillSRA;
Common.ExitFunctionLinker = ExitFunctionLinkerAddress;
Common.ThreadStopHandlerSpillSRA = ThreadStopHandlerAddress;
Common.ThreadPauseHandlerSpillSRA = ThreadPauseHandlerAddress;
Common.GuestSignal_SIGILL = GuestSignal_SIGILL;
Common.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
Common.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV;
Common.SignalReturnHandler = SignalHandlerReturnAddress;
Common.SignalReturnHandlerRT = SignalHandlerReturnAddressRT;
auto &Interpreter = Thread->CurrentFrame->Pointers.Interpreter;
(uintptr_t&)Interpreter.CallbackReturn = IntCallbackReturnAddress;
}
}
fextl::unique_ptr<Dispatcher> Dispatcher::CreateX86(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config) {
return fextl::make_unique<X86Dispatcher>(CTX, Config);
}
}
@@ -1,39 +0,0 @@
#pragma once
#include <FEXCore/fextl/list.h>
#include <FEXCore/fextl/unordered_map.h>
#include <FEXCore/fextl/unordered_set.h>
#include "Interface/Core/Dispatcher/Dispatcher.h"
#define XBYAK64
#define XBYAK_CUSTOM_ALLOC
#define XBYAK_CUSTOM_MALLOC FEXCore::Allocator::malloc
#define XBYAK_CUSTOM_FREE FEXCore::Allocator::free
#define XBYAK_CUSTOM_SETS
#define XBYAK_STD_UNORDERED_SET fextl::unordered_set
#define XBYAK_STD_UNORDERED_MAP fextl::unordered_map
#define XBYAK_STD_UNORDERED_MULTIMAP fextl::unordered_multimap
#define XBYAK_STD_LIST fextl::list
#define XBYAK_NO_EXCEPTION
#include <xbyak/xbyak.h>
#include <xbyak/xbyak_util.h>
namespace FEXCore::Core {
struct InternalThreadState;
}
namespace FEXCore::CPU {
class X86Dispatcher final : public Dispatcher, public Xbyak::CodeGenerator {
public:
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;
virtual ~X86Dispatcher() override;
};
}
-176
View File
@@ -1,176 +0,0 @@
#include "Interface/Core/CPUID.h"
#include <FEXCore/Core/HostFeatures.h>
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
#include "aarch64/assembler-aarch64.h"
#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
#include "aarch64/assembler-aarch64.h"
#endif
#ifdef _M_X86_64
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#endif
namespace FEXCore {
// Data Zero Prohibited flag
// 0b0 = ZVA/GVA/GZVA permitted
// 0b1 = ZVA/GVA/GZVA prohibited
[[maybe_unused]] constexpr uint32_t DCZID_DZP_MASK = 0b1'0000;
// Log2 of the blocksize in 32-bit words
[[maybe_unused]] constexpr uint32_t DCZID_BS_MASK = 0b0'1111;
#ifdef _M_ARM_64
[[maybe_unused]] static uint32_t GetDCZID() {
uint64_t Result{};
__asm("mrs %[Res], DCZID_EL0"
: [Res] "=r" (Result));
return Result;
}
static uint32_t GetFPCR() {
uint64_t Result{};
__asm ("mrs %[Res], FPCR"
: [Res] "=r" (Result));
return Result;
}
static void SetFPCR(uint64_t Value) {
__asm ("msr FPCR, %[Value]"
:: [Value] "r" (Value));
}
#else
static uint32_t GetDCZID() {
// Return unsupported
return DCZID_DZP_MASK;
}
#endif
HostFeatures::HostFeatures() {
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
#ifdef VIXL_SIMULATOR
auto Features = vixl::CPUFeatures::All();
#else
#ifndef _WIN32
auto Features = vixl::CPUFeatures::InferFromOS();
#else
// Need to use ID registers in WINE.
auto Features = vixl::CPUFeatures::InferFromIDRegisters();
#endif
#endif
SupportsAES = Features.Has(vixl::CPUFeatures::Feature::kAES);
SupportsCRC = Features.Has(vixl::CPUFeatures::Feature::kCRC32);
SupportsAtomics = Features.Has(vixl::CPUFeatures::Feature::kAtomics);
SupportsRAND = Features.Has(vixl::CPUFeatures::Feature::kRNG);
// Only supported when FEAT_AFP is supported
SupportsFlushInputsToZero = Features.Has(vixl::CPUFeatures::Feature::kAFP);
SupportsRCPC = Features.Has(vixl::CPUFeatures::Feature::kRCpc);
SupportsTSOImm9 = Features.Has(vixl::CPUFeatures::Feature::kRCpcImm);
SupportsPMULL_128Bit = Features.Has(vixl::CPUFeatures::Feature::kPmull1Q);
Supports3DNow = true;
SupportsSSE4A = true;
#ifdef VIXL_SIMULATOR
// Hardcode enable SVE with 256-bit wide registers.
SupportsAVX = true;
#else
SupportsAVX = Features.Has(vixl::CPUFeatures::Feature::kSVE2) &&
vixl::aarch64::CPU::ReadSVEVectorLengthInBits() >= 256;
#endif
SupportsSHA = true;
SupportsBMI1 = true;
SupportsBMI2 = true;
SupportsCLWB = true;
if (!SupportsAtomics) {
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
}
#ifdef _M_ARM_64
// We need to get the CPU's cache line size
// We expect sane targets that have correct cacheline sizes across clusters
uint64_t CTR;
__asm volatile ("mrs %[ctr], ctr_el0"
: [ctr] "=r"(CTR));
DCacheLineSize = 4 << ((CTR >> 16) & 0xF);
ICacheLineSize = 4 << (CTR & 0xF);
// Test if this CPU supports float exception trapping by attempting to enable
// On unsupported these bits are architecturally defined as RAZ/WI
constexpr uint32_t ExceptionEnableTraps =
(1U << 8) | // Invalid Operation float exception trap enable
(1U << 9) | // Divide by zero float exception trap enable
(1U << 10) | // Overflow float exception trap enable
(1U << 11) | // Underflow float exception trap enable
(1U << 12) | // Inexact float exception trap enable
(1U << 15); // Input Denormal float exception trap enable
uint32_t OriginalFPCR = GetFPCR();
uint32_t FPCR = OriginalFPCR | ExceptionEnableTraps;
SetFPCR(FPCR);
FPCR = GetFPCR();
SupportsFloatExceptions = (FPCR & ExceptionEnableTraps) == ExceptionEnableTraps;
// Set FPCR back to original just in case anything changed
SetFPCR(OriginalFPCR);
#endif
#endif
#if defined(_M_X86_64) && !defined(VIXL_SIMULATOR)
Xbyak::util::Cpu Features{};
SupportsAES = Features.has(Xbyak::util::Cpu::tAESNI);
SupportsCRC = Features.has(Xbyak::util::Cpu::tSSE42);
SupportsRAND = Features.has(Xbyak::util::Cpu::tRDRAND) && Features.has(Xbyak::util::Cpu::tRDSEED);
SupportsRCPC = true;
SupportsTSOImm9 = true;
Supports3DNow = Features.has(Xbyak::util::Cpu::t3DN) && Features.has(Xbyak::util::Cpu::tE3DN);
SupportsSSE4A = Features.has(Xbyak::util::Cpu::tSSE4a);
SupportsAVX = true;
SupportsSHA = Features.has(Xbyak::util::Cpu::tSHA);
SupportsBMI1 = Features.has(Xbyak::util::Cpu::tBMI1);
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tBMI2);
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tCLWB);
SupportsPMULL_128Bit = Features.has(Xbyak::util::Cpu::tPCLMULQDQ);
// xbyak doesn't know how to check for CLZero
// First ensure we support a new enough extended CPUID function range
uint32_t data[4];
Xbyak::util::Cpu::getCpuid(0x8000'0000, data);
if (data[0] >= 0x8000'0008U) {
// CLZero defined in 8000_00008_EBX[bit 0]
Xbyak::util::Cpu::getCpuid(0x8000'0008, data);
SupportsCLZERO = data[1] & 1;
}
SupportsFlushInputsToZero = true;
SupportsFloatExceptions = true;
#endif
#ifdef VIXL_SIMULATOR
// simulator doesn't support dc(ZVA)
SupportsCLZERO = false;
#else
// Check if we can support cacheline clears
uint32_t DCZID = GetDCZID();
if ((DCZID & DCZID_DZP_MASK) == 0) {
uint32_t DCZID_Log2 = DCZID & DCZID_BS_MASK;
uint32_t DCZID_Bytes = (1 << DCZID_Log2) * sizeof(uint32_t);
// If the DC ZVA size matches the emulated cache line size
// This means we can use the instruction
SupportsCLZERO = DCZID_Bytes == CPUIDEmu::CACHELINE_SIZE;
}
#endif
// Disable AVX if the configuration explicitly has disabled it.
FEX_CONFIG_OPT(EnableAVX, ENABLEAVX);
if (!EnableAVX) {
SupportsAVX = false;
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,777 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include <FEXCore/Utils/BitUtils.h>
#include <cstdint>
namespace FEXCore::CPU {
#ifdef _M_X86_64
uint8_t AtomicFetchNeg(uint8_t *Addr) {
using Type = uint8_t;
std::atomic<Type> *MemData = reinterpret_cast<std::atomic<Type>*>(Addr);
Type Expected = MemData->load();
Type Desired = -Expected;
do {
Desired = -Expected;
} while (!MemData->compare_exchange_strong(Expected, Desired, std::memory_order_seq_cst));
return Expected;
}
uint16_t AtomicFetchNeg(uint16_t *Addr) {
using Type = uint16_t;
std::atomic<Type> *MemData = reinterpret_cast<std::atomic<Type>*>(Addr);
Type Expected = MemData->load();
Type Desired = -Expected;
do {
Desired = -Expected;
} while (!MemData->compare_exchange_strong(Expected, Desired, std::memory_order_seq_cst));
return Expected;
}
uint32_t AtomicFetchNeg(uint32_t *Addr) {
using Type = uint32_t;
std::atomic<Type> *MemData = reinterpret_cast<std::atomic<Type>*>(Addr);
Type Expected = MemData->load();
Type Desired = -Expected;
do {
Desired = -Expected;
} while (!MemData->compare_exchange_strong(Expected, Desired, std::memory_order_seq_cst));
return Expected;
}
uint64_t AtomicFetchNeg(uint64_t *Addr) {
using Type = uint64_t;
std::atomic<Type> *MemData = reinterpret_cast<std::atomic<Type>*>(Addr);
Type Expected = MemData->load();
Type Desired = -Expected;
do {
Desired = -Expected;
} while (!MemData->compare_exchange_strong(Expected, Desired, std::memory_order_seq_cst));
return Expected;
}
template<typename T>
T AtomicCompareAndSwap(T expected, T desired, T *addr)
{
std::atomic<T> *MemData = reinterpret_cast<std::atomic<T>*>(addr);
T Src1 = expected;
T Src2 = desired;
T Expected = Src1;
bool Result = MemData->compare_exchange_strong(Expected, Src2);
return Result ? Src1 : Expected;
}
template uint8_t AtomicCompareAndSwap<uint8_t>(uint8_t expected, uint8_t desired, uint8_t *addr);
template uint16_t AtomicCompareAndSwap<uint16_t>(uint16_t expected, uint16_t desired, uint16_t *addr);
template uint32_t AtomicCompareAndSwap<uint32_t>(uint32_t expected, uint32_t desired, uint32_t *addr);
template uint64_t AtomicCompareAndSwap<uint64_t>(uint64_t expected, uint64_t desired, uint64_t *addr);
#else
// Needs to match what the AArch64 JIT and unaligned signal handler expects
uint8_t AtomicFetchNeg(uint8_t *Addr) {
using Type = uint8_t;
Type Result{};
Type Tmp{};
Type TmpStatus{};
__asm__ volatile(
R"(
1:
ldaxrb %w[Result], [%[Memory]];
neg %w[Tmp], %w[Result];
stlxrb %w[TmpStatus], %w[Tmp], [%[Memory]];
cbnz %w[TmpStatus], 1b;
)"
: [Result] "=r" (Result)
, [Tmp] "=r" (Tmp)
, [TmpStatus] "=r" (TmpStatus)
, [Memory] "+r" (Addr)
:: "memory"
);
return Result;
}
uint16_t AtomicFetchNeg(uint16_t *Addr) {
using Type = uint16_t;
Type Result{};
Type Tmp{};
Type TmpStatus{};
__asm__ volatile(
R"(
1:
ldaxrh %w[Result], [%[Memory]];
neg %w[Tmp], %w[Result];
stlxrh %w[TmpStatus], %w[Tmp], [%[Memory]];
cbnz %w[TmpStatus], 1b;
)"
: [Result] "=r" (Result)
, [Tmp] "=r" (Tmp)
, [TmpStatus] "=r" (TmpStatus)
, [Memory] "+r" (Addr)
:: "memory"
);
return Result;
}
uint32_t AtomicFetchNeg(uint32_t *Addr) {
using Type = uint32_t;
Type Result{};
Type Tmp{};
Type TmpStatus{};
__asm__ volatile(
R"(
1:
ldaxr %w[Result], [%[Memory]];
neg %w[Tmp], %w[Result];
stlxr %w[TmpStatus], %w[Tmp], [%[Memory]];
cbnz %w[TmpStatus], 1b;
)"
: [Result] "=r" (Result)
, [Tmp] "=r" (Tmp)
, [TmpStatus] "=r" (TmpStatus)
, [Memory] "+r" (Addr)
:: "memory"
);
return Result;
}
uint64_t AtomicFetchNeg(uint64_t *Addr) {
using Type = uint64_t;
Type Result{};
Type Tmp{};
Type TmpStatus{};
__asm__ volatile(
R"(
1:
ldaxr %[Result], [%[Memory]];
neg %[Tmp], %[Result];
stlxr %w[TmpStatus], %[Tmp], [%[Memory]];
cbnz %w[TmpStatus], 1b;
)"
: [Result] "=r" (Result)
, [Tmp] "=r" (Tmp)
, [TmpStatus] "=r" (TmpStatus)
, [Memory] "+r" (Addr)
:: "memory"
);
return Result;
}
template<>
uint8_t AtomicCompareAndSwap(uint8_t expected, uint8_t desired, uint8_t *addr) {
using Type = uint8_t;
//force Result to r9 (scratch register) or clang spills to stack
register Type Result asm("r9"){};
Type Tmp{};
Type Tmp2{};
__asm__ volatile(
R"(
1:
ldaxrb %w[Tmp], [%[Memory]];
cmp %w[Tmp], %w[Expected], uxtb;
b.ne 2f;
stlxrb %w[Tmp2], %w[Desired], [%[Memory]];
cbnz %w[Tmp2], 1b;
mov %w[Result], %w[Expected];
b 3f;
2:
mov %w[Result], %w[Tmp];
clrex;
3:
)"
: [Tmp] "=r" (Tmp)
, [Tmp2] "=r" (Tmp2)
, [Desired] "+r" (desired)
, [Expected] "+r" (expected)
, [Result] "=r" (Result)
, [Memory] "+r" (addr)
:: "memory"
);
return Result;
}
template<>
uint16_t AtomicCompareAndSwap(uint16_t expected, uint16_t desired, uint16_t *addr) {
using Type = uint16_t;
//force Result to r9 (scratch register) or clang spills to stack
register Type Result asm("r9"){};
Type Tmp{};
Type Tmp2{};
__asm__ volatile(
R"(
1:
ldaxrh %w[Tmp], [%[Memory]];
cmp %w[Tmp], %w[Expected], uxth;
b.ne 2f;
stlxrh %w[Tmp2], %w[Desired], [%[Memory]];
cbnz %w[Tmp2], 1b;
mov %w[Result], %w[Expected];
b 3f;
2:
mov %w[Result], %w[Tmp];
clrex;
3:
)"
: [Tmp] "=r" (Tmp)
, [Tmp2] "=r" (Tmp2)
, [Desired] "+r" (desired)
, [Expected] "+r" (expected)
, [Result] "=r" (Result)
, [Memory] "+r" (addr)
:: "memory"
);
return Result;
}
template<>
uint32_t AtomicCompareAndSwap(uint32_t expected, uint32_t desired, uint32_t *addr) {
using Type = uint32_t;
//force Result to r9 (scratch register) or clang spills to stack
register Type Result asm("r9"){};
Type Tmp{};
Type Tmp2{};
__asm__ volatile(
R"(
1:
ldaxr %w[Tmp], [%[Memory]];
cmp %w[Tmp], %w[Expected];
b.ne 2f;
stlxr %w[Tmp2], %w[Desired], [%[Memory]];
cbnz %w[Tmp2], 1b;
mov %w[Result], %w[Expected];
b 3f;
2:
mov %w[Result], %w[Tmp];
clrex;
3:
)"
: [Tmp] "=r" (Tmp)
, [Tmp2] "=r" (Tmp2)
, [Desired] "+r" (desired)
, [Expected] "+r" (expected)
, [Result] "=r" (Result)
, [Memory] "+r" (addr)
:: "memory"
);
return Result;
}
template<>
uint64_t AtomicCompareAndSwap(uint64_t expected, uint64_t desired, uint64_t *addr) {
using Type = uint64_t;
//force Result to r9 (scratch register) or clang spills to stack
register Type Result asm("r9"){};
Type Tmp{};
Type Tmp2{};
__asm__ volatile(
R"(
1:
ldaxr %[Tmp], [%[Memory]];
cmp %[Tmp], %[Expected];
b.ne 2f;
stlxr %w[Tmp2], %[Desired], [%[Memory]];
cbnz %w[Tmp2], 1b;
mov %[Result], %[Expected];
b 3f;
2:
mov %[Result], %[Tmp];
clrex;
3:
)"
: [Tmp] "=r" (Tmp)
, [Tmp2] "=r" (Tmp2)
, [Desired] "+r" (desired)
, [Expected] "+r" (expected)
, [Result] "=r" (Result)
, [Memory] "+r" (addr)
:: "memory"
);
return Result;
}
#endif
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(CASPair) {
auto Op = IROp->C<IR::IROp_CASPair>();
// Size is the size of each pair element
switch (IROp->ElementSize) {
case 4: {
GD = AtomicCompareAndSwap(
*GetSrc<uint64_t*>(Data->SSAData, Op->Expected),
*GetSrc<uint64_t*>(Data->SSAData, Op->Desired),
*GetSrc<uint64_t**>(Data->SSAData, Op->Addr)
);
break;
}
case 8: {
std::atomic<__uint128_t> *MemData = *GetSrc<std::atomic<__uint128_t> **>(Data->SSAData, Op->Addr);
__uint128_t Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Expected);
__uint128_t Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Desired);
__uint128_t Expected = Src1;
bool Result = MemData->compare_exchange_strong(Expected, Src2);
memcpy(GDP, Result ? &Src1 : &Expected, 16);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown CAS size: {}", IROp->ElementSize); break;
}
}
DEF_OP(CAS) {
auto Op = IROp->C<IR::IROp_CAS>();
uint8_t OpSize = IROp->Size;
switch (OpSize) {
case 1: {
GD = AtomicCompareAndSwap(
*GetSrc<uint8_t*>(Data->SSAData, Op->Expected),
*GetSrc<uint8_t*>(Data->SSAData, Op->Desired),
*GetSrc<uint8_t**>(Data->SSAData, Op->Addr)
);
break;
}
case 2: {
GD = AtomicCompareAndSwap(
*GetSrc<uint16_t*>(Data->SSAData, Op->Expected),
*GetSrc<uint16_t*>(Data->SSAData, Op->Desired),
*GetSrc<uint16_t**>(Data->SSAData, Op->Addr)
);
break;
}
case 4: {
GD = AtomicCompareAndSwap(
*GetSrc<uint32_t*>(Data->SSAData, Op->Expected),
*GetSrc<uint32_t*>(Data->SSAData, Op->Desired),
*GetSrc<uint32_t**>(Data->SSAData, Op->Addr)
);
break;
}
case 8: {
GD = AtomicCompareAndSwap(
*GetSrc<uint64_t*>(Data->SSAData, Op->Expected),
*GetSrc<uint64_t*>(Data->SSAData, Op->Desired),
*GetSrc<uint64_t**>(Data->SSAData, Op->Addr)
);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown CAS size: {}", OpSize); break;
}
}
DEF_OP(AtomicAdd) {
auto Op = IROp->C<IR::IROp_AtomicAdd>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
*MemData += Src;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
*MemData += Src;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
*MemData += Src;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
*MemData += Src;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicSub) {
auto Op = IROp->C<IR::IROp_AtomicSub>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
*MemData -= Src;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
*MemData -= Src;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
*MemData -= Src;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
*MemData -= Src;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicAnd) {
auto Op = IROp->C<IR::IROp_AtomicAnd>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
*MemData &= Src;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
*MemData &= Src;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
*MemData &= Src;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
*MemData &= Src;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicOr) {
auto Op = IROp->C<IR::IROp_AtomicOr>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
*MemData |= Src;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
*MemData |= Src;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
*MemData |= Src;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
*MemData |= Src;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicXor) {
auto Op = IROp->C<IR::IROp_AtomicXor>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
*MemData ^= Src;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
*MemData ^= Src;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
*MemData ^= Src;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
*MemData ^= Src;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicSwap) {
auto Op = IROp->C<IR::IROp_AtomicSwap>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
uint8_t Previous = MemData->exchange(Src);
GD = Previous;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
uint16_t Previous = MemData->exchange(Src);
GD = Previous;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
uint32_t Previous = MemData->exchange(Src);
GD = Previous;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
uint64_t Previous = MemData->exchange(Src);
GD = Previous;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchAdd) {
auto Op = IROp->C<IR::IROp_AtomicFetchAdd>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
uint8_t Previous = MemData->fetch_add(Src);
GD = Previous;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
uint16_t Previous = MemData->fetch_add(Src);
GD = Previous;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
uint32_t Previous = MemData->fetch_add(Src);
GD = Previous;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
uint64_t Previous = MemData->fetch_add(Src);
GD = Previous;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchSub) {
auto Op = IROp->C<IR::IROp_AtomicFetchSub>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
uint8_t Previous = MemData->fetch_sub(Src);
GD = Previous;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
uint16_t Previous = MemData->fetch_sub(Src);
GD = Previous;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
uint32_t Previous = MemData->fetch_sub(Src);
GD = Previous;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
uint64_t Previous = MemData->fetch_sub(Src);
GD = Previous;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchAnd) {
auto Op = IROp->C<IR::IROp_AtomicFetchAnd>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
uint8_t Previous = MemData->fetch_and(Src);
GD = Previous;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
uint16_t Previous = MemData->fetch_and(Src);
GD = Previous;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
uint32_t Previous = MemData->fetch_and(Src);
GD = Previous;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
uint64_t Previous = MemData->fetch_and(Src);
GD = Previous;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchOr) {
auto Op = IROp->C<IR::IROp_AtomicFetchOr>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
uint8_t Previous = MemData->fetch_or(Src);
GD = Previous;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
uint16_t Previous = MemData->fetch_or(Src);
GD = Previous;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
uint32_t Previous = MemData->fetch_or(Src);
GD = Previous;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
uint64_t Previous = MemData->fetch_or(Src);
GD = Previous;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchXor) {
auto Op = IROp->C<IR::IROp_AtomicFetchXor>();
switch (IROp->Size) {
case 1: {
std::atomic<uint8_t> *MemData = *GetSrc<std::atomic<uint8_t> **>(Data->SSAData, Op->Addr);
uint8_t Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
uint8_t Previous = MemData->fetch_xor(Src);
GD = Previous;
break;
}
case 2: {
std::atomic<uint16_t> *MemData = *GetSrc<std::atomic<uint16_t> **>(Data->SSAData, Op->Addr);
uint16_t Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Value);
uint16_t Previous = MemData->fetch_xor(Src);
GD = Previous;
break;
}
case 4: {
std::atomic<uint32_t> *MemData = *GetSrc<std::atomic<uint32_t> **>(Data->SSAData, Op->Addr);
uint32_t Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Value);
uint32_t Previous = MemData->fetch_xor(Src);
GD = Previous;
break;
}
case 8: {
std::atomic<uint64_t> *MemData = *GetSrc<std::atomic<uint64_t> **>(Data->SSAData, Op->Addr);
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
uint64_t Previous = MemData->fetch_xor(Src);
GD = Previous;
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchNeg) {
auto Op = IROp->C<IR::IROp_AtomicFetchNeg>();
switch (IROp->Size) {
case 1: {
using Type = uint8_t;
GD = AtomicFetchNeg(*GetSrc<Type**>(Data->SSAData, Op->Addr));
break;
}
case 2: {
using Type = uint16_t;
GD = AtomicFetchNeg(*GetSrc<Type**>(Data->SSAData, Op->Addr));
break;
}
case 4: {
using Type = uint32_t;
GD = AtomicFetchNeg(*GetSrc<Type**>(Data->SSAData, Op->Addr));
break;
}
case 8: {
using Type = uint64_t;
GD = AtomicFetchNeg(*GetSrc<Type**>(Data->SSAData, Op->Addr));
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled Atomic size: {}", IROp->Size);
}
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,157 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include "Interface/HLE/Thunks/Thunks.h"
#include <FEXCore/Utils/BitUtils.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <cstdint>
#include <unistd.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(CallbackReturn) {
Data->State->CurrentFrame->Pointers.Interpreter.CallbackReturn(Data->State, Data->StackEntry);
}
DEF_OP(ExitFunction) {
auto Op = IROp->C<IR::IROp_ExitFunction>();
uint8_t OpSize = IROp->Size;
uintptr_t* ContextPtr = reinterpret_cast<uintptr_t*>(Data->State->CurrentFrame);
void *ContextData = reinterpret_cast<void*>(ContextPtr);
void *Src = GetSrc<void*>(Data->SSAData, Op->NewRIP);
memcpy(ContextData, Src, OpSize);
Data->BlockResults.Quit = true;
}
DEF_OP(Jump) {
auto Op = IROp->C<IR::IROp_Jump>();
const uintptr_t ListBegin = Data->CurrentIR->GetListData();
const uintptr_t DataBegin = Data->CurrentIR->GetData();
Data->BlockIterator = IR::NodeIterator(ListBegin, DataBegin, Op->TargetBlock);
Data->BlockResults.Redo = true;
}
DEF_OP(CondJump) {
auto Op = IROp->C<IR::IROp_CondJump>();
const uintptr_t ListBegin = Data->CurrentIR->GetListData();
const uintptr_t DataBegin = Data->CurrentIR->GetData();
bool CompResult;
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp1);
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp2);
if (Op->CompareSize == 4)
CompResult = IsConditionTrue<uint32_t, int32_t, float>(Op->Cond.Val, Src1, Src2);
else
CompResult = IsConditionTrue<uint64_t, int64_t, double>(Op->Cond.Val, Src1, Src2);
if (CompResult) {
Data->BlockIterator = IR::NodeIterator(ListBegin, DataBegin, Op->TrueBlock);
}
else {
Data->BlockIterator = IR::NodeIterator(ListBegin, DataBegin, Op->FalseBlock);
}
Data->BlockResults.Redo = true;
}
DEF_OP(Syscall) {
auto Op = IROp->C<IR::IROp_Syscall>();
FEXCore::HLE::SyscallArguments Args;
for (size_t j = 0; j < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++j) {
if (Op->Header.Args[j].IsInvalid()) break;
Args.Argument[j] = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[j]);
}
uint64_t Res = FEXCore::Context::HandleSyscall(static_cast<Context::ContextImpl*>(Data->State->CTX)->SyscallHandler, Data->State->CurrentFrame, &Args);
GD = Res;
}
DEF_OP(InlineSyscall) {
auto Op = IROp->C<IR::IROp_InlineSyscall>();
FEXCore::HLE::SyscallArguments Args;
for (size_t j = 0; j < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++j) {
if (Op->Header.Args[j].IsInvalid()) break;
Args.Argument[j] = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[j]);
}
// We don't want the errno handling but I also don't want to write inline ASM atm
uint64_t Res = syscall(
Op->HostSyscallNumber,
Args.Argument[0],
Args.Argument[1],
Args.Argument[2],
Args.Argument[3],
Args.Argument[4],
Args.Argument[5],
Args.Argument[6]
);
if (Res == -1) {
Res = -errno;
}
GD = Res;
}
DEF_OP(Thunk) {
auto Op = IROp->C<IR::IROp_Thunk>();
auto thunkFn = static_cast<Context::ContextImpl*>(Data->State->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
thunkFn(*GetSrc<void**>(Data->SSAData, Op->ArgPtr));
}
DEF_OP(ValidateCode) {
auto Op = IROp->C<IR::IROp_ValidateCode>();
auto CodePtr = Data->CurrentEntry + Op->Offset;
if (memcmp((void*)CodePtr, &Op->CodeOriginalLow, Op->CodeLength) != 0) {
GD = 1;
} else {
GD = 0;
}
}
DEF_OP(ThreadRemoveCodeEntry) {
static_cast<Context::ContextImpl*>(Data->State->CTX)->ThreadRemoveCodeEntryFromJit(Data->State->CurrentFrame, Data->CurrentEntry);
}
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
uint64_t *DstPtr = GetDest<uint64_t*>(Data->SSAData, Node);
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->RunCPUIDFunction(Arg, Leaf);
memcpy(DstPtr, &Results, sizeof(uint32_t) * 4);
}
DEF_OP(XGETBV) {
auto Op = IROp->C<IR::IROp_XGetBV>();
uint32_t *DstPtr = GetDest<uint32_t*>(Data->SSAData, Node);
const uint32_t Function = *GetSrc<uint32_t*>(Data->SSAData, Op->Function);
auto Results = Data->State->CTX->RunXCRFunction(Function);
memcpy(DstPtr, &Results, sizeof(uint32_t) * 2);
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,278 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include <cstdint>
namespace FEXCore::CPU {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(VInsGPR) {
const auto Op = IROp->C<IR::IROp_VInsGPR>();
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto ElementSizeBits = ElementSize * 8;
constexpr auto SSEBitSize = Core::CPUState::XMM_SSE_REG_SIZE * 8;
const uint64_t Offset = Op->DestIdx * ElementSizeBits;
const auto InUpperLane = Offset >= SSEBitSize;
__uint128_t Mask = (1ULL << ElementSizeBits) - 1;
if (ElementSize == 8) {
Mask = ~0ULL;
}
const auto Src1 = *GetSrc<InterpVector256*>(Data->SSAData, Op->DestVector);
const auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Src);
const auto Scalar = Src2 & Mask;
const auto ScaledOffset = InUpperLane ? Offset - SSEBitSize
: Offset;
// Now shift into place and set all bits but
// the ones where we're going to insert our value.
Mask <<= ScaledOffset;
Mask = ~Mask;
const auto Dst = [&] {
if (InUpperLane) {
return InterpVector256{
.Lower = Src1.Lower,
.Upper = (Src1.Upper & Mask) | (Scalar << ScaledOffset),
};
} else {
return InterpVector256{
.Lower = (Src1.Lower & Mask) | (Scalar << ScaledOffset),
.Upper = Src1.Upper,
};
}
}();
memcpy(GDP, &Dst, OpSize);
}
DEF_OP(VCastFromGPR) {
auto Op = IROp->C<IR::IROp_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>();
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0404: { // Float <- int32_t
const float Dst = (float)*GetSrc<int32_t*>(Data->SSAData, Op->Src);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
case 0x0408: { // Float <- int64_t
const float Dst = (float)*GetSrc<int64_t*>(Data->SSAData, Op->Src);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
case 0x0804: { // Double <- int32_t
const double Dst = (double)*GetSrc<int32_t*>(Data->SSAData, Op->Src);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
case 0x0808: { // Double <- int64_t
const double Dst = (double)*GetSrc<int64_t*>(Data->SSAData, Op->Src);
memcpy(GDP, &Dst, Op->Header.ElementSize);
break;
}
}
}
DEF_OP(Float_FToF) {
auto Op = IROp->C<IR::IROp_Float_FToF>();
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0804: { // Double <- Float
const double Dst = (double)*GetSrc<float*>(Data->SSAData, Op->Scalar);
memcpy(GDP, &Dst, 8);
break;
}
case 0x0408: { // Float <- Double
const float Dst = (float)*GetSrc<double*>(Data->SSAData, Op->Scalar);
memcpy(GDP, &Dst, 4);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown FCVT sizes: 0x{:x}", Conv);
}
}
DEF_OP(Vector_SToF) {
auto Op = IROp->C<IR::IROp_Vector_SToF>();
const uint8_t OpSize = IROp->Size;
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
const uint8_t ElementSize = Op->Header.ElementSize;
const uint8_t Elements = OpSize / ElementSize;
const auto Func = [](auto a, auto min, auto max) { return a; };
switch (ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, float, int32_t, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, double, int64_t, Func, 0, 0)
default:
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
break;
}
memcpy(GDP, Tmp, OpSize);
}
DEF_OP(Vector_FToZS) {
const auto Op = IROp->C<IR::IROp_Vector_FToZS>();
const uint8_t OpSize = IROp->Size;
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
const uint8_t ElementSize = Op->Header.ElementSize;
const uint8_t Elements = OpSize / ElementSize;
const auto Func = [](auto a, auto min, auto max) { return std::trunc(a); };
switch (ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
default:
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
break;
}
memcpy(GDP, Tmp, OpSize);
}
DEF_OP(Vector_FToS) {
const auto Op = IROp->C<IR::IROp_Vector_FToS>();
const uint8_t OpSize = IROp->Size;
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
const uint8_t ElementSize = Op->Header.ElementSize;
const uint8_t Elements = OpSize / ElementSize;
const auto Func = [](auto a, auto min, auto max) { return std::nearbyint(a); };
switch (ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
default:
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
break;
}
memcpy(GDP, Tmp, OpSize);
}
DEF_OP(Vector_FToF) {
const auto Op = IROp->C<IR::IROp_Vector_FToF>();
const uint8_t OpSize = IROp->Size;
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
const uint16_t ElementSize = Op->Header.ElementSize;
const uint16_t Conv = (ElementSize << 8) | Op->SrcElementSize;
const auto Func = [](auto a, auto min, auto max) { return a; };
switch (Conv) {
case 0x0804: { // Double <- float
// Only the lower elements from the source
// This uses half the source elements
uint8_t Elements = OpSize / 8;
DO_VECTOR_1SRC_2TYPE_OP_NOSIZE(double, float, Func, 0, 0)
break;
}
case 0x0408: { // Float <- Double
// Little bit tricky here
// Sometimes is used to convert from a 128bit vector register
// in to a 64bit vector register with different sized elements
// eg: %ssa5 i32v2 = Vector_FToF %ssa4 i128, #0x8
uint8_t Elements = OpSize == 8 ? 2 : OpSize / Op->SrcElementSize;
DO_VECTOR_1SRC_2TYPE_OP_NOSIZE(float, double, Func, 0, 0)
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Conversion Type : 0x{:04x}", Conv);
break;
}
memcpy(GDP, Tmp, OpSize);
}
DEF_OP(Vector_FToI) {
const auto Op = IROp->C<IR::IROp_Vector_FToI>();
const uint8_t OpSize = IROp->Size;
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
const uint8_t ElementSize = Op->Header.ElementSize;
const uint8_t Elements = OpSize / ElementSize;
const auto Func_Nearest = [](auto a) { return std::rint(a); };
const auto Func_Neg = [](auto a) { return std::floor(a); };
const auto Func_Pos = [](auto a) { return std::ceil(a); };
const auto Func_Trunc = [](auto a) { return std::trunc(a); };
const auto Func_Host = [](auto a) { return std::rint(a); };
switch (Op->Round) {
case FEXCore::IR::Round_Nearest.Val:
switch (ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Nearest)
DO_VECTOR_1SRC_OP(8, double, Func_Nearest)
}
break;
case FEXCore::IR::Round_Negative_Infinity.Val:
switch (ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Neg)
DO_VECTOR_1SRC_OP(8, double, Func_Neg)
}
break;
case FEXCore::IR::Round_Positive_Infinity.Val:
switch (ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Pos)
DO_VECTOR_1SRC_OP(8, double, Func_Pos)
}
break;
case FEXCore::IR::Round_Towards_Zero.Val:
switch (ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Trunc)
DO_VECTOR_1SRC_OP(8, double, Func_Trunc)
}
break;
case FEXCore::IR::Round_Host.Val:
switch (ElementSize) {
DO_VECTOR_1SRC_OP(4, float, Func_Host)
DO_VECTOR_1SRC_OP(8, double, Func_Host)
}
break;
}
memcpy(GDP, Tmp, OpSize);
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,556 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include <cstdint>
namespace AES {
static __uint128_t InvShiftRows(uint8_t *State) {
uint8_t Shifted[16] = {
State[0], State[13], State[10], State[7],
State[4], State[1], State[14], State[11],
State[8], State[5], State[2], State[15],
State[12], State[9], State[6], State[3],
};
__uint128_t Res{};
memcpy(&Res, Shifted, 16);
return Res;
}
static __uint128_t InvSubBytes(uint8_t *State) {
// 16x16 matrix table
static const uint8_t InvSubstitutionTable[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
};
// Uses a byte substitution table with a constant set of values
// Needs to do a table look up
uint8_t Substituted[16];
for (size_t i = 0; i < 16; ++i) {
Substituted[i] = InvSubstitutionTable[State[i]];
}
__uint128_t Res{};
memcpy(&Res, Substituted, 16);
return Res;
}
static __uint128_t ShiftRows(uint8_t *State) {
uint8_t Shifted[16] = {
State[0], State[5], State[10], State[15],
State[4], State[9], State[14], State[3],
State[8], State[13], State[2], State[7],
State[12], State[1], State[6], State[11],
};
__uint128_t Res{};
memcpy(&Res, Shifted, 16);
return Res;
}
static __uint128_t SubBytes(uint8_t *State, size_t Bytes) {
// 16x16 matrix table
static const uint8_t SubstitutionTable[256] = {
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
};
// Uses a byte substitution table with a constant set of values
// Needs to do a table look up
uint8_t Substituted[16];
Bytes = std::min(Bytes, (size_t)16);
for (size_t i = 0; i < Bytes; ++i) {
Substituted[i] = SubstitutionTable[State[i]];
}
__uint128_t Res{};
memcpy(&Res, Substituted, Bytes);
return Res;
}
static uint8_t FFMul02(uint8_t in) {
static const uint8_t FFMul02[256] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5,
};
return FFMul02[in];
}
static uint8_t FFMul03(uint8_t in) {
static const uint8_t FFMul03[256] = {
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a,
};
return FFMul03[in];
}
static __uint128_t MixColumns(uint8_t *State) {
uint8_t In0[16] = {
State[0], State[4], State[8], State[12],
State[1], State[5], State[9], State[13],
State[2], State[6], State[10], State[14],
State[3], State[7], State[11], State[15],
};
uint8_t Out0[4]{};
uint8_t Out1[4]{};
uint8_t Out2[4]{};
uint8_t Out3[4]{};
for (size_t i = 0; i < 4; ++i) {
Out0[i] = FFMul02(In0[0 + i]) ^ FFMul03(In0[4 + i]) ^ In0[8 + i] ^ In0[12 + i];
Out1[i] = In0[0 + i] ^ FFMul02(In0[4 + i]) ^ FFMul03(In0[8 + i]) ^ In0[12 + i];
Out2[i] = In0[0 + i] ^ In0[4 + i] ^ FFMul02(In0[8 + i]) ^ FFMul03(In0[12 + i]);
Out3[i] = FFMul03(In0[0 + i]) ^ In0[4 + i] ^ In0[8 + i] ^ FFMul02(In0[12 + i]);
}
uint8_t OutArray[16] = {
Out0[0], Out1[0], Out2[0], Out3[0],
Out0[1], Out1[1], Out2[1], Out3[1],
Out0[2], Out1[2], Out2[2], Out3[2],
Out0[3], Out1[3], Out2[3], Out3[3],
};
__uint128_t Res{};
memcpy(&Res, OutArray, 16);
return Res;
}
static uint8_t FFMul09(uint8_t in) {
static const uint8_t FFMul09[256] = {
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46,
};
return FFMul09[in];
}
static uint8_t FFMul0B(uint8_t in) {
static const uint8_t FFMul0B[256] = {
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3,
};
return FFMul0B[in];
}
static uint8_t FFMul0D(uint8_t in) {
static const uint8_t FFMul0D[256] = {
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97,
};
return FFMul0D[in];
}
static uint8_t FFMul0E(uint8_t in) {
static const uint8_t FFMul0E[256] = {
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d,
};
return FFMul0E[in];
}
static __uint128_t InvMixColumns(uint8_t *State) {
uint8_t In0[16] = {
State[0], State[4], State[8], State[12],
State[1], State[5], State[9], State[13],
State[2], State[6], State[10], State[14],
State[3], State[7], State[11], State[15],
};
uint8_t Out0[4]{};
uint8_t Out1[4]{};
uint8_t Out2[4]{};
uint8_t Out3[4]{};
for (size_t i = 0; i < 4; ++i) {
Out0[i] = FFMul0E(In0[0 + i]) ^ FFMul0B(In0[4 + i]) ^ FFMul0D(In0[8 + i]) ^ FFMul09(In0[12 + i]);
Out1[i] = FFMul09(In0[0 + i]) ^ FFMul0E(In0[4 + i]) ^ FFMul0B(In0[8 + i]) ^ FFMul0D(In0[12 + i]);
Out2[i] = FFMul0D(In0[0 + i]) ^ FFMul09(In0[4 + i]) ^ FFMul0E(In0[8 + i]) ^ FFMul0B(In0[12 + i]);
Out3[i] = FFMul0B(In0[0 + i]) ^ FFMul0D(In0[4 + i]) ^ FFMul09(In0[8 + i]) ^ FFMul0E(In0[12 + i]);
}
uint8_t OutArray[16] = {
Out0[0], Out1[0], Out2[0], Out3[0],
Out0[1], Out1[1], Out2[1], Out3[1],
Out0[2], Out1[2], Out2[2], Out3[2],
Out0[3], Out1[3], Out2[3], Out3[3],
};
__uint128_t Res{};
memcpy(&Res, OutArray, 16);
return Res;
}
}
namespace CRC32 {
// CRC32 per byte lookup table.
constexpr std::array<uint32_t, 256> CRC32CTable = []() consteval {
std::array<uint32_t, 256> Table{};
// Clang 11.x doesn't support bitreverse as a consteval
// constexpr uint32_t Polynomial = 0x1EDC6F41;
constexpr uint32_t PolynomialRev = 0x82F63B78; //__builtin_bitreverse32(Polynomial);
for (size_t Char = 0; Char < std::size(Table); ++Char) {
uint32_t CurrentChar = Char;
for (size_t i = 0; i < 8; ++i) {
if (CurrentChar & 1) {
CurrentChar = (CurrentChar >> 1) ^ PolynomialRev;
}
else {
CurrentChar >>= 1;
}
}
Table[Char] = CurrentChar;
}
return Table;
}();
uint32_t crc32cb(uint32_t Accumulator, uint8_t data) {
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ data] ^ Accumulator >> 8;
return Accumulator;
}
uint32_t crc32ch(uint32_t Accumulator, uint16_t data) {
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 0) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 8) & 0xFF)] ^ Accumulator >> 8;
return Accumulator;
}
uint32_t crc32cw(uint32_t Accumulator, uint32_t data) {
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 0) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 8) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 16) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 24) & 0xFF)] ^ Accumulator >> 8;
return Accumulator;
}
uint32_t crc32cx(uint32_t Accumulator, uint64_t data) {
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 0) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 8) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 16) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 24) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 32) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 40) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 48) & 0xFF)] ^ Accumulator >> 8;
Accumulator = CRC32CTable[(uint8_t)Accumulator ^ ((data >> 56) & 0xFF)] ^ Accumulator >> 8;
return Accumulator;
}
}
namespace FEXCore::CPU {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(AESImc) {
auto Op = IROp->C<IR::IROp_VAESImc>();
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Vector);
// Pseudo-code
// Dst = InvMixColumns(STATE)
__uint128_t Tmp{};
Tmp = AES::InvMixColumns(reinterpret_cast<uint8_t*>(&Src1));
memcpy(GDP, &Tmp, sizeof(Tmp));
}
DEF_OP(AESEnc) {
auto Op = IROp->C<IR::IROp_VAESEnc>();
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
// Pseudo-code
// STATE = Src1
// RoundKey = Src2
// STATE = ShiftRows(STATE)
// STATE = SubBytes(STATE)
// STATE = MixColumns(STATE)
// Dst = STATE XOR RoundKey
__uint128_t Tmp{};
Tmp = AES::ShiftRows(reinterpret_cast<uint8_t*>(&Src1));
Tmp = AES::SubBytes(reinterpret_cast<uint8_t*>(&Tmp), 16);
Tmp = AES::MixColumns(reinterpret_cast<uint8_t*>(&Tmp));
Tmp = Tmp ^ Src2;
memcpy(GDP, &Tmp, sizeof(Tmp));
}
DEF_OP(AESEncLast) {
auto Op = IROp->C<IR::IROp_VAESEncLast>();
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
// Pseudo-code
// STATE = Src1
// RoundKey = Src2
// STATE = ShiftRows(STATE)
// STATE = SubBytes(STATE)
// Dst = STATE XOR RoundKey
__uint128_t Tmp{};
Tmp = AES::ShiftRows(reinterpret_cast<uint8_t*>(&Src1));
Tmp = AES::SubBytes(reinterpret_cast<uint8_t*>(&Tmp), 16);
Tmp = Tmp ^ Src2;
memcpy(GDP, &Tmp, sizeof(Tmp));
}
DEF_OP(AESDec) {
auto Op = IROp->C<IR::IROp_VAESDec>();
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
// Pseudo-code
// STATE = Src1
// RoundKey = Src2
// STATE = InvShiftRows(STATE)
// STATE = InvSubBytes(STATE)
// STATE = InvMixColumns(STATE)
// Dst = STATE XOR RoundKey
__uint128_t Tmp{};
Tmp = AES::InvShiftRows(reinterpret_cast<uint8_t*>(&Src1));
Tmp = AES::InvSubBytes(reinterpret_cast<uint8_t*>(&Tmp));
Tmp = AES::InvMixColumns(reinterpret_cast<uint8_t*>(&Tmp));
Tmp = Tmp ^ Src2;
memcpy(GDP, &Tmp, sizeof(Tmp));
}
DEF_OP(AESDecLast) {
auto Op = IROp->C<IR::IROp_VAESDecLast>();
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
// Pseudo-code
// STATE = Src1
// RoundKey = Src2
// STATE = InvShiftRows(STATE)
// STATE = InvSubBytes(STATE)
// Dst = STATE XOR RoundKey
__uint128_t Tmp{};
Tmp = AES::InvShiftRows(reinterpret_cast<uint8_t*>(&Src1));
Tmp = AES::InvSubBytes(reinterpret_cast<uint8_t*>(&Tmp));
Tmp = Tmp ^ Src2;
memcpy(GDP, &Tmp, sizeof(Tmp));
}
DEF_OP(AESKeyGenAssist) {
auto Op = IROp->C<IR::IROp_VAESKeyGenAssist>();
const uint8_t *Src1 = GetSrc<uint8_t*>(Data->SSAData, Op->Src);
// Pseudo-code
// X3 = Src1[127:96]
// X2 = Src1[95:64]
// X1 = Src1[63:32]
// X0 = Src1[31:30]
// RCON = (Zext)rcon
// Dest[31:0] = SubWord(X1)
// Dest[63:32] = RotWord(SubWord(X1)) XOR RCON
// Dest[95:64] = SubWord(X3)
// Dest[127:96] = RotWord(SubWord(X3)) XOR RCON
__uint128_t Tmp{};
uint32_t X1{};
uint32_t X3{};
memcpy(&X1, &Src1[4], 4);
memcpy(&X3, &Src1[12], 4);
uint32_t SubWord_X1 = AES::SubBytes(reinterpret_cast<uint8_t*>(&X1), 4);
uint32_t SubWord_X3 = AES::SubBytes(reinterpret_cast<uint8_t*>(&X3), 4);
auto Ror = [] (auto In, auto R) {
auto RotateMask = sizeof(In) * 8 - 1;
R &= RotateMask;
return (In >> R) | (In << (sizeof(In) * 8 - R));
};
uint32_t Rot_X1 = Ror(SubWord_X1, 8);
uint32_t Rot_X3 = Ror(SubWord_X3, 8);
Tmp = Rot_X3 ^ Op->RCON;
Tmp <<= 32;
Tmp |= SubWord_X3;
Tmp <<= 32;
Tmp |= Rot_X1 ^ Op->RCON;
Tmp <<= 32;
Tmp |= SubWord_X1;
memcpy(GDP, &Tmp, sizeof(Tmp));
}
DEF_OP(CRC32) {
auto Op = IROp->C<IR::IROp_CRC32>();
uint32_t Src1 = *GetSrc<uint32_t*>(Data->SSAData, Op->Src1);
uint8_t *Src2 = GetSrc<uint8_t*>(Data->SSAData, Op->Src2);
uint32_t Tmp{};
switch (Op->SrcSize) {
case 1:
Tmp = CRC32::crc32cb(Src1, *(uint8_t*)Src2);
break;
case 2:
Tmp = CRC32::crc32ch(Src1, *(uint16_t*)Src2);
break;
case 4:
Tmp = CRC32::crc32cw(Src1, *(uint32_t*)Src2);
break;
case 8:
Tmp = CRC32::crc32cx(Src1, *(uint64_t*)Src2);
break;
default:
LOGMAN_MSG_A_FMT("Unknown CRC32C size: {}", Op->SrcSize);
break;
}
memcpy(GDP, &Tmp, sizeof(Tmp));
}
DEF_OP(PCLMUL) {
auto Op = IROp->C<IR::IROp_PCLMUL>();
const auto Selector = Op->Selector;
auto* Dst = GetDest<uint64_t*>(Data->SSAData, Node);
auto* Src1 = GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
auto* Src2 = GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
const uint64_t TMP1 = (Selector & 0x01) == 0 ? Src1[0] : Src1[1];
const uint64_t TMP2 = (Selector & 0x10) == 0 ? Src2[0] : Src2[1];
const auto make_lo = [](uint64_t lhs, uint64_t rhs) {
uint64_t result = 0;
for (size_t i = 0; i < 64; i++) {
if ((lhs & (1ULL << i)) != 0) {
result ^= rhs << i;
}
}
return result;
};
const auto make_hi = [](uint64_t lhs, uint64_t rhs) {
uint64_t result = 0;
for (size_t i = 1; i < 64; i++) {
if ((lhs & (1ULL << i)) != 0) {
result ^= rhs >> (64 - i);
}
}
return result;
};
Dst[0] = make_lo(TMP1, TMP2);
Dst[1] = make_hi(TMP1, TMP2);
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,422 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include "Interface/Core/Interpreter/Fallbacks/F80Fallbacks.h"
#include <cstdint>
namespace FEXCore::CPU {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(F80LOADFCW) {
FEXCore::CPU::OpHandlers<IR::OP_F80LOADFCW>::handle(*GetSrc<uint16_t*>(Data->SSAData, IROp->Args[0]));
}
DEF_OP(F80ADD) {
auto Op = IROp->C<IR::IROp_F80Add>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FADD(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80SUB) {
auto Op = IROp->C<IR::IROp_F80Sub>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FSUB(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80MUL) {
auto Op = IROp->C<IR::IROp_F80Mul>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FMUL(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80DIV) {
auto Op = IROp->C<IR::IROp_F80Div>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FDIV(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80FYL2X) {
auto Op = IROp->C<IR::IROp_F80FYL2X>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FYL2X(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80ATAN) {
auto Op = IROp->C<IR::IROp_F80ATAN>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FATAN(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80FPREM1) {
auto Op = IROp->C<IR::IROp_F80FPREM1>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FREM1(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80FPREM) {
auto Op = IROp->C<IR::IROp_F80FPREM>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FREM(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80SCALE) {
auto Op = IROp->C<IR::IROp_F80SCALE>();
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
const auto Tmp = X80SoftFloat::FSCALE(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80CVT) {
auto Op = IROp->C<IR::IROp_F80CVT>();
const uint8_t OpSize = IROp->Size;
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
switch (OpSize) {
case 4: {
float Tmp = Src;
memcpy(GDP, &Tmp, OpSize);
break;
}
case 8: {
double Tmp = Src;
memcpy(GDP, &Tmp, OpSize);
break;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize);
}
}
DEF_OP(F80CVTINT) {
auto Op = IROp->C<IR::IROp_F80CVTInt>();
const uint8_t OpSize = IROp->Size;
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
switch (OpSize) {
case 2: {
int16_t Tmp = (Op->Truncate? FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle2t : FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle2)(Src);
memcpy(GDP, &Tmp, sizeof(Tmp));
break;
}
case 4: {
int32_t Tmp = (Op->Truncate? FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle4t : FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle4)(Src);
memcpy(GDP, &Tmp, sizeof(Tmp));
break;
}
case 8: {
int64_t Tmp = (Op->Truncate? FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle8t : FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle8)(Src);
memcpy(GDP, &Tmp, sizeof(Tmp));
break;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize);
}
}
DEF_OP(F80CVTTO) {
auto Op = IROp->C<IR::IROp_F80CVTTo>();
switch (Op->SrcSize) {
case 4: {
float Src = *GetSrc<float *>(Data->SSAData, Op->X80Src);
X80SoftFloat Tmp = Src;
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
break;
}
case 8: {
double Src = *GetSrc<double *>(Data->SSAData, Op->X80Src);
X80SoftFloat Tmp = Src;
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
break;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", Op->SrcSize);
}
}
DEF_OP(F80CVTTOINT) {
auto Op = IROp->C<IR::IROp_F80CVTToInt>();
switch (Op->SrcSize) {
case 2: {
int16_t Src = *GetSrc<int16_t*>(Data->SSAData, Op->Src);
X80SoftFloat Tmp = Src;
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
break;
}
case 4: {
int32_t Src = *GetSrc<int32_t*>(Data->SSAData, Op->Src);
X80SoftFloat Tmp = Src;
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
break;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", Op->SrcSize);
}
}
DEF_OP(F80ROUND) {
auto Op = IROp->C<IR::IROp_F80Round>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::FRNDINT(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80F2XM1) {
auto Op = IROp->C<IR::IROp_F80F2XM1>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::F2XM1(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80TAN) {
auto Op = IROp->C<IR::IROp_F80TAN>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::FTAN(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80SQRT) {
auto Op = IROp->C<IR::IROp_F80SQRT>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::FSQRT(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80SIN) {
auto Op = IROp->C<IR::IROp_F80SIN>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::FSIN(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80COS) {
auto Op = IROp->C<IR::IROp_F80COS>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::FCOS(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80XTRACT_EXP) {
auto Op = IROp->C<IR::IROp_F80XTRACT_EXP>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::FXTRACT_EXP(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80XTRACT_SIG) {
auto Op = IROp->C<IR::IROp_F80XTRACT_SIG>();
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
const auto Tmp = X80SoftFloat::FXTRACT_SIG(Src);
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80CMP) {
auto Op = IROp->C<IR::IROp_F80Cmp>();
uint32_t ResultFlags{};
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
bool eq, lt, nan;
X80SoftFloat::FCMP(Src1, Src2, &eq, &lt, &nan);
if (Op->Flags & (1 << IR::FCMP_FLAG_LT) &&
lt) {
ResultFlags |= (1 << IR::FCMP_FLAG_LT);
}
if (Op->Flags & (1 << IR::FCMP_FLAG_UNORDERED) &&
nan) {
ResultFlags |= (1 << IR::FCMP_FLAG_UNORDERED);
}
if (Op->Flags & (1 << IR::FCMP_FLAG_EQ) &&
eq) {
ResultFlags |= (1 << IR::FCMP_FLAG_EQ);
}
GD = ResultFlags;
}
DEF_OP(F80BCDLOAD) {
auto Op = IROp->C<IR::IROp_F80BCDLoad>();
const uint8_t *Src1 = GetSrc<uint8_t*>(Data->SSAData, Op->X80Src);
uint64_t BCD{};
// We walk through each uint8_t and pull out the BCD encoding
// Each 4bit split is a digit
// Only 0-9 is supported, A-F results in undefined data
// | 4 bit | 4 bit |
// | 10s place | 1s place |
// EG 0x48 = 48
// EG 0x4847 = 4847
// This gives us an 18digit value encoded in BCD
// The last byte lets us know if it negative or not
for (size_t i = 0; i < 9; ++i) {
uint8_t Digit = Src1[8 - i];
// First shift our last value over
BCD *= 100;
// Add the tens place digit
BCD += (Digit >> 4) * 10;
// Add the ones place digit
BCD += Digit & 0xF;
}
// Set negative flag once converted to x87
bool Negative = Src1[9] & 0x80;
X80SoftFloat Tmp;
Tmp = BCD;
Tmp.Sign = Negative;
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
}
DEF_OP(F80BCDSTORE) {
auto Op = IROp->C<IR::IROp_F80BCDStore>();
X80SoftFloat Src1 = X80SoftFloat::FRNDINT(*GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src));
bool Negative = Src1.Sign;
// Clear the Sign bit
Src1.Sign = 0;
uint64_t Tmp = Src1;
uint8_t BCD[10]{};
for (size_t i = 0; i < 9; ++i) {
if (Tmp == 0) {
// Nothing left? Just leave
break;
}
// Extract the lower 100 values
uint8_t Digit = Tmp % 100;
// Now divide it for the next iteration
Tmp /= 100;
uint8_t UpperNibble = Digit / 10;
uint8_t LowerNibble = Digit % 10;
// Now store the BCD
BCD[i] = (UpperNibble << 4) | LowerNibble;
}
// Set negative flag once converted to x87
BCD[9] = Negative ? 0x80 : 0;
memcpy(GDP, BCD, 10);
}
DEF_OP(F64SIN) {
auto Op = IROp->C<IR::IROp_F64SIN>();
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
const double Tmp = sin(Src);
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64COS) {
auto Op = IROp->C<IR::IROp_F64COS>();
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
const double Tmp = cos(Src);
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64TAN) {
auto Op = IROp->C<IR::IROp_F64TAN>();
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
const double Tmp = tan(Src);
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64F2XM1) {
auto Op = IROp->C<IR::IROp_F64F2XM1>();
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
const double Tmp = exp2(Src) - 1.0;
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64ATAN) {
auto Op = IROp->C<IR::IROp_F64ATAN>();
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
const double Tmp = atan2(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64FPREM) {
auto Op = IROp->C<IR::IROp_F64FPREM>();
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
const double Tmp = fmod(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64FPREM1) {
auto Op = IROp->C<IR::IROp_F64FPREM1>();
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
const double Tmp = remainder(Src1, Src2);
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64FYL2X) {
auto Op = IROp->C<IR::IROp_F64FYL2X>();
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src);
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
const double Tmp = Src2 * log2(Src1);
memcpy(GDP, &Tmp, sizeof(double));
}
DEF_OP(F64SCALE) {
auto Op = IROp->C<IR::IROp_F64SCALE>();
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
const double trunc = (double)(int64_t)(Src2); //truncate
const double Tmp = Src1 * exp2(trunc);
memcpy(GDP, &Tmp, sizeof(double));
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,336 +0,0 @@
#include <FEXCore/Core/CoreState.h>
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/Fallbacks/F80Fallbacks.h"
#include "Interface/Core/Interpreter/Fallbacks/VectorFallbacks.h"
#include <cstddef>
#include <cstdint>
namespace FEXCore::CPU {
template<typename R, typename... Args>
static FallbackInfo GetFallbackInfo(R(*fn)(Args...), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_UNKNOWN, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(float), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F80_F32, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(double), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F80_F64, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(int16_t), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F80_I16, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(void(*fn)(uint16_t), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_VOID_U16, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(int32_t), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F80_I32, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(float(*fn)(X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F32_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(double(*fn)(X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F64_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(double(*fn)(double), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F64_F64, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(double(*fn)(double,double), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F64_F64_F64, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(int16_t(*fn)(X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_I16_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(int32_t(*fn)(X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_I32_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(int64_t(*fn)(X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_I64_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(uint64_t(*fn)(X80SoftFloat, X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_I64_F80_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F80_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(X80SoftFloat, X80SoftFloat), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_F80_F80_F80, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(uint32_t(*fn)(uint64_t, uint64_t, __uint128_t, __uint128_t, uint16_t), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_I32_I64_I64_I128_I128_I16, (void*)fn, HandlerIndex};
}
template<>
FallbackInfo GetFallbackInfo(uint32_t(*fn)(__uint128_t, __uint128_t, uint16_t), FEXCore::Core::FallbackHandlerIndex HandlerIndex) {
return {FABI_I32_I128_I128_I16, (void*)fn, HandlerIndex};
}
void InterpreterOps::FillFallbackIndexPointers(uint64_t *Info) {
Info[Core::OPINDEX_F80LOADFCW] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80LOADFCW>::handle, Core::OPINDEX_F80LOADFCW).fn);
Info[Core::OPINDEX_F80CVTTO_4] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTO>::handle4, Core::OPINDEX_F80CVTTO_4).fn);
Info[Core::OPINDEX_F80CVTTO_8] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTO>::handle8, Core::OPINDEX_F80CVTTO_8).fn);
Info[Core::OPINDEX_F80CVT_4] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVT>::handle4, Core::OPINDEX_F80CVT_4).fn);
Info[Core::OPINDEX_F80CVT_8] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVT>::handle8, Core::OPINDEX_F80CVT_8).fn);
Info[Core::OPINDEX_F80CVTINT_2] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle2, Core::OPINDEX_F80CVTINT_2).fn);
Info[Core::OPINDEX_F80CVTINT_4] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle4, Core::OPINDEX_F80CVTINT_4).fn);
Info[Core::OPINDEX_F80CVTINT_8] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle8, Core::OPINDEX_F80CVTINT_8).fn);
Info[Core::OPINDEX_F80CVTINT_TRUNC2] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle2t, Core::OPINDEX_F80CVTINT_TRUNC2).fn);
Info[Core::OPINDEX_F80CVTINT_TRUNC4] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle4t, Core::OPINDEX_F80CVTINT_TRUNC4).fn);
Info[Core::OPINDEX_F80CVTINT_TRUNC8] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle8t, Core::OPINDEX_F80CVTINT_TRUNC8).fn);
Info[Core::OPINDEX_F80CMP_0] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<0>, Core::OPINDEX_F80CMP_0).fn);
Info[Core::OPINDEX_F80CMP_1] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<1>, Core::OPINDEX_F80CMP_1).fn);
Info[Core::OPINDEX_F80CMP_2] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<2>, Core::OPINDEX_F80CMP_2).fn);
Info[Core::OPINDEX_F80CMP_3] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<3>, Core::OPINDEX_F80CMP_3).fn);
Info[Core::OPINDEX_F80CMP_4] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<4>, Core::OPINDEX_F80CMP_4).fn);
Info[Core::OPINDEX_F80CMP_5] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<5>, Core::OPINDEX_F80CMP_5).fn);
Info[Core::OPINDEX_F80CMP_6] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<6>, Core::OPINDEX_F80CMP_6).fn);
Info[Core::OPINDEX_F80CMP_7] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<7>, Core::OPINDEX_F80CMP_7).fn);
Info[Core::OPINDEX_F80CVTTOINT_2] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTOINT>::handle2, Core::OPINDEX_F80CVTTOINT_2).fn);
Info[Core::OPINDEX_F80CVTTOINT_4] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTOINT>::handle4, Core::OPINDEX_F80CVTTOINT_4).fn);
// Unary
Info[Core::OPINDEX_F80ROUND] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80ROUND>::handle, Core::OPINDEX_F80ROUND).fn);
Info[Core::OPINDEX_F80F2XM1] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80F2XM1>::handle, Core::OPINDEX_F80F2XM1).fn);
Info[Core::OPINDEX_F80TAN] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80TAN>::handle, Core::OPINDEX_F80TAN).fn);
Info[Core::OPINDEX_F80SQRT] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80SQRT>::handle, Core::OPINDEX_F80SQRT).fn);
Info[Core::OPINDEX_F80SIN] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80SIN>::handle, Core::OPINDEX_F80SIN).fn);
Info[Core::OPINDEX_F80COS] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80COS>::handle, Core::OPINDEX_F80COS).fn);
Info[Core::OPINDEX_F80XTRACT_EXP] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80XTRACT_EXP>::handle, Core::OPINDEX_F80XTRACT_EXP).fn);
Info[Core::OPINDEX_F80XTRACT_SIG] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80XTRACT_SIG>::handle, Core::OPINDEX_F80XTRACT_SIG).fn);
Info[Core::OPINDEX_F80BCDSTORE] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80BCDSTORE>::handle, Core::OPINDEX_F80BCDSTORE).fn);
Info[Core::OPINDEX_F80BCDLOAD] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80BCDLOAD>::handle, Core::OPINDEX_F80BCDLOAD).fn);
// Binary
Info[Core::OPINDEX_F80ADD] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80ADD>::handle, Core::OPINDEX_F80ADD).fn);
Info[Core::OPINDEX_F80SUB] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80SUB>::handle, Core::OPINDEX_F80SUB).fn);
Info[Core::OPINDEX_F80MUL] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80MUL>::handle, Core::OPINDEX_F80MUL).fn);
Info[Core::OPINDEX_F80DIV] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80DIV>::handle, Core::OPINDEX_F80DIV).fn);
Info[Core::OPINDEX_F80FYL2X] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80FYL2X>::handle, Core::OPINDEX_F80FYL2X).fn);
Info[Core::OPINDEX_F80ATAN] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80ATAN>::handle, Core::OPINDEX_F80ATAN).fn);
Info[Core::OPINDEX_F80FPREM1] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80FPREM1>::handle, Core::OPINDEX_F80FPREM1).fn);
Info[Core::OPINDEX_F80FPREM] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80FPREM>::handle, Core::OPINDEX_F80FPREM).fn);
Info[Core::OPINDEX_F80SCALE] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80SCALE>::handle, Core::OPINDEX_F80SCALE).fn);
// Double Precision
Info[Core::OPINDEX_F64SIN] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64SIN>::handle, Core::OPINDEX_F64SIN).fn);
Info[Core::OPINDEX_F64COS] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64COS>::handle, Core::OPINDEX_F64COS).fn);
Info[Core::OPINDEX_F64TAN] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64TAN>::handle, Core::OPINDEX_F64TAN).fn);
Info[Core::OPINDEX_F64ATAN] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64ATAN>::handle, Core::OPINDEX_F64ATAN).fn);
Info[Core::OPINDEX_F64F2XM1] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64F2XM1>::handle, Core::OPINDEX_F64F2XM1).fn);
Info[Core::OPINDEX_F64FYL2X] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64FYL2X>::handle, Core::OPINDEX_F64FYL2X).fn);
Info[Core::OPINDEX_F64FPREM] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64FPREM>::handle, Core::OPINDEX_F64FPREM).fn);
Info[Core::OPINDEX_F64FPREM1] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64FPREM1>::handle, Core::OPINDEX_F64FPREM1).fn);
Info[Core::OPINDEX_F64SCALE] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64SCALE>::handle, Core::OPINDEX_F64SCALE).fn);
// SSE4.2 string instructions
Info[Core::OPINDEX_VPCMPESTRX] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_VPCMPESTRX>::handle, Core::OPINDEX_VPCMPESTRX).fn);
Info[Core::OPINDEX_VPCMPISTRX] = reinterpret_cast<uint64_t>(GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_VPCMPISTRX>::handle, Core::OPINDEX_VPCMPISTRX).fn);
}
bool InterpreterOps::GetFallbackHandler(IR::IROp_Header const *IROp, FallbackInfo *Info) {
uint8_t OpSize = IROp->Size;
switch(IROp->Op) {
case IR::OP_F80LOADFCW: {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80LOADFCW>::handle, Core::OPINDEX_F80LOADFCW);
return true;
}
case IR::OP_F80CVTTO: {
auto Op = IROp->C<IR::IROp_F80CVTTo>();
switch (Op->SrcSize) {
case 4: {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTO>::handle4, Core::OPINDEX_F80CVTTO_4);
return true;
}
case 8: {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTO>::handle8, Core::OPINDEX_F80CVTTO_8);
return true;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize);
}
break;
}
case IR::OP_F80CVT: {
switch (OpSize) {
case 4: {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVT>::handle4, Core::OPINDEX_F80CVT_4);
return true;
}
case 8: {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVT>::handle8, Core::OPINDEX_F80CVT_8);
return true;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize);
}
break;
}
case IR::OP_F80CVTINT: {
auto Op = IROp->C<IR::IROp_F80CVTInt>();
switch (OpSize) {
case 2: {
if (Op->Truncate) {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle2t, Core::OPINDEX_F80CVTINT_TRUNC2);
}
else {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle2, Core::OPINDEX_F80CVTINT_2);
}
return true;
}
case 4: {
if (Op->Truncate) {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle4t, Core::OPINDEX_F80CVTINT_TRUNC4);
}
else {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle4, Core::OPINDEX_F80CVTINT_4);
}
return true;
}
case 8: {
if (Op->Truncate) {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle8t, Core::OPINDEX_F80CVTINT_TRUNC8);
}
else {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTINT>::handle8, Core::OPINDEX_F80CVTINT_8);
}
return true;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize);
}
break;
}
case IR::OP_F80CMP: {
auto Op = IROp->C<IR::IROp_F80Cmp>();
static constexpr std::array handlers{
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<0>,
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<1>,
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<2>,
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<3>,
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<4>,
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<5>,
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<6>,
&FEXCore::CPU::OpHandlers<IR::OP_F80CMP>::handle<7>,
};
*Info = GetFallbackInfo(handlers[Op->Flags], (Core::FallbackHandlerIndex)(Core::OPINDEX_F80CMP_0 + Op->Flags));
return true;
}
case IR::OP_F80CVTTOINT: {
auto Op = IROp->C<IR::IROp_F80CVTToInt>();
switch (Op->SrcSize) {
case 2: {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTOINT>::handle2, Core::OPINDEX_F80CVTTOINT_2);
return true;
}
case 4: {
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80CVTTOINT>::handle4, Core::OPINDEX_F80CVTTOINT_4);
return true;
}
default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize);
}
break;
}
#define COMMON_X87_OP(OP) \
case IR::OP_F80##OP: { \
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F80##OP>::handle, Core::OPINDEX_F80##OP); \
return true; \
}
#define COMMON_F64_OP(OP) \
case IR::OP_F64##OP: { \
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_F64##OP>::handle, Core::OPINDEX_F64##OP); \
return true; \
}
// Unary
COMMON_X87_OP(ROUND)
COMMON_X87_OP(F2XM1)
COMMON_X87_OP(TAN)
COMMON_X87_OP(SQRT)
COMMON_X87_OP(SIN)
COMMON_X87_OP(COS)
COMMON_X87_OP(XTRACT_EXP)
COMMON_X87_OP(XTRACT_SIG)
COMMON_X87_OP(BCDSTORE)
COMMON_X87_OP(BCDLOAD)
// Binary
COMMON_X87_OP(ADD)
COMMON_X87_OP(SUB)
COMMON_X87_OP(MUL)
COMMON_X87_OP(DIV)
COMMON_X87_OP(FYL2X)
COMMON_X87_OP(ATAN)
COMMON_X87_OP(FPREM1)
COMMON_X87_OP(FPREM)
COMMON_X87_OP(SCALE)
// Double Precision Unary
COMMON_F64_OP(F2XM1)
COMMON_F64_OP(TAN)
COMMON_F64_OP(SIN)
COMMON_F64_OP(COS)
// Double Precision Binary
COMMON_F64_OP(FYL2X)
COMMON_F64_OP(ATAN)
COMMON_F64_OP(FPREM1)
COMMON_F64_OP(FPREM)
COMMON_F64_OP(SCALE)
// SSE4.2 Fallbacks
case IR::OP_VPCMPESTRX:
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_VPCMPESTRX>::handle, Core::OPINDEX_VPCMPESTRX);
return true;
case IR::OP_VPCMPISTRX:
*Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers<IR::OP_VPCMPISTRX>::handle, Core::OPINDEX_VPCMPISTRX);
return true;
default:
break;
}
return false;
}
}
@@ -1,21 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include <cstdint>
namespace FEXCore::CPU {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(GetHostFlag) {
auto Op = IROp->C<IR::IROp_GetHostFlag>();
GD = (*GetSrc<uint64_t*>(Data->SSAData, Op->Value) >> Op->Flag) & 1;
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,52 +0,0 @@
#pragma once
#include "Interface/Core/InternalThreadState.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/vector.h>
namespace FEXCore::CPU {
class Dispatcher;
class X86DispatchGenerator;
class Arm64DispatchGenerator;
using DestMapType = fextl::vector<uint32_t>;
class InterpreterCore final : public CPUBackend {
public:
explicit InterpreterCore(Dispatcher *Dispatch,
FEXCore::Core::InternalThreadState *Thread);
[[nodiscard]] fextl::string GetName() override { return "Interpreter"; }
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
[[nodiscard]] void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
[[nodiscard]] bool NeedsOpDispatch() override { return true; }
static void InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX);
void ClearCache() override;
private:
size_t BufferUsed;
Dispatcher *Dispatch;
};
template<typename T>
T AtomicCompareAndSwap(T expected, T desired, T *addr);
uint8_t AtomicFetchNeg(uint8_t *Addr);
uint16_t AtomicFetchNeg(uint16_t *Addr);
uint32_t AtomicFetchNeg(uint32_t *Addr);
uint64_t AtomicFetchNeg(uint64_t *Addr);
} // namespace FEXCore::CPU
@@ -1,98 +0,0 @@
#include "Interface/Context/Context.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/fextl/memory.h>
#include <signal.h>
#include <stdint.h>
#include <utility>
#include "InterpreterOps.h"
#if defined(_M_X86_64)
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#elif defined(_M_ARM_64)
#include "Interface/Core/Dispatcher/Arm64Dispatcher.h"
#else
#error missing arch
#endif
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 128;
namespace FEXCore::IR {
class IRListView;
class RegisterAllocationData;
}
namespace FEXCore::CPU {
InterpreterCore::InterpreterCore(Dispatcher *Dispatcher, FEXCore::Core::InternalThreadState *Thread)
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
, Dispatch(Dispatcher)
{
auto &Interpreter = Thread->CurrentFrame->Pointers.Interpreter;
Interpreter.FragmentExecuter = reinterpret_cast<uint64_t>(&InterpreterOps::InterpretIR);
ClearCache();
}
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) {
static_cast<Context::ContextImpl*>(ThreadState->CTX)->ClearCodeCache(ThreadState);
}
CPUBackend::CompiledCode CodeData{};
const auto BufferStartOffset = BufferUsed;
CodeData.BlockBegin = CodeData.BlockEntry = CurrentCodeBuffer->Ptr + BufferStartOffset;
auto DestBuffer = CodeData.BlockBegin;
if (GDBEnabled) {
const auto GDBSize = Dispatch->GenerateGDBPauseCheck(DestBuffer, Entry);
DestBuffer += GDBSize;
BufferUsed += GDBSize;
}
const auto TrampolineSize = Dispatch->GenerateInterpreterTrampoline(DestBuffer);
DestBuffer += TrampolineSize;
BufferUsed += TrampolineSize;
IR->Serialize(DestBuffer);
DestBuffer += IRSize;
BufferUsed += IRSize;
CodeData.Size = BufferUsed - BufferStartOffset;
return CodeData;
}
void InterpreterCore::ClearCache() {
// Calling this one is needed to setup the initial CurrentCodeBuffer
[[maybe_unused]] auto CodeBuffer = GetEmptyCodeBuffer();
BufferUsed = 0;
}
fextl::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
return fextl::make_unique<InterpreterCore>(ctx->Dispatcher.get(), Thread);
}
CPUBackendFeatures GetInterpreterBackendFeatures() {
return CPUBackendFeatures { };
}
}
@@ -1,23 +0,0 @@
#pragma once
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/fextl/memory.h>
namespace FEXCore::Context {
class ContextImpl;
}
namespace FEXCore::Core {
struct InternalThreadState;
}
namespace FEXCore::CPU {
class CPUBackend;
struct DispatcherConfig;
[[nodiscard]] fextl::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::ContextImpl *ctx,
FEXCore::Core::InternalThreadState *Thread);
void InitializeInterpreterSignalHandlers(FEXCore::Context::ContextImpl *CTX);
CPUBackendFeatures GetInterpreterBackendFeatures();
} // namespace FEXCore::CPU
@@ -1,184 +0,0 @@
#pragma once
#include <FEXCore/IR/IR.h>
#define GD *GetDest<uint64_t*>(Data->SSAData, Node)
#define GDP GetDest<void*>(Data->SSAData, Node)
#define DO_OP(size, type, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(GDP); \
auto *Src1_d = reinterpret_cast<type*>(Src1); \
auto *Src2_d = reinterpret_cast<type*>(Src2); \
*Dst_d = func(*Src1_d, *Src2_d); \
break; \
}
#define DO_SCALAR_COMPARE_OP(size, type, type2, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type2*>(Tmp); \
auto *Src1_d = reinterpret_cast<type*>(Src1); \
auto *Src2_d = reinterpret_cast<type*>(Src2); \
Dst_d[0] = func(Src1_d[0], Src2_d[0]); \
break; \
}
#define DO_VECTOR_COMPARE_OP(size, type, type2, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type2*>(Tmp); \
auto *Src1_d = reinterpret_cast<type*>(Src1); \
auto *Src2_d = reinterpret_cast<type*>(Src2); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = func(Src1_d[i], Src2_d[i]); \
} \
break; \
}
#define DO_VECTOR_OP(size, type, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src1_d = reinterpret_cast<type*>(Src1); \
auto *Src2_d = reinterpret_cast<type*>(Src2); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = func(Src1_d[i], Src2_d[i]); \
} \
break; \
}
#define DO_VECTOR_PAIR_OP(size, type, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src1_d = reinterpret_cast<type*>(Src1); \
auto *Src2_d = reinterpret_cast<type*>(Src2); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = func(Src1_d[i*2], Src1_d[i*2 + 1]); \
Dst_d[i+Elements] = func(Src2_d[i*2], Src2_d[i*2 + 1]); \
} \
break; \
}
#define DO_VECTOR_SCALAR_OP(size, type, func)\
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src1_d = reinterpret_cast<type*>(Src1); \
auto *Src2_d = reinterpret_cast<type*>(Src2); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = func(Src1_d[i], *Src2_d); \
} \
break; \
}
#define DO_VECTOR_0SRC_OP(size, type, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = func(); \
} \
break; \
}
#define DO_VECTOR_1SRC_OP(size, type, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src_d = reinterpret_cast<type*>(Src); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = func(Src_d[i]); \
} \
break; \
}
#define DO_VECTOR_REDUCE_1SRC_OP(size, type, func, start_val) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src_d = reinterpret_cast<type*>(Src); \
type begin = start_val; \
for (uint8_t i = 0; i < Elements; ++i) { \
begin = func(begin, Src_d[i]); \
} \
Dst_d[0] = begin; \
break; \
}
#define DO_VECTOR_SAT_OP(size, type, func, min, max) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src1_d = reinterpret_cast<type*>(Src1); \
auto *Src2_d = reinterpret_cast<type*>(Src2); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = func(Src1_d[i], Src2_d[i], min, max); \
} \
break; \
}
#define DO_VECTOR_1SRC_2TYPE_OP(size, type, type2, func, min, max) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src_d = reinterpret_cast<type2*>(Src); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = (type)func(Src_d[i], min, max); \
} \
break; \
}
#define DO_VECTOR_1SRC_2TYPE_OP_NOSIZE(type, type2, func, min, max) \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src_d = reinterpret_cast<type2*>(Src); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = (type)func(Src_d[i], min, max); \
}
#define DO_VECTOR_1SRC_2TYPE_OP_TOP(size, type, type2, func, min, max) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src_d = reinterpret_cast<type2*>(Src2); \
memcpy(Dst_d, Src1, Elements * sizeof(type2));\
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i+Elements] = (type)func(Src_d[i], min, max); \
} \
break; \
}
#define DO_VECTOR_1SRC_2TYPE_OP_TOP_SRC(size, type, type2, func, min, max) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src_d = reinterpret_cast<type2*>(Src); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = (type)func(Src_d[i+Elements], min, max); \
} \
break; \
}
#define DO_VECTOR_2SRC_2TYPE_OP(size, type, type2, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src1_d = reinterpret_cast<type2*>(Src1); \
auto *Src2_d = reinterpret_cast<type2*>(Src2); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = (type)func((type)Src1_d[i], (type)Src2_d[i]); \
} \
break; \
}
#define DO_VECTOR_2SRC_2TYPE_OP_TOP_SRC(size, type, type2, func) \
case size: { \
auto *Dst_d = reinterpret_cast<type*>(Tmp); \
auto *Src1_d = reinterpret_cast<type2*>(Src1); \
auto *Src2_d = reinterpret_cast<type2*>(Src2); \
for (uint8_t i = 0; i < Elements; ++i) { \
Dst_d[i] = (type)func((type)Src1_d[i+Elements], (type)Src2_d[i+Elements]); \
} \
break; \
}
struct InterpVector256 {
__uint128_t Lower;
__uint128_t Upper;
};
template<typename Res>
Res GetDest(void* SSAData, FEXCore::IR::OrderedNodeWrapper Op) {
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.ID().Value];
return reinterpret_cast<Res>(DstPtr);
}
template<typename Res>
Res GetDest(void* SSAData, FEXCore::IR::NodeID Op) {
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.Value];
return reinterpret_cast<Res>(DstPtr);
}
template<typename Res>
Res GetSrc(void* SSAData, FEXCore::IR::OrderedNodeWrapper Src) {
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Src.ID().Value];
return reinterpret_cast<Res>(DstPtr);
}
@@ -1,399 +0,0 @@
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUID.h"
#include "InterpreterDefines.h"
#include "InterpreterOps.h"
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/BitUtils.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
#include "Interface/HLE/Thunks/Thunks.h"
#include <alloca.h>
#include <algorithm>
#include <array>
#include <atomic>
#include <bit>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <limits>
#include <memory>
namespace FEXCore::CPU {
using OpHandler = void (*)(IR::IROp_Header *IROp, InterpreterOps::IROpData *Data, IR::NodeID Node);
using OpHandlerArray = std::array<OpHandler, IR::IROps::OP_LAST + 1>;
constexpr OpHandlerArray InterpreterOpHandlers = [] {
OpHandlerArray Handlers{};
for (auto& Entry : Handlers) {
Entry = &InterpreterOps::Op_Unhandled;
}
#define REGISTER_OP(op, x) Handlers[IR::IROps::OP_##op] = &InterpreterOps::Op_##x
// ALU ops
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
REGISTER_OP(CONSTANT, Constant);
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
REGISTER_OP(INLINECONSTANT, InlineConstant);
REGISTER_OP(INLINEENTRYPOINTOFFSET, InlineEntrypointOffset);
REGISTER_OP(CYCLECOUNTER, CycleCounter);
REGISTER_OP(ADD, Add);
REGISTER_OP(SUB, Sub);
REGISTER_OP(NEG, Neg);
REGISTER_OP(MUL, Mul);
REGISTER_OP(UMUL, UMul);
REGISTER_OP(DIV, Div);
REGISTER_OP(UDIV, UDiv);
REGISTER_OP(REM, Rem);
REGISTER_OP(UREM, URem);
REGISTER_OP(MULH, MulH);
REGISTER_OP(UMULH, UMulH);
REGISTER_OP(OR, Or);
REGISTER_OP(AND, And);
REGISTER_OP(ANDN, Andn);
REGISTER_OP(XOR, Xor);
REGISTER_OP(LSHL, Lshl);
REGISTER_OP(LSHR, Lshr);
REGISTER_OP(ASHR, Ashr);
REGISTER_OP(ROR, Ror);
REGISTER_OP(EXTR, Extr);
REGISTER_OP(PDEP, PDep);
REGISTER_OP(PEXT, PExt);
REGISTER_OP(LDIV, LDiv);
REGISTER_OP(LUDIV, LUDiv);
REGISTER_OP(LREM, LRem);
REGISTER_OP(LUREM, LURem);
REGISTER_OP(NOT, Not);
REGISTER_OP(POPCOUNT, Popcount);
REGISTER_OP(FINDLSB, FindLSB);
REGISTER_OP(FINDMSB, FindMSB);
REGISTER_OP(FINDTRAILINGZEROS, FindTrailingZeros);
REGISTER_OP(COUNTLEADINGZEROES, CountLeadingZeroes);
REGISTER_OP(REV, Rev);
REGISTER_OP(BFI, Bfi);
REGISTER_OP(BFE, Bfe);
REGISTER_OP(SBFE, Sbfe);
REGISTER_OP(SELECT, Select);
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
REGISTER_OP(FCMP, FCmp);
// Atomic ops
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
REGISTER_OP(ATOMICSUB, AtomicSub);
REGISTER_OP(ATOMICAND, AtomicAnd);
REGISTER_OP(ATOMICOR, AtomicOr);
REGISTER_OP(ATOMICXOR, AtomicXor);
REGISTER_OP(ATOMICSWAP, AtomicSwap);
REGISTER_OP(ATOMICFETCHADD, AtomicFetchAdd);
REGISTER_OP(ATOMICFETCHSUB, AtomicFetchSub);
REGISTER_OP(ATOMICFETCHAND, AtomicFetchAnd);
REGISTER_OP(ATOMICFETCHOR, AtomicFetchOr);
REGISTER_OP(ATOMICFETCHXOR, AtomicFetchXor);
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
// Branch ops
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
REGISTER_OP(EXITFUNCTION, ExitFunction);
REGISTER_OP(JUMP, Jump);
REGISTER_OP(CONDJUMP, CondJump);
REGISTER_OP(SYSCALL, Syscall);
REGISTER_OP(INLINESYSCALL, InlineSyscall);
REGISTER_OP(THUNK, Thunk);
REGISTER_OP(VALIDATECODE, ValidateCode);
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
REGISTER_OP(CPUID, CPUID);
REGISTER_OP(XGETBV, XGETBV);
// 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);
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
// Flag ops
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
// Memory ops
REGISTER_OP(LOADCONTEXT, LoadContext);
REGISTER_OP(STORECONTEXT, StoreContext);
REGISTER_OP(LOADREGISTER, LoadRegister);
REGISTER_OP(STOREREGISTER, StoreRegister);
REGISTER_OP(LOADCONTEXTINDEXED, LoadContextIndexed);
REGISTER_OP(STORECONTEXTINDEXED, StoreContextIndexed);
REGISTER_OP(SPILLREGISTER, SpillRegister);
REGISTER_OP(FILLREGISTER, FillRegister);
REGISTER_OP(LOADFLAG, LoadFlag);
REGISTER_OP(STOREFLAG, StoreFlag);
REGISTER_OP(LOADMEM, LoadMem);
REGISTER_OP(STOREMEM, StoreMem);
REGISTER_OP(LOADMEMTSO, LoadMem);
REGISTER_OP(STOREMEMTSO, StoreMem);
REGISTER_OP(VLOADVECTORMASKED, VLoadVectorMasked);
REGISTER_OP(VSTOREVECTORMASKED, VStoreVectorMasked);
REGISTER_OP(MEMSET, MemSet);
REGISTER_OP(MEMCPY, MemCpy);
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
REGISTER_OP(CACHELINEZERO, CacheLineZero);
// Misc ops
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
REGISTER_OP(BEGINBLOCK, NoOp);
REGISTER_OP(ENDBLOCK, NoOp);
REGISTER_OP(GUESTOPCODE, NoOp);
REGISTER_OP(FENCE, Fence);
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);
REGISTER_OP(PHIVALUE, NoOp);
REGISTER_OP(PRINT, Print);
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
REGISTER_OP(INVALIDATEFLAGS, NoOp);
REGISTER_OP(PROCESSORID, ProcessorID);
REGISTER_OP(RDRAND, RDRAND);
REGISTER_OP(YIELD, Yield);
// Move ops
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
// Vector ops
REGISTER_OP(VECTORZERO, VectorZero);
REGISTER_OP(VECTORIMM, VectorImm);
REGISTER_OP(VMOV, VMov);
REGISTER_OP(VAND, VAnd);
REGISTER_OP(VBIC, VBic);
REGISTER_OP(VOR, VOr);
REGISTER_OP(VXOR, VXor);
REGISTER_OP(VADD, VAdd);
REGISTER_OP(VSUB, VSub);
REGISTER_OP(VUQADD, VUQAdd);
REGISTER_OP(VUQSUB, VUQSub);
REGISTER_OP(VSQADD, VSQAdd);
REGISTER_OP(VSQSUB, VSQSub);
REGISTER_OP(VADDP, VAddP);
REGISTER_OP(VADDV, VAddV);
REGISTER_OP(VUMINV, VUMinV);
REGISTER_OP(VURAVG, VURAvg);
REGISTER_OP(VABS, VAbs);
REGISTER_OP(VPOPCOUNT, VPopcount);
REGISTER_OP(VFADD, VFAdd);
REGISTER_OP(VFADDP, VFAddP);
REGISTER_OP(VFSUB, VFSub);
REGISTER_OP(VFMUL, VFMul);
REGISTER_OP(VFDIV, VFDiv);
REGISTER_OP(VFMIN, VFMin);
REGISTER_OP(VFMAX, VFMax);
REGISTER_OP(VFRECP, VFRecp);
REGISTER_OP(VFSQRT, VFSqrt);
REGISTER_OP(VFRSQRT, VFRSqrt);
REGISTER_OP(VNEG, VNeg);
REGISTER_OP(VFNEG, VFNeg);
REGISTER_OP(VNOT, VNot);
REGISTER_OP(VUMIN, VUMin);
REGISTER_OP(VSMIN, VSMin);
REGISTER_OP(VUMAX, VUMax);
REGISTER_OP(VSMAX, VSMax);
REGISTER_OP(VZIP, VZip);
REGISTER_OP(VZIP2, 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);
REGISTER_OP(VCMPGT, VCMPGT);
REGISTER_OP(VCMPGTZ, VCMPGTZ);
REGISTER_OP(VCMPLTZ, VCMPLTZ);
REGISTER_OP(VFCMPEQ, VFCMPEQ);
REGISTER_OP(VFCMPNEQ, VFCMPNEQ);
REGISTER_OP(VFCMPLT, VFCMPLT);
REGISTER_OP(VFCMPGT, VFCMPGT);
REGISTER_OP(VFCMPLE, VFCMPLE);
REGISTER_OP(VFCMPORD, VFCMPORD);
REGISTER_OP(VFCMPUNO, VFCMPUNO);
REGISTER_OP(VUSHL, VUShl);
REGISTER_OP(VUSHR, VUShr);
REGISTER_OP(VSSHR, VSShr);
REGISTER_OP(VUSHLS, VUShlS);
REGISTER_OP(VUSHRS, VUShrS);
REGISTER_OP(VSSHRS, VSShrS);
REGISTER_OP(VINSELEMENT, VInsElement);
REGISTER_OP(VDUPELEMENT, VDupElement);
REGISTER_OP(VEXTR, VExtr);
REGISTER_OP(VUSHRI, VUShrI);
REGISTER_OP(VSSHRI, VSShrI);
REGISTER_OP(VSHLI, VShlI);
REGISTER_OP(VUSHRNI, VUShrNI);
REGISTER_OP(VUSHRNI2, VUShrNI2);
REGISTER_OP(VSXTL, VSXTL);
REGISTER_OP(VSXTL2, VSXTL2);
REGISTER_OP(VUXTL, VUXTL);
REGISTER_OP(VUXTL2, VUXTL2);
REGISTER_OP(VSQXTN, VSQXTN);
REGISTER_OP(VSQXTN2, VSQXTN2);
REGISTER_OP(VSQXTUN, VSQXTUN);
REGISTER_OP(VSQXTUN2, VSQXTUN2);
REGISTER_OP(VUMUL, VUMul);
REGISTER_OP(VSMUL, VSMul);
REGISTER_OP(VUMULL, VUMull);
REGISTER_OP(VSMULL, VSMull);
REGISTER_OP(VUMULL2, VUMull2);
REGISTER_OP(VSMULL2, VSMull2);
REGISTER_OP(VUABDL, VUABDL);
REGISTER_OP(VTBL1, VTBL1);
REGISTER_OP(VREV64, VRev64);
REGISTER_OP(VPCMPESTRX, VPCMPESTRX);
REGISTER_OP(VPCMPISTRX, VPCMPISTRX);
// Encryption ops
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
REGISTER_OP(VAESDEC, AESDec);
REGISTER_OP(VAESDECLAST, AESDecLast);
REGISTER_OP(VAESKEYGENASSIST, AESKeyGenAssist);
REGISTER_OP(CRC32, CRC32);
REGISTER_OP(PCLMUL, PCLMUL);
// F80 ops
REGISTER_OP(F80LOADFCW, F80LOADFCW);
REGISTER_OP(F80ADD, F80ADD);
REGISTER_OP(F80SUB, F80SUB);
REGISTER_OP(F80MUL, F80MUL);
REGISTER_OP(F80DIV, F80DIV);
REGISTER_OP(F80FYL2X, F80FYL2X);
REGISTER_OP(F80ATAN, F80ATAN);
REGISTER_OP(F80FPREM1, F80FPREM1);
REGISTER_OP(F80FPREM, F80FPREM);
REGISTER_OP(F80SCALE, F80SCALE);
REGISTER_OP(F80CVT, F80CVT);
REGISTER_OP(F80CVTINT, F80CVTINT);
REGISTER_OP(F80CVTTO, F80CVTTO);
REGISTER_OP(F80CVTTOINT, F80CVTTOINT);
REGISTER_OP(F80ROUND, F80ROUND);
REGISTER_OP(F80F2XM1, F80F2XM1);
REGISTER_OP(F80TAN, F80TAN);
REGISTER_OP(F80SQRT, F80SQRT);
REGISTER_OP(F80SIN, F80SIN);
REGISTER_OP(F80COS, F80COS);
REGISTER_OP(F80XTRACT_EXP, F80XTRACT_EXP);
REGISTER_OP(F80XTRACT_SIG, F80XTRACT_SIG);
REGISTER_OP(F80CMP, F80CMP);
REGISTER_OP(F80BCDLOAD, F80BCDLOAD);
REGISTER_OP(F80BCDSTORE, F80BCDSTORE);
// F64 ops
REGISTER_OP(F64SIN, F64SIN);
REGISTER_OP(F64COS, F64COS);
REGISTER_OP(F64TAN, F64TAN);
REGISTER_OP(F64F2XM1, F64F2XM1);
REGISTER_OP(F64ATAN, F64ATAN);
REGISTER_OP(F64FPREM, F64FPREM);
REGISTER_OP(F64FPREM1, F64FPREM1);
REGISTER_OP(F64FYL2X, F64FYL2X);
REGISTER_OP(F64SCALE, F64SCALE);
return Handlers;
}();
void InterpreterOps::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node) {
LOGMAN_MSG_A_FMT("Unhandled IR Op: {}", FEXCore::IR::GetName(IROp->Op));
}
void InterpreterOps::Op_NoOp(FEXCore::IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node) {
}
void InterpreterOps::InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::IR::IRListView const *CurrentIR) {
volatile void *StackEntry = alloca(0);
const uintptr_t ListSize = CurrentIR->GetSSACount();
static_assert(sizeof(FEXCore::IR::OrderedNode) == 16);
auto BlockEnd = CurrentIR->GetBlocks().end();
constexpr size_t ListEntrySizeInBytes = sizeof(InterpVector256);
const size_t SSADataSize = ListSize * ListEntrySizeInBytes;
InterpreterOps::IROpData OpData{
.State = Frame->Thread,
.CurrentEntry = Frame->State.rip,
.CurrentIR = CurrentIR,
.StackEntry = StackEntry,
.SSAData = alloca(SSADataSize),
.BlockResults = {},
.BlockIterator = CurrentIR->GetBlocks().begin(),
};
// Clear all SSAData entries to zero. Required for Zero-extend semantics
memset(OpData.SSAData, 0, SSADataSize);
while (1) {
using namespace FEXCore::IR;
auto [BlockNode, BlockHeader] = OpData.BlockIterator();
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
// Reset the block results per block
memset(&OpData.BlockResults, 0, sizeof(OpData.BlockResults));
auto CodeBegin = CurrentIR->at(BlockIROp->Begin);
auto CodeLast = CurrentIR->at(BlockIROp->Last);
for (auto [CodeNode, IROp] : CurrentIR->GetCode(BlockNode)) {
const auto ID = CurrentIR->GetID(CodeNode);
const uint32_t Op = IROp->Op;
// Execute handler
OpHandler Handler = InterpreterOpHandlers[Op];
Handler(IROp, &OpData, ID);
if (OpData.BlockResults.Quit ||
OpData.BlockResults.Redo ||
CodeBegin == CodeLast) {
break;
}
++CodeBegin;
}
// Iterator will have been set, go again
if (OpData.BlockResults.Redo) {
continue;
}
// If we have set to early exit or at the end block then leave
if (OpData.BlockResults.Quit || ++OpData.BlockIterator == BlockEnd) {
break;
}
}
}
}
@@ -1,419 +0,0 @@
#pragma once
#include <stdint.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
namespace FEXCore::Core {
struct InternalThreadState;
}
namespace FEXCore::IR {
class IRListView;
struct IROp_Header;
}
namespace FEXCore::Core{
struct DebugData;
}
namespace FEXCore::CPU {
enum FallbackABI {
FABI_UNKNOWN,
FABI_VOID_U16,
FABI_F80_F32,
FABI_F80_F64,
FABI_F80_I16,
FABI_F80_I32,
FABI_F32_F80,
FABI_F64_F80,
FABI_F64_F64,
FABI_F64_F64_F64,
FABI_I16_F80,
FABI_I32_F80,
FABI_I64_F80,
FABI_I64_F80_F80,
FABI_F80_F80,
FABI_F80_F80_F80,
FABI_I32_I64_I64_I128_I128_I16,
FABI_I32_I128_I128_I16,
};
struct FallbackInfo {
FallbackABI ABI;
void *fn;
FEXCore::Core::FallbackHandlerIndex HandlerIndex;
};
class InterpreterOps {
public:
static void InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::IR::IRListView const *IR);
static void FillFallbackIndexPointers(uint64_t *Info);
static bool GetFallbackHandler(IR::IROp_Header const *IROp, FallbackInfo *Info);
struct IROpData {
FEXCore::Core::InternalThreadState *State{};
uint64_t CurrentEntry{};
FEXCore::IR::IRListView const *CurrentIR{};
volatile void *StackEntry{};
void *SSAData{};
struct {
bool Quit;
bool Redo;
} BlockResults{};
IR::NodeIterator BlockIterator{0, 0};
};
#define DEF_OP(x) static void Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
///< Unhandled handler
DEF_OP(Unhandled);
///< No-op Handler
DEF_OP(NoOp);
///< ALU Ops
DEF_OP(TruncElementPair);
DEF_OP(Constant);
DEF_OP(EntrypointOffset);
DEF_OP(InlineConstant);
DEF_OP(InlineEntrypointOffset);
DEF_OP(CycleCounter);
DEF_OP(Add);
DEF_OP(Sub);
DEF_OP(Neg);
DEF_OP(Mul);
DEF_OP(UMul);
DEF_OP(Div);
DEF_OP(UDiv);
DEF_OP(Rem);
DEF_OP(URem);
DEF_OP(MulH);
DEF_OP(UMulH);
DEF_OP(Or);
DEF_OP(And);
DEF_OP(Andn);
DEF_OP(Xor);
DEF_OP(Lshl);
DEF_OP(Lshr);
DEF_OP(Ashr);
DEF_OP(Rol);
DEF_OP(Ror);
DEF_OP(Extr);
DEF_OP(PDep);
DEF_OP(PExt);
DEF_OP(LDiv);
DEF_OP(LUDiv);
DEF_OP(LRem);
DEF_OP(LURem);
DEF_OP(Zext);
DEF_OP(Not);
DEF_OP(Popcount);
DEF_OP(FindLSB);
DEF_OP(FindMSB);
DEF_OP(FindTrailingZeros);
DEF_OP(CountLeadingZeroes);
DEF_OP(Rev);
DEF_OP(Bfi);
DEF_OP(Bfe);
DEF_OP(Sbfe);
DEF_OP(Select);
DEF_OP(VExtractToGPR);
DEF_OP(Float_ToGPR_ZU);
DEF_OP(Float_ToGPR_ZS);
DEF_OP(Float_ToGPR_S);
DEF_OP(FCmp);
///< Atomic ops
DEF_OP(CASPair);
DEF_OP(CAS);
DEF_OP(AtomicAdd);
DEF_OP(AtomicSub);
DEF_OP(AtomicAnd);
DEF_OP(AtomicOr);
DEF_OP(AtomicXor);
DEF_OP(AtomicSwap);
DEF_OP(AtomicFetchAdd);
DEF_OP(AtomicFetchSub);
DEF_OP(AtomicFetchAnd);
DEF_OP(AtomicFetchOr);
DEF_OP(AtomicFetchXor);
DEF_OP(AtomicFetchNeg);
///< Branch ops
DEF_OP(CallbackReturn);
DEF_OP(ExitFunction);
DEF_OP(Jump);
DEF_OP(CondJump);
DEF_OP(Syscall);
DEF_OP(InlineSyscall);
DEF_OP(Thunk);
DEF_OP(ValidateCode);
DEF_OP(ThreadRemoveCodeEntry);
DEF_OP(CPUID);
DEF_OP(XGETBV);
///< Conversion ops
DEF_OP(VInsGPR);
DEF_OP(VCastFromGPR);
DEF_OP(VDupFromGPR);
DEF_OP(Float_FromGPR_S);
DEF_OP(Float_FToF);
DEF_OP(Vector_SToF);
DEF_OP(Vector_FToZS);
DEF_OP(Vector_FToS);
DEF_OP(Vector_FToF);
DEF_OP(Vector_FToI);
///< Flag ops
DEF_OP(GetHostFlag);
///< Memory ops
DEF_OP(LoadContext);
DEF_OP(StoreContext);
DEF_OP(LoadRegister);
DEF_OP(StoreRegister);
DEF_OP(LoadContextIndexed);
DEF_OP(StoreContextIndexed);
DEF_OP(SpillRegister);
DEF_OP(FillRegister);
DEF_OP(LoadFlag);
DEF_OP(StoreFlag);
DEF_OP(LoadMem);
DEF_OP(StoreMem);
DEF_OP(VLoadVectorMasked);
DEF_OP(VStoreVectorMasked);
DEF_OP(MemSet);
DEF_OP(MemCpy);
DEF_OP(CacheLineClear);
DEF_OP(CacheLineClean);
DEF_OP(CacheLineZero);
///< Misc ops
DEF_OP(EndBlock);
DEF_OP(Fence);
DEF_OP(Break);
DEF_OP(Phi);
DEF_OP(PhiValue);
DEF_OP(Print);
DEF_OP(GetRoundingMode);
DEF_OP(SetRoundingMode);
DEF_OP(ProcessorID);
DEF_OP(RDRAND);
DEF_OP(Yield);
///< Move ops
DEF_OP(ExtractElementPair);
DEF_OP(CreateElementPair);
DEF_OP(Mov);
///< Vector ops
DEF_OP(VectorZero);
DEF_OP(VectorImm);
DEF_OP(VMov);
DEF_OP(VAnd);
DEF_OP(VBic);
DEF_OP(VOr);
DEF_OP(VXor);
DEF_OP(VAdd);
DEF_OP(VSub);
DEF_OP(VUQAdd);
DEF_OP(VUQSub);
DEF_OP(VSQAdd);
DEF_OP(VSQSub);
DEF_OP(VAddP);
DEF_OP(VAddV);
DEF_OP(VUMinV);
DEF_OP(VURAvg);
DEF_OP(VAbs);
DEF_OP(VPopcount);
DEF_OP(VFAdd);
DEF_OP(VFAddP);
DEF_OP(VFSub);
DEF_OP(VFMul);
DEF_OP(VFDiv);
DEF_OP(VFMin);
DEF_OP(VFMax);
DEF_OP(VFRecp);
DEF_OP(VFSqrt);
DEF_OP(VFRSqrt);
DEF_OP(VNeg);
DEF_OP(VFNeg);
DEF_OP(VNot);
DEF_OP(VUMin);
DEF_OP(VSMin);
DEF_OP(VUMax);
DEF_OP(VSMax);
DEF_OP(VZip);
DEF_OP(VUnZip);
DEF_OP(VTrn);
DEF_OP(VBSL);
DEF_OP(VCMPEQ);
DEF_OP(VCMPEQZ);
DEF_OP(VCMPGT);
DEF_OP(VCMPGTZ);
DEF_OP(VCMPLTZ);
DEF_OP(VFCMPEQ);
DEF_OP(VFCMPNEQ);
DEF_OP(VFCMPLT);
DEF_OP(VFCMPGT);
DEF_OP(VFCMPLE);
DEF_OP(VFCMPORD);
DEF_OP(VFCMPUNO);
DEF_OP(VUShl);
DEF_OP(VUShr);
DEF_OP(VSShr);
DEF_OP(VUShlS);
DEF_OP(VUShrS);
DEF_OP(VSShrS);
DEF_OP(VInsElement);
DEF_OP(VDupElement);
DEF_OP(VExtr);
DEF_OP(VUShrI);
DEF_OP(VSShrI);
DEF_OP(VShlI);
DEF_OP(VUShrNI);
DEF_OP(VUShrNI2);
DEF_OP(VSXTL);
DEF_OP(VSXTL2);
DEF_OP(VUXTL);
DEF_OP(VUXTL2);
DEF_OP(VSQXTN);
DEF_OP(VSQXTN2);
DEF_OP(VSQXTUN);
DEF_OP(VSQXTUN2);
DEF_OP(VUMul);
DEF_OP(VUMull);
DEF_OP(VSMul);
DEF_OP(VSMull);
DEF_OP(VUMull2);
DEF_OP(VSMull2);
DEF_OP(VUABDL);
DEF_OP(VTBL1);
DEF_OP(VRev64);
DEF_OP(VPCMPESTRX);
DEF_OP(VPCMPISTRX);
///< Encryption ops
DEF_OP(AESImc);
DEF_OP(AESEnc);
DEF_OP(AESEncLast);
DEF_OP(AESDec);
DEF_OP(AESDecLast);
DEF_OP(AESKeyGenAssist);
DEF_OP(CRC32);
DEF_OP(PCLMUL);
///< F80 ops
DEF_OP(F80LOADFCW);
DEF_OP(F80ADD);
DEF_OP(F80SUB);
DEF_OP(F80MUL);
DEF_OP(F80DIV);
DEF_OP(F80FYL2X);
DEF_OP(F80ATAN);
DEF_OP(F80FPREM1);
DEF_OP(F80FPREM);
DEF_OP(F80SCALE);
DEF_OP(F80CVT);
DEF_OP(F80CVTINT);
DEF_OP(F80CVTTO);
DEF_OP(F80CVTTOINT);
DEF_OP(F80ROUND);
DEF_OP(F80F2XM1);
DEF_OP(F80TAN);
DEF_OP(F80SQRT);
DEF_OP(F80SIN);
DEF_OP(F80COS);
DEF_OP(F80XTRACT_EXP);
DEF_OP(F80XTRACT_SIG);
DEF_OP(F80CMP);
DEF_OP(F80BCDLOAD);
DEF_OP(F80BCDSTORE);
//< F64 ops
DEF_OP(F64SIN);
DEF_OP(F64COS);
DEF_OP(F64TAN);
DEF_OP(F64F2XM1);
DEF_OP(F64ATAN);
DEF_OP(F64FPREM);
DEF_OP(F64FPREM1);
DEF_OP(F64FYL2X);
DEF_OP(F64SCALE);
#undef DEF_OP
template<typename unsigned_type, typename signed_type, typename float_type>
[[nodiscard]] static bool IsConditionTrue(uint8_t Cond, uint64_t Src1, uint64_t Src2) {
bool CompResult = false;
switch (Cond) {
case FEXCore::IR::COND_EQ:
CompResult = static_cast<unsigned_type>(Src1) == static_cast<unsigned_type>(Src2);
break;
case FEXCore::IR::COND_NEQ:
CompResult = static_cast<unsigned_type>(Src1) != static_cast<unsigned_type>(Src2);
break;
case FEXCore::IR::COND_SGE:
CompResult = static_cast<signed_type>(Src1) >= static_cast<signed_type>(Src2);
break;
case FEXCore::IR::COND_SLT:
CompResult = static_cast<signed_type>(Src1) < static_cast<signed_type>(Src2);
break;
case FEXCore::IR::COND_SGT:
CompResult = static_cast<signed_type>(Src1) > static_cast<signed_type>(Src2);
break;
case FEXCore::IR::COND_SLE:
CompResult = static_cast<signed_type>(Src1) <= static_cast<signed_type>(Src2);
break;
case FEXCore::IR::COND_UGE:
CompResult = static_cast<unsigned_type>(Src1) >= static_cast<unsigned_type>(Src2);
break;
case FEXCore::IR::COND_ULT:
CompResult = static_cast<unsigned_type>(Src1) < static_cast<unsigned_type>(Src2);
break;
case FEXCore::IR::COND_UGT:
CompResult = static_cast<unsigned_type>(Src1) > static_cast<unsigned_type>(Src2);
break;
case FEXCore::IR::COND_ULE:
CompResult = static_cast<unsigned_type>(Src1) <= static_cast<unsigned_type>(Src2);
break;
case FEXCore::IR::COND_FLU:
CompResult = reinterpret_cast<float_type&>(Src1) < reinterpret_cast<float_type&>(Src2) || (std::isnan(reinterpret_cast<float_type&>(Src1)) || std::isnan(reinterpret_cast<float_type&>(Src2)));
break;
case FEXCore::IR::COND_FGE:
CompResult = reinterpret_cast<float_type&>(Src1) >= reinterpret_cast<float_type&>(Src2) && !(std::isnan(reinterpret_cast<float_type&>(Src1)) || std::isnan(reinterpret_cast<float_type&>(Src2)));
break;
case FEXCore::IR::COND_FLEU:
CompResult = reinterpret_cast<float_type&>(Src1) <= reinterpret_cast<float_type&>(Src2) || (std::isnan(reinterpret_cast<float_type&>(Src1)) || std::isnan(reinterpret_cast<float_type&>(Src2)));
break;
case FEXCore::IR::COND_FGT:
CompResult = reinterpret_cast<float_type&>(Src1) > reinterpret_cast<float_type&>(Src2) && !(std::isnan(reinterpret_cast<float_type&>(Src1)) || std::isnan(reinterpret_cast<float_type&>(Src2)));
break;
case FEXCore::IR::COND_FU:
CompResult = (std::isnan(reinterpret_cast<float_type&>(Src1)) || std::isnan(reinterpret_cast<float_type&>(Src2)));
break;
case FEXCore::IR::COND_FNU:
CompResult = !(std::isnan(reinterpret_cast<float_type&>(Src1)) || std::isnan(reinterpret_cast<float_type&>(Src2)));
break;
case FEXCore::IR::COND_MI:
case FEXCore::IR::COND_PL:
case FEXCore::IR::COND_VS:
case FEXCore::IR::COND_VC:
default:
LOGMAN_MSG_A_FMT("Unsupported compare type");
break;
}
return CompResult;
}
static uint8_t GetOpSize(FEXCore::IR::IRListView const *CurrentIR, IR::OrderedNodeWrapper Node) {
auto IROp = CurrentIR->GetOp<FEXCore::IR::IROp_Header>(Node);
return IROp->Size;
}
};
} // namespace FEXCore::CPU
@@ -1,687 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Core/CPUID.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include <cstdint>
namespace FEXCore::CPU {
static inline void CacheLineFlush(char *Addr) {
#ifdef _M_X86_64
__asm volatile (
"clflush (%[Addr]);"
:: [Addr] "r" (Addr)
: "memory");
#else
__builtin___clear_cache(Addr, Addr+64);
#endif
}
static inline void CacheLineClean(char *Addr) {
#ifdef _M_X86_64
__asm volatile (
"clwb (%[Addr]);"
:: [Addr] "r" (Addr)
: "memory");
#elif _M_ARM_64
__asm volatile (
"dc cvac, %[Addr]"
:: [Addr] "r" (Addr)
: "memory");
#else
LOGMAN_THROW_A_FMT("Unsupported architecture with cacheline clean");
#endif
}
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(LoadContext) {
const auto Op = IROp->C<IR::IROp_LoadContext>();
const auto OpSize = IROp->Size;
const auto ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
const auto Src = ContextPtr + Op->Offset;
#define LOAD_CTX(x, y) \
case x: { \
y const *MemData = reinterpret_cast<y const*>(Src); \
GD = *MemData; \
break; \
}
switch (OpSize) {
LOAD_CTX(1, uint8_t)
LOAD_CTX(2, uint16_t)
LOAD_CTX(4, uint32_t)
LOAD_CTX(8, uint64_t)
case 16:
case 32: {
void const *MemData = reinterpret_cast<void const*>(Src);
memcpy(GDP, MemData, OpSize);
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled LoadContext size: {}", OpSize);
break;
}
#undef LOAD_CTX
}
DEF_OP(StoreContext) {
const auto Op = IROp->C<IR::IROp_StoreContext>();
const auto OpSize = IROp->Size;
const auto ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
const auto Dst = ContextPtr + Op->Offset;
void *MemData = reinterpret_cast<void*>(Dst);
void *Src = GetSrc<void*>(Data->SSAData, Op->Value);
memcpy(MemData, Src, OpSize);
}
DEF_OP(LoadRegister) {
const auto Op = IROp->C<IR::IROp_LoadRegister>();
const auto OpSize = IROp->Size;
const auto ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
const auto Src = ContextPtr + Op->Offset;
#define LOAD_CTX(x, y) \
case x: { \
y const *MemData = reinterpret_cast<y const*>(Src); \
GD = *MemData; \
break; \
}
switch (OpSize) {
LOAD_CTX(1, uint8_t)
LOAD_CTX(2, uint16_t)
LOAD_CTX(4, uint32_t)
LOAD_CTX(8, uint64_t)
case 16:
case 32: {
void const *MemData = reinterpret_cast<void const*>(Src);
memcpy(GDP, MemData, OpSize);
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled LoadContext size: {}", OpSize);
break;
}
#undef LOAD_CTX
}
DEF_OP(StoreRegister) {
const auto Op = IROp->C<IR::IROp_StoreRegister>();
const auto OpSize = IROp->Size;
const auto ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
const auto Dst = ContextPtr + Op->Offset;
void *MemData = reinterpret_cast<void*>(Dst);
void *Src = GetSrc<void*>(Data->SSAData, Op->Value);
memcpy(MemData, Src, OpSize);
}
DEF_OP(LoadContextIndexed) {
const auto Op = IROp->C<IR::IROp_LoadContextIndexed>();
const auto OpSize = IROp->Size;
const auto Index = *GetSrc<uint64_t*>(Data->SSAData, Op->Index);
const auto ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
const auto Src = ContextPtr + Op->BaseOffset + (Index * Op->Stride);
#define LOAD_CTX(x, y) \
case x: { \
y const *MemData = reinterpret_cast<y const*>(Src); \
GD = *MemData; \
break; \
}
switch (OpSize) {
LOAD_CTX(1, uint8_t)
LOAD_CTX(2, uint16_t)
LOAD_CTX(4, uint32_t)
LOAD_CTX(8, uint64_t)
case 16:
case 32: {
void const *MemData = reinterpret_cast<void const*>(Src);
memcpy(GDP, MemData, OpSize);
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled LoadContextIndexed size: {}", OpSize);
break;
}
#undef LOAD_CTX
}
DEF_OP(StoreContextIndexed) {
const auto Op = IROp->C<IR::IROp_StoreContextIndexed>();
const auto OpSize = IROp->Size;
const auto Index = *GetSrc<uint64_t*>(Data->SSAData, Op->Index);
const auto ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
const auto Dst = ContextPtr + Op->BaseOffset + (Index * Op->Stride);
void *MemData = reinterpret_cast<void*>(Dst);
void *Src = GetSrc<void*>(Data->SSAData, Op->Value);
memcpy(MemData, Src, OpSize);
}
DEF_OP(SpillRegister) {
LOGMAN_MSG_A_FMT("Unimplemented");
}
DEF_OP(FillRegister) {
LOGMAN_MSG_A_FMT("Unimplemented");
}
DEF_OP(LoadFlag) {
auto Op = IROp->C<IR::IROp_LoadFlag>();
uintptr_t ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
ContextPtr += offsetof(FEXCore::Core::CPUState, flags[0]);
ContextPtr += Op->Flag;
uint8_t const *MemData = reinterpret_cast<uint8_t const*>(ContextPtr);
GD = *MemData;
}
DEF_OP(StoreFlag) {
auto Op = IROp->C<IR::IROp_StoreFlag>();
uint8_t Arg = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
uintptr_t ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
ContextPtr += offsetof(FEXCore::Core::CPUState, flags[0]);
ContextPtr += Op->Flag;
uint8_t *MemData = reinterpret_cast<uint8_t*>(ContextPtr);
*MemData = Arg;
}
DEF_OP(LoadMem) {
const auto Op = IROp->C<IR::IROp_LoadMem>();
const auto OpSize = IROp->Size;
uint8_t const *MemData = *GetSrc<uint8_t const**>(Data->SSAData, Op->Addr);
if (!Op->Offset.IsInvalid()) {
auto Offset = *GetSrc<uintptr_t const*>(Data->SSAData, Op->Offset) * Op->OffsetScale;
switch(Op->OffsetType.Val) {
case IR::MEM_OFFSET_SXTX.Val: MemData += Offset; break;
case IR::MEM_OFFSET_UXTW.Val: MemData += (uint32_t)Offset; break;
case IR::MEM_OFFSET_SXTW.Val: MemData += (int32_t)Offset; break;
}
}
memset(GDP, 0, Core::CPUState::XMM_AVX_REG_SIZE);
switch (OpSize) {
case 1: {
auto D = reinterpret_cast<const std::atomic<uint8_t>*>(MemData);
GD = D->load();
break;
}
case 2: {
auto D = reinterpret_cast<const std::atomic<uint16_t>*>(MemData);
GD = D->load();
break;
}
case 4: {
auto D = reinterpret_cast<const std::atomic<uint32_t>*>(MemData);
GD = D->load();
break;
}
case 8: {
auto D = reinterpret_cast<const std::atomic<uint64_t>*>(MemData);
GD = D->load();
break;
}
default:
memcpy(GDP, MemData, OpSize);
break;
}
}
DEF_OP(StoreMem) {
const auto Op = IROp->C<IR::IROp_StoreMem>();
const auto OpSize = IROp->Size;
uint8_t *MemData = *GetSrc<uint8_t **>(Data->SSAData, Op->Addr);
if (!Op->Offset.IsInvalid()) {
auto Offset = *GetSrc<uintptr_t const*>(Data->SSAData, Op->Offset) * Op->OffsetScale;
switch(Op->OffsetType.Val) {
case IR::MEM_OFFSET_SXTX.Val: MemData += Offset; break;
case IR::MEM_OFFSET_UXTW.Val: MemData += (uint32_t)Offset; break;
case IR::MEM_OFFSET_SXTW.Val: MemData += (int32_t)Offset; break;
}
}
switch (OpSize) {
case 1: {
reinterpret_cast<std::atomic<uint8_t>*>(MemData)->store(*GetSrc<uint8_t*>(Data->SSAData, Op->Value));
break;
}
case 2: {
reinterpret_cast<std::atomic<uint16_t>*>(MemData)->store(*GetSrc<uint16_t*>(Data->SSAData, Op->Value));
break;
}
case 4: {
reinterpret_cast<std::atomic<uint32_t>*>(MemData)->store(*GetSrc<uint32_t*>(Data->SSAData, Op->Value));
break;
}
case 8: {
reinterpret_cast<std::atomic<uint64_t>*>(MemData)->store(*GetSrc<uint64_t*>(Data->SSAData, Op->Value));
break;
}
default:
memcpy(MemData, GetSrc<void*>(Data->SSAData, Op->Value), OpSize);
break;
}
}
DEF_OP(VLoadVectorMasked) {
const auto Op = IROp->C<IR::IROp_VLoadVectorMasked>();
const auto OpSize = IROp->Size;
const auto ElementSize = IROp->ElementSize;
const auto NumElements = OpSize / ElementSize;
const auto *MemData = *GetSrc<uint8_t const**>(Data->SSAData, Op->Addr);
const auto *Mask = GetSrc<uint8_t const*>(Data->SSAData, Op->Mask);
const auto SetElements = [NumElements]<typename T>(void* Dst, const T* MaskValues, const T* MemoryData) {
const auto SignBit = 1ULL << ((sizeof(T) * 8) - 1);
for (size_t i = 0; i < NumElements; i++) {
if ((MaskValues[i] & SignBit) != 0) {
std::memcpy(static_cast<uint8_t*>(Dst) + (i * sizeof(T)), MemoryData + i, sizeof(T));
}
}
};
if (!Op->Offset.IsInvalid()) {
auto Offset = *GetSrc<uintptr_t const*>(Data->SSAData, Op->Offset) * Op->OffsetScale;
switch(Op->OffsetType.Val) {
case IR::MEM_OFFSET_SXTX.Val: MemData += Offset; break;
case IR::MEM_OFFSET_UXTW.Val: MemData += (uint32_t)Offset; break;
case IR::MEM_OFFSET_SXTW.Val: MemData += (int32_t)Offset; break;
}
}
memset(GDP, 0, Core::CPUState::XMM_AVX_REG_SIZE);
switch (ElementSize) {
case 1: {
SetElements(GDP, Mask, MemData);
return;
}
case 2: {
SetElements(GDP,
reinterpret_cast<const uint16_t*>(Mask),
reinterpret_cast<const uint16_t*>(MemData));
return;
}
case 4: {
SetElements(GDP,
reinterpret_cast<const uint32_t*>(Mask),
reinterpret_cast<const uint32_t*>(MemData));
return;
}
case 8: {
SetElements(GDP,
reinterpret_cast<const uint64_t*>(Mask),
reinterpret_cast<const uint64_t*>(MemData));
return;
}
default:
LOGMAN_MSG_A_FMT("Unhandled VLoadVectorMasked element size: {}", ElementSize);
return;
}
}
DEF_OP(VStoreVectorMasked) {
const auto Op = IROp->C<IR::IROp_VStoreVectorMasked>();
const auto OpSize = IROp->Size;
const auto ElementSize = IROp->ElementSize;
const auto NumElements = OpSize / ElementSize;
auto *Dst = *GetSrc<uint8_t**>(Data->SSAData, Op->Addr);
const auto *RegData = GetSrc<uint8_t const*>(Data->SSAData, Op->Data);
const auto *Mask = GetSrc<uint8_t const*>(Data->SSAData, Op->Mask);
const auto SetElements = [NumElements]<typename T>(void* Dst, const T* MaskValues, const T* DataVals) {
const auto SignBit = 1ULL << ((sizeof(T) * 8) - 1);
for (size_t i = 0; i < NumElements; i++) {
if ((MaskValues[i] & SignBit) != 0) {
std::memcpy(static_cast<uint8_t*>(Dst) + (i * sizeof(T)), DataVals + i, sizeof(T));
}
}
};
if (!Op->Offset.IsInvalid()) {
auto Offset = *GetSrc<uintptr_t const*>(Data->SSAData, Op->Offset) * Op->OffsetScale;
switch(Op->OffsetType.Val) {
case IR::MEM_OFFSET_SXTX.Val: Dst += Offset; break;
case IR::MEM_OFFSET_UXTW.Val: Dst += (uint32_t)Offset; break;
case IR::MEM_OFFSET_SXTW.Val: Dst += (int32_t)Offset; break;
}
}
switch (ElementSize) {
case 1: {
SetElements(Dst, Mask, RegData);
return;
}
case 2: {
SetElements(Dst,
reinterpret_cast<const uint16_t*>(Mask),
reinterpret_cast<const uint16_t*>(RegData));
return;
}
case 4: {
SetElements(Dst,
reinterpret_cast<const uint32_t*>(Mask),
reinterpret_cast<const uint32_t*>(RegData));
return;
}
case 8: {
SetElements(Dst,
reinterpret_cast<const uint64_t*>(Mask),
reinterpret_cast<const uint64_t*>(RegData));
return;
}
default:
LOGMAN_MSG_A_FMT("Unhandled VStoreVectorMasked element size: {}", ElementSize);
return;
}
}
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);
uint64_t MemPrefix{};
if (!Op->Prefix.IsInvalid()) {
MemPrefix = *GetSrc<uint64_t*>(Data->SSAData, Op->Prefix);
}
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 + MemPrefix), Value, Length);
break;
case 2:
MemSetElements(reinterpret_cast<std::atomic<uint16_t>*>(MemData + MemPrefix), Value, Length);
break;
case 4:
MemSetElements(reinterpret_cast<std::atomic<uint32_t>*>(MemData + MemPrefix), Value, Length);
break;
case 8:
MemSetElements(reinterpret_cast<std::atomic<uint64_t>*>(MemData + MemPrefix), Value, Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
else {
switch (Size) {
case 1:
MemSetElements(reinterpret_cast<uint8_t*>(MemData + MemPrefix), Value, Length);
break;
case 2:
MemSetElements(reinterpret_cast<uint16_t*>(MemData + MemPrefix), Value, Length);
break;
case 4:
MemSetElements(reinterpret_cast<uint32_t*>(MemData + MemPrefix), Value, Length);
break;
case 8:
MemSetElements(reinterpret_cast<uint64_t*>(MemData + MemPrefix), 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 + MemPrefix), Value, Length);
break;
case 2:
MemSetElementsInverse(reinterpret_cast<std::atomic<uint16_t>*>(MemData + MemPrefix), Value, Length);
break;
case 4:
MemSetElementsInverse(reinterpret_cast<std::atomic<uint32_t>*>(MemData + MemPrefix), Value, Length);
break;
case 8:
MemSetElementsInverse(reinterpret_cast<std::atomic<uint64_t>*>(MemData + MemPrefix), Value, Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
else {
switch (Size) {
case 1:
MemSetElementsInverse(reinterpret_cast<uint8_t*>(MemData + MemPrefix), Value, Length);
break;
case 2:
MemSetElementsInverse(reinterpret_cast<uint16_t*>(MemData + MemPrefix), Value, Length);
break;
case 4:
MemSetElementsInverse(reinterpret_cast<uint32_t*>(MemData + MemPrefix), Value, Length);
break;
case 8:
MemSetElementsInverse(reinterpret_cast<uint64_t*>(MemData + MemPrefix), Value, Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
GD = reinterpret_cast<uint64_t>(MemData - (Length * Size));
}
}
DEF_OP(MemCpy) {
const auto Op = IROp->C<IR::IROp_MemCpy>();
const int32_t Size = Op->Size;
uint64_t *DstPtr = GetDest<uint64_t*>(Data->SSAData, Node);
char *MemDataDest = *GetSrc<char **>(Data->SSAData, Op->AddrDest);
char *MemDataSrc = *GetSrc<char **>(Data->SSAData, Op->AddrSrc);
uint64_t DestPrefix{};
uint64_t SrcPrefix{};
if (!Op->PrefixDest.IsInvalid()) {
DestPrefix = *GetSrc<uint64_t*>(Data->SSAData, Op->PrefixDest);
}
if (!Op->PrefixSrc.IsInvalid()) {
SrcPrefix = *GetSrc<uint64_t*>(Data->SSAData, Op->PrefixSrc);
}
const auto Length = *GetSrc<uint64_t*>(Data->SSAData, Op->Length);
const auto Direction = *GetSrc<uint8_t*>(Data->SSAData, Op->Direction);
auto MemSetElementsAtomic = [](auto* MemDst, auto* MemSrc, size_t Length) {
for (size_t i = 0; i < Length; ++i) {
MemDst[i].store(MemSrc[i].load());
}
};
auto MemSetElementsAtomicInverse = [](auto* MemDst, auto* MemSrc, size_t Length) {
for (size_t i = 0; i < Length; ++i) {
MemDst[-i].store(MemSrc[-i].load());
}
};
auto MemSetElements = [](auto* MemDst, auto* MemSrc, size_t Length) {
for (size_t i = 0; i < Length; ++i) {
MemDst[i] = MemSrc[i];
}
};
auto MemSetElementsInverse = [](auto* MemDst, auto* MemSrc, size_t Length) {
for (size_t i = 0; i < Length; ++i) {
MemDst[-i] = MemSrc[-i];
}
};
if (Direction == 0) { // Forward
if (Op->IsAtomic) {
switch (Size) {
case 1:
MemSetElementsAtomic(reinterpret_cast<std::atomic<uint8_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint8_t>*>(MemDataSrc + SrcPrefix), Length);
break;
case 2:
MemSetElementsAtomic(reinterpret_cast<std::atomic<uint16_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint16_t>*>(MemDataSrc + SrcPrefix), Length);
break;
case 4:
MemSetElementsAtomic(reinterpret_cast<std::atomic<uint32_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint32_t>*>(MemDataSrc + SrcPrefix), Length);
break;
case 8:
MemSetElementsAtomic(reinterpret_cast<std::atomic<uint64_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint64_t>*>(MemDataSrc + SrcPrefix), Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
else {
switch (Size) {
case 1:
MemSetElements(reinterpret_cast<uint8_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint8_t*>(MemDataSrc + SrcPrefix), Length);
break;
case 2:
MemSetElements(reinterpret_cast<uint16_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint16_t*>(MemDataSrc + SrcPrefix), Length);
break;
case 4:
MemSetElements(reinterpret_cast<uint32_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint32_t*>(MemDataSrc + SrcPrefix), Length);
break;
case 8:
MemSetElements(reinterpret_cast<uint64_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint64_t*>(MemDataSrc + SrcPrefix), Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
DstPtr[0] = reinterpret_cast<uint64_t>(MemDataDest + (Length * Size));
DstPtr[1] = reinterpret_cast<uint64_t>(MemDataSrc + (Length * Size));
}
else { // Backward
if (Op->IsAtomic) {
switch (Size) {
case 1:
MemSetElementsAtomicInverse(reinterpret_cast<std::atomic<uint8_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint8_t>*>(MemDataSrc + SrcPrefix), Length);
break;
case 2:
MemSetElementsAtomicInverse(reinterpret_cast<std::atomic<uint16_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint16_t>*>(MemDataSrc + SrcPrefix), Length);
break;
case 4:
MemSetElementsAtomicInverse(reinterpret_cast<std::atomic<uint32_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint32_t>*>(MemDataSrc + SrcPrefix), Length);
break;
case 8:
MemSetElementsAtomicInverse(reinterpret_cast<std::atomic<uint64_t>*>(MemDataDest + DestPrefix), reinterpret_cast<std::atomic<uint64_t>*>(MemDataSrc + SrcPrefix), Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
else {
switch (Size) {
case 1:
MemSetElementsInverse(reinterpret_cast<uint8_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint8_t*>(MemDataSrc + SrcPrefix), Length);
break;
case 2:
MemSetElementsInverse(reinterpret_cast<uint16_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint16_t*>(MemDataSrc + SrcPrefix), Length);
break;
case 4:
MemSetElementsInverse(reinterpret_cast<uint32_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint32_t*>(MemDataSrc + SrcPrefix), Length);
break;
case 8:
MemSetElementsInverse(reinterpret_cast<uint64_t*>(MemDataDest + DestPrefix), reinterpret_cast<uint64_t*>(MemDataSrc + SrcPrefix), Length);
break;
default:
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
break;
}
}
DstPtr[0] = reinterpret_cast<uint64_t>(MemDataDest - (Length * Size));
DstPtr[1] = reinterpret_cast<uint64_t>(MemDataSrc - (Length * Size));
}
}
DEF_OP(CacheLineClear) {
auto Op = IROp->C<IR::IROp_CacheLineClear>();
char *MemData = *GetSrc<char **>(Data->SSAData, Op->Addr);
// 64-byte cache line clear
CacheLineFlush(MemData);
}
DEF_OP(CacheLineClean) {
auto Op = IROp->C<IR::IROp_CacheLineClean>();
char *MemData = *GetSrc<char **>(Data->SSAData, Op->Addr);
// 64-byte cache line clear
CacheLineClean(MemData);
}
DEF_OP(CacheLineZero) {
auto Op = IROp->C<IR::IROp_CacheLineZero>();
uintptr_t MemData = *GetSrc<uintptr_t*>(Data->SSAData, Op->Addr);
// Force cacheline alignment
MemData = MemData & ~(CPUIDEmu::CACHELINE_SIZE - 1);
using DataType = uint64_t;
DataType *MemData64 = reinterpret_cast<DataType*>(MemData);
// 64-byte cache line zero
for (size_t i = 0; i < (CPUIDEmu::CACHELINE_SIZE / sizeof(DataType)); ++i) {
MemData64[i] = 0;
}
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,174 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include <FEXHeaderUtils/Syscalls.h>
#include <cstdint>
#ifdef _M_X86_64
#include <xmmintrin.h>
#endif
#include <sys/random.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
switch (Op->Fence) {
case IR::Fence_Load.Val:
std::atomic_thread_fence(std::memory_order_acquire);
break;
case IR::Fence_LoadStore.Val:
std::atomic_thread_fence(std::memory_order_seq_cst);
break;
case IR::Fence_Store.Val:
std::atomic_thread_fence(std::memory_order_release);
break;
default: LOGMAN_MSG_A_FMT("Unknown Fence: {}", Op->Fence); break;
}
}
DEF_OP(Break) {
auto Op = IROp->C<IR::IROp_Break>();
Data->State->CurrentFrame->SynchronousFaultData.FaultToTopAndGeneratedException = 1;
Data->State->CurrentFrame->SynchronousFaultData.Signal = Op->Reason.Signal;
Data->State->CurrentFrame->SynchronousFaultData.TrapNo = Op->Reason.TrapNumber;
Data->State->CurrentFrame->SynchronousFaultData.err_code = Op->Reason.ErrorRegister;
Data->State->CurrentFrame->SynchronousFaultData.si_code = Op->Reason.si_code;
switch (Op->Reason.Signal) {
case SIGILL:
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGILL);
break;
case SIGTRAP:
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGTRAP);
break;
case SIGSEGV:
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGSEGV);
break;
default:
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGTRAP);
break;
}
}
DEF_OP(GetRoundingMode) {
uint32_t GuestRounding{};
#ifdef _M_ARM_64
uint64_t Tmp{};
__asm(R"(
mrs %[Tmp], FPCR;
)"
: [Tmp] "=r" (Tmp));
// Extract the rounding
// On ARM the ordering is different than on x86
GuestRounding |= ((Tmp >> 24) & 1) ? IR::ROUND_MODE_FLUSH_TO_ZERO : 0;
uint8_t RoundingMode = (Tmp >> 22) & 0b11;
if (RoundingMode == 0)
GuestRounding |= IR::ROUND_MODE_NEAREST;
else if (RoundingMode == 1)
GuestRounding |= IR::ROUND_MODE_POSITIVE_INFINITY;
else if (RoundingMode == 2)
GuestRounding |= IR::ROUND_MODE_NEGATIVE_INFINITY;
else if (RoundingMode == 3)
GuestRounding |= IR::ROUND_MODE_TOWARDS_ZERO;
#else
GuestRounding = _mm_getcsr();
// Extract the rounding
GuestRounding = (GuestRounding >> 13) & 0b111;
#endif
memcpy(GDP, &GuestRounding, sizeof(GuestRounding));
}
DEF_OP(SetRoundingMode) {
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
const auto GuestRounding = *GetSrc<uint8_t*>(Data->SSAData, Op->RoundMode);
#ifdef _M_ARM_64
uint64_t HostRounding{};
__asm volatile(R"(
mrs %[Tmp], FPCR;
)"
: [Tmp] "=r" (HostRounding));
// Mask out the rounding
HostRounding &= ~(0b111 << 22);
HostRounding |= (GuestRounding & IR::ROUND_MODE_FLUSH_TO_ZERO) ? (1U << 24) : 0;
uint8_t RoundingMode = GuestRounding & 0b11;
if (RoundingMode == IR::ROUND_MODE_NEAREST)
HostRounding |= (0b00U << 22);
else if (RoundingMode == IR::ROUND_MODE_POSITIVE_INFINITY)
HostRounding |= (0b01U << 22);
else if (RoundingMode == IR::ROUND_MODE_NEGATIVE_INFINITY)
HostRounding |= (0b10U << 22);
else if (RoundingMode == IR::ROUND_MODE_TOWARDS_ZERO)
HostRounding |= (0b11U << 22);
__asm volatile(R"(
msr FPCR, %[Tmp];
)"
:: [Tmp] "r" (HostRounding));
#else
uint32_t HostRounding = _mm_getcsr();
// Cut out the host rounding mode
HostRounding &= ~(0b111 << 13);
// Insert our new rounding mode
HostRounding |= GuestRounding << 13;
_mm_setcsr(HostRounding);
#endif
}
DEF_OP(Print) {
auto Op = IROp->C<IR::IROp_Print>();
const uint8_t OpSize = IROp->Size;
if (OpSize <= 8) {
const auto Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
LogMan::Msg::IFmt(">>>> Value in Arg: 0x{:x}, {}", Src, Src);
}
else if (OpSize == 16) {
const auto Src = *GetSrc<__uint128_t*>(Data->SSAData, Op->Value);
const uint64_t Src0 = Src;
const uint64_t Src1 = Src >> 64;
LogMan::Msg::IFmt(">>>> Value[0] in Arg: 0x{:x}, {}", Src0, Src0);
LogMan::Msg::IFmt(" Value[1] in Arg: 0x{:x}, {}", Src1, Src1);
}
else
LOGMAN_MSG_A_FMT("Unknown value size: {}", OpSize);
}
DEF_OP(ProcessorID) {
uint32_t CPU, CPUNode;
FHU::Syscalls::getcpu(&CPU, &CPUNode);
GD = (CPUNode << 12) | CPU;
}
DEF_OP(RDRAND) {
// We are ignoring Op->GetReseeded in the interpreter
uint64_t *DstPtr = GetDest<uint64_t*>(Data->SSAData, Node);
ssize_t Result = ::getrandom(&DstPtr[0], 8, 0);
// Second result is if we managed to read a valid random number or not
DstPtr[1] = Result == 8 ? 1 : 0;
}
DEF_OP(Yield) {
// Nop implementation
}
#undef DEF_OP
} // namespace FEXCore::CPU
@@ -1,35 +0,0 @@
/*
$info$
tags: backend|interpreter
$end_info$
*/
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/Interpreter/InterpreterDefines.h"
#include <cstdint>
namespace FEXCore::CPU {
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
DEF_OP(ExtractElementPair) {
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
const auto Src = GetSrc<uintptr_t>(Data->SSAData, Op->Pair);
memcpy(GDP,
reinterpret_cast<void*>(Src + Op->Header.Size * Op->Element), Op->Header.Size);
}
DEF_OP(CreateElementPair) {
auto Op = IROp->C<IR::IROp_CreateElementPair>();
const void *Src_Lower = GetSrc<void*>(Data->SSAData, Op->Lower);
const void *Src_Upper = GetSrc<void*>(Data->SSAData, Op->Upper);
uint8_t *Dst = GetDest<uint8_t*>(Data->SSAData, Node);
memcpy(Dst, Src_Lower, IROp->ElementSize);
memcpy(Dst + IROp->ElementSize, Src_Upper, IROp->ElementSize);
}
#undef DEF_OP
} // namespace FEXCore::CPU
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,651 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/LogManager.h>
#include <array>
#include <stdint.h>
#include <utility>
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(CASPair) {
auto Op = IROp->C<IR::IROp_CAS>();
// DataSrc = *Src1
// if (DataSrc == Src3) { *Src1 == Src2; } Src2 = DataSrc
// This will write to memory! Careful!
// Third operand must be a calculated guest memory address
//OrderedNode *CASResult = _CAS(Src3, Src2, Src1);
auto Dst = GetSrcPair<RA_64>(Node);
auto Expected = GetSrcPair<RA_64>(Op->Expected.ID());
auto Desired = GetSrcPair<RA_64>(Op->Desired.ID());
auto MemSrc = GetSrc<RA_64>(Op->Addr.ID());
Xbyak::Reg MemReg = MemSrc;
mov(rax, Expected.first);
mov(rdx, Expected.second);
mov(rbx, Desired.first);
mov(rcx, Desired.second);
// RDI(Or Source) now contains pointer
// RDX:RAX contains our expected value
// RCX:RBX contains our desired
lock();
switch (IROp->ElementSize) {
case 4: {
cmpxchg8b(dword [MemReg]);
// EDX:EAX now contains the result
mov(Dst.first.cvt32(), eax);
mov(Dst.second.cvt32(), edx);
break;
}
case 8: {
cmpxchg16b(qword [MemReg]);
// RDX:RAX now contains the result
mov(Dst.first, rax);
mov(Dst.second, rdx);
break;
}
default: LOGMAN_MSG_A_FMT("Unsupported: {}", IROp->ElementSize);
}
}
DEF_OP(CAS) {
auto Op = IROp->C<IR::IROp_CAS>();
uint8_t OpSize = IROp->Size;
// DataSrc = *Src1
// if (DataSrc == Src3) { *Src1 == Src2; } Src2 = DataSrc
// This will write to memory! Careful!
// Third operand must be a calculated guest memory address
//OrderedNode *CASResult = _CAS(Src3, Src2, Src1);
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
mov(rax, GetSrc<RA_64>(Op->Expected.ID()));
// RCX now contains pointer
// RAX contains our expected value
// RDX contains our desired
lock();
switch (OpSize) {
case 1: {
cmpxchg(byte [MemReg], GetSrc<RA_8>(Op->Desired.ID()));
movzx(GetDst<RA_64>(Node), al);
break;
}
case 2: {
cmpxchg(word [MemReg], GetSrc<RA_16>(Op->Desired.ID()));
movzx(GetDst<RA_64>(Node), ax);
break;
}
case 4: {
cmpxchg(dword [MemReg], GetSrc<RA_32>(Op->Desired.ID()));
// RAX now contains the result
mov (GetDst<RA_64>(Node), eax);
break;
}
case 8: {
cmpxchg(qword [MemReg], GetSrc<RA_64>(Op->Desired.ID()));
// RAX now contains the result
mov (GetDst<RA_64>(Node), rax);
break;
}
default: LOGMAN_MSG_A_FMT("Unsupported: {}", OpSize);
}
}
DEF_OP(AtomicAdd) {
auto Op = IROp->C<IR::IROp_AtomicAdd>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
lock();
switch (IROp->Size) {
case 1:
add(byte [MemReg], GetSrc<RA_8>(Op->Value.ID()));
break;
case 2:
add(word [MemReg], GetSrc<RA_16>(Op->Value.ID()));
break;
case 4:
add(dword [MemReg], GetSrc<RA_32>(Op->Value.ID()));
break;
case 8:
add(qword [MemReg], GetSrc<RA_64>(Op->Value.ID()));
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicAdd size: {}", IROp->Size);
}
}
DEF_OP(AtomicSub) {
auto Op = IROp->C<IR::IROp_AtomicSub>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
lock();
switch (IROp->Size) {
case 1:
sub(byte [MemReg], GetSrc<RA_8>(Op->Value.ID()));
break;
case 2:
sub(word [MemReg], GetSrc<RA_16>(Op->Value.ID()));
break;
case 4:
sub(dword [MemReg], GetSrc<RA_32>(Op->Value.ID()));
break;
case 8:
sub(qword [MemReg], GetSrc<RA_64>(Op->Value.ID()));
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicAdd size: {}", IROp->Size);
}
}
DEF_OP(AtomicAnd) {
auto Op = IROp->C<IR::IROp_AtomicAnd>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
lock();
switch (IROp->Size) {
case 1:
and_(byte [MemReg], GetSrc<RA_8>(Op->Value.ID()));
break;
case 2:
and_(word [MemReg], GetSrc<RA_16>(Op->Value.ID()));
break;
case 4:
and_(dword [MemReg], GetSrc<RA_32>(Op->Value.ID()));
break;
case 8:
and_(qword [MemReg], GetSrc<RA_64>(Op->Value.ID()));
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicAdd size: {}", IROp->Size);
}
}
DEF_OP(AtomicOr) {
auto Op = IROp->C<IR::IROp_AtomicOr>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
lock();
switch (IROp->Size) {
case 1:
or_(byte [MemReg], GetSrc<RA_8>(Op->Value.ID()));
break;
case 2:
or_(word [MemReg], GetSrc<RA_16>(Op->Value.ID()));
break;
case 4:
or_(dword [MemReg], GetSrc<RA_32>(Op->Value.ID()));
break;
case 8:
or_(qword [MemReg], GetSrc<RA_64>(Op->Value.ID()));
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicAdd size: {}", IROp->Size);
}
}
DEF_OP(AtomicXor) {
auto Op = IROp->C<IR::IROp_AtomicXor>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
lock();
switch (IROp->Size) {
case 1:
xor_(byte [MemReg], GetSrc<RA_8>(Op->Value.ID()));
break;
case 2:
xor_(word [MemReg], GetSrc<RA_16>(Op->Value.ID()));
break;
case 4:
xor_(dword [MemReg], GetSrc<RA_32>(Op->Value.ID()));
break;
case 8:
xor_(qword [MemReg], GetSrc<RA_64>(Op->Value.ID()));
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicAdd size: {}", IROp->Size);
}
}
DEF_OP(AtomicSwap) {
auto Op = IROp->C<IR::IROp_AtomicSwap>();
Xbyak::Reg MemReg = rax;
mov(MemReg, GetSrc<RA_64>(Op->Addr.ID()));
switch (IROp->Size) {
case 1:
movzx(GetDst<RA_64>(Node), GetSrc<RA_8>(Op->Value.ID()));
lock();
xchg(byte [MemReg], GetDst<RA_8>(Node));
break;
case 2:
movzx(GetDst<RA_64>(Node), GetSrc<RA_16>(Op->Value.ID()));
lock();
xchg(word [MemReg], GetDst<RA_16>(Node));
break;
case 4:
mov(GetDst<RA_64>(Node), GetSrc<RA_32>(Op->Value.ID()));
lock();
xchg(dword [MemReg], GetDst<RA_32>(Node));
break;
case 8:
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Value.ID()));
lock();
xchg(qword [MemReg], GetDst<RA_64>(Node));
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicSwap size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchAdd) {
auto Op = IROp->C<IR::IROp_AtomicFetchAdd>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
switch (IROp->Size) {
case 1:
movzx(rcx, GetSrc<RA_8>(Op->Value.ID()));
lock();
xadd(byte [MemReg], cl);
movzx(GetDst<RA_32>(Node), cl);
break;
case 2:
movzx(rcx, GetSrc<RA_16>(Op->Value.ID()));
lock();
xadd(word [MemReg], cx);
movzx(GetDst<RA_32>(Node), cx);
break;
case 4:
mov(ecx, GetSrc<RA_32>(Op->Value.ID()));
lock();
xadd(dword [MemReg], ecx);
mov(GetDst<RA_64>(Node), ecx);
break;
case 8:
mov(rcx, GetSrc<RA_64>(Op->Value.ID()));
lock();
xadd(qword [MemReg], rcx);
mov(GetDst<RA_64>(Node), rcx);
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicFetchAdd size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchSub) {
auto Op = IROp->C<IR::IROp_AtomicFetchSub>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
switch (IROp->Size) {
case 1:
mov(cl, GetSrc<RA_8>(Op->Value.ID()));
neg(cl);
lock();
xadd(byte [MemReg], cl);
movzx(GetDst<RA_32>(Node), cl);
break;
case 2:
mov(cx, GetSrc<RA_16>(Op->Value.ID()));
neg(cx);
lock();
xadd(word [MemReg], cx);
movzx(GetDst<RA_32>(Node), cx);
break;
case 4:
mov(ecx, GetSrc<RA_32>(Op->Value.ID()));
neg(ecx);
lock();
xadd(dword [MemReg], ecx);
mov(GetDst<RA_32>(Node), ecx);
break;
case 8:
mov(rcx, GetSrc<RA_64>(Op->Value.ID()));
neg(rcx);
lock();
xadd(qword [MemReg], rcx);
mov(GetDst<RA_64>(Node), rcx);
break;
default: LOGMAN_MSG_A_FMT("Unhandled AtomicFetchSub size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchAnd) {
auto Op = IROp->C<IR::IROp_AtomicFetchAnd>();
// TMP1 = rax
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
switch (IROp->Size) {
case 1: {
mov(TMP1.cvt8(), byte [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt8(), TMP1.cvt8());
mov(TMP3.cvt8(), TMP1.cvt8());
and_(TMP2.cvt8(), GetSrc<RA_8>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(byte [MemReg], TMP2.cvt8());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt8());
break;
}
case 2: {
mov(TMP1.cvt16(), word [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt16(), TMP1.cvt16());
mov(TMP3.cvt16(), TMP1.cvt16());
and_(TMP2.cvt16(), GetSrc<RA_16>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(word [MemReg], TMP2.cvt16());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt16());
break;
}
case 4: {
mov(TMP1.cvt32(), dword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt32(), TMP1.cvt32());
mov(TMP3.cvt32(), TMP1.cvt32());
and_(TMP2.cvt32(), GetSrc<RA_32>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(dword [MemReg], TMP2.cvt32());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_32>(Node), TMP3.cvt32());
break;
}
case 8: {
mov(TMP1.cvt64(), qword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt64(), TMP1.cvt64());
mov(TMP3.cvt64(), TMP1.cvt64());
and_(TMP2.cvt64(), GetSrc<RA_64>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(qword [MemReg], TMP2.cvt64());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_64>(Node), TMP3.cvt64());
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled AtomicFetchAnd size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchOr) {
auto Op = IROp->C<IR::IROp_AtomicFetchOr>();
// TMP1 = rax
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
switch (IROp->Size) {
case 1: {
mov(TMP1.cvt8(), byte [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt8(), TMP1.cvt8());
mov(TMP3.cvt8(), TMP1.cvt8());
or_(TMP2.cvt8(), GetSrc<RA_8>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(byte [MemReg], TMP2.cvt8());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt8());
break;
}
case 2: {
mov(TMP1.cvt16(), word [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt16(), TMP1.cvt16());
mov(TMP3.cvt16(), TMP1.cvt16());
or_(TMP2.cvt16(), GetSrc<RA_16>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(word [MemReg], TMP2.cvt16());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt16());
break;
}
case 4: {
mov(TMP1.cvt32(), dword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt32(), TMP1.cvt32());
mov(TMP3.cvt32(), TMP1.cvt32());
or_(TMP2.cvt32(), GetSrc<RA_32>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(dword [MemReg], TMP2.cvt32());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_32>(Node), TMP3.cvt32());
break;
}
case 8: {
mov(TMP1.cvt64(), qword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt64(), TMP1.cvt64());
mov(TMP3.cvt64(), TMP1.cvt64());
or_(TMP2.cvt64(), GetSrc<RA_64>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(qword [MemReg], TMP2.cvt64());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_64>(Node), TMP3.cvt64());
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled AtomicFetchOr size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchXor) {
auto Op = IROp->C<IR::IROp_AtomicFetchXor>();
// TMP1 = rax
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
switch (IROp->Size) {
case 1: {
mov(TMP1.cvt8(), byte [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt8(), TMP1.cvt8());
mov(TMP3.cvt8(), TMP1.cvt8());
xor_(TMP2.cvt8(), GetSrc<RA_8>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(byte [MemReg], TMP2.cvt8());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt8());
break;
}
case 2: {
mov(TMP1.cvt16(), word [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt16(), TMP1.cvt16());
mov(TMP3.cvt16(), TMP1.cvt16());
xor_(TMP2.cvt16(), GetSrc<RA_16>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(word [MemReg], TMP2.cvt16());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt16());
break;
}
case 4: {
mov(TMP1.cvt32(), dword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt32(), TMP1.cvt32());
mov(TMP3.cvt32(), TMP1.cvt32());
xor_(TMP2.cvt32(), GetSrc<RA_32>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(dword [MemReg], TMP2.cvt32());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_32>(Node), TMP3.cvt32());
break;
}
case 8: {
mov(TMP1.cvt64(), qword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt64(), TMP1.cvt64());
mov(TMP3.cvt64(), TMP1.cvt64());
xor_(TMP2.cvt64(), GetSrc<RA_64>(Op->Value.ID()));
// Updates RAX with the value from memory
lock(); cmpxchg(qword [MemReg], TMP2.cvt64());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_64>(Node), TMP3.cvt64());
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled AtomicFetchXor size: {}", IROp->Size);
}
}
DEF_OP(AtomicFetchNeg) {
auto Op = IROp->C<IR::IROp_AtomicFetchNeg>();
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
switch (IROp->Size) {
case 1: {
mov(TMP1.cvt8(), byte [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt8(), TMP1.cvt8());
mov(TMP3.cvt8(), TMP1.cvt8());
neg(TMP2.cvt8());
// Updates RAX with the value from memory
lock(); cmpxchg(byte [MemReg], TMP2.cvt8());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt8());
break;
}
case 2: {
mov(TMP1.cvt16(), word [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt16(), TMP1.cvt16());
mov(TMP3.cvt16(), TMP1.cvt16());
neg(TMP2.cvt16());
// Updates RAX with the value from memory
lock(); cmpxchg(word [MemReg], TMP2.cvt16());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
movzx(GetDst<RA_64>(Node), TMP3.cvt16());
break;
}
case 4: {
mov(TMP1.cvt32(), dword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt32(), TMP1.cvt32());
mov(TMP3.cvt32(), TMP1.cvt32());
neg(TMP2.cvt32());
// Updates RAX with the value from memory
lock(); cmpxchg(dword [MemReg], TMP2.cvt32());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_32>(Node), TMP3.cvt32());
break;
}
case 8: {
mov(TMP1.cvt64(), qword [MemReg]);
Label Loop;
L(Loop);
mov(TMP2.cvt64(), TMP1.cvt64());
mov(TMP3.cvt64(), TMP1.cvt64());
neg(TMP2.cvt64());
// Updates RAX with the value from memory
lock(); cmpxchg(qword [MemReg], TMP2.cvt64());
jne(Loop);
// Result is the previous value from memory, which is currently in TMP3
mov(GetDst<RA_64>(Node), TMP3.cvt64());
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled AtomicFetchNeg size: {}", IROp->Size);
}
}
#undef DEF_OP
void X86JITCore::RegisterAtomicHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(CASPAIR, CASPair);
REGISTER_OP(CAS, CAS);
REGISTER_OP(ATOMICADD, AtomicAdd);
REGISTER_OP(ATOMICSUB, AtomicSub);
REGISTER_OP(ATOMICAND, AtomicAnd);
REGISTER_OP(ATOMICOR, AtomicOr);
REGISTER_OP(ATOMICXOR, AtomicXor);
REGISTER_OP(ATOMICSWAP, AtomicSwap);
REGISTER_OP(ATOMICFETCHADD, AtomicFetchAdd);
REGISTER_OP(ATOMICFETCHSUB, AtomicFetchSub);
REGISTER_OP(ATOMICFETCHAND, AtomicFetchAnd);
REGISTER_OP(ATOMICFETCHOR, AtomicFetchOr);
REGISTER_OP(ATOMICFETCHXOR, AtomicFetchXor);
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
#undef REGISTER_OP
}
}
@@ -1,361 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUID.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/HLE/Thunks/Thunks.h"
#include <FEXCore/Core/CPUID.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/LogManager.h>
#include <array>
#include <memory>
#include <stddef.h>
#include <stdint.h>
#include <unordered_map>
#include <utility>
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(CallbackReturn) {
// Adjust the stack first for a regular return
if (SpillSlots) {
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
}
// Make sure to adjust the refcounter so we don't clear the cache now
sub(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, SignalHandlerRefCounter)], 1);
// We need to adjust an additional 8 bytes to get back to the original "misaligned" RSP state
add(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])], 8);
// Now jump back to the thunk
// XXX: XMM?
add(rsp, 8);
pop(r15);
pop(r14);
pop(r13);
pop(r12);
pop(rbp);
pop(rbx);
ret();
}
DEF_OP(ExitFunction) {
Label FullLookup;
auto Op = IROp->C<IR::IROp_ExitFunction>();
if (SpillSlots) {
add(rsp, SpillSlots * MaxSpillSlotSize);
}
uint64_t NewRIP;
if (IsInlineConstant(Op->NewRIP, &NewRIP) || IsInlineEntrypointOffset(Op->NewRIP, &NewRIP)) {
Label l_BranchHost;
Label l_BranchGuest;
lea(rax, ptr[rip + l_BranchHost]);
jmp(qword[rax]);
L(l_BranchHost);
//FEX_TODO(this is not per thread)
dq(ThreadState->CurrentFrame->Pointers.Common.ExitFunctionLinker);
L(l_BranchGuest);
dq(NewRIP);
} else {
Xbyak::Reg RipReg = GetSrc<RA_64>(Op->NewRIP.ID());
// L1 Cache
mov(rcx, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.L1Pointer)]);
mov(rax, RipReg);
and_(rax, LookupCache::L1_ENTRIES_MASK);
shl(rax, 4);
Xbyak::RegExp LookupBase = rcx + rax;
cmp(qword[LookupBase + 8], RipReg);
jne(FullLookup);
jmp(qword[LookupBase + 0]);
L(FullLookup);
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.rip)], RipReg);
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.DispatcherLoopTop)]);
}
#ifdef BLOCKSTATS
ExitBlock();
#endif
}
DEF_OP(Jump) {
const auto Op = IROp->C<IR::IROp_Jump>();
const auto Target = Op->TargetBlock.ID();
PendingTargetLabel = &JumpTargets.try_emplace(Target).first->second;
}
#define GRCMP(Node) (Op->CompareSize == 4 ? GetSrc<RA_32>(Node) : GetSrc<RA_64>(Node))
DEF_OP(CondJump) {
auto Op = IROp->C<IR::IROp_CondJump>();
Label *TrueTargetLabel = &JumpTargets.try_emplace(Op->TrueBlock.ID()).first->second;
if (IsGPR(Op->Cmp1.ID())) {
uint64_t Const;
if (IsInlineConstant(Op->Cmp2, &Const)) {
cmp(GRCMP(Op->Cmp1.ID()), Const);
} else {
cmp(GRCMP(Op->Cmp1.ID()), GRCMP(Op->Cmp2.ID()));
}
} else if (IsFPR(Op->Cmp1.ID())) {
if (Op->CompareSize == 4) {
ucomiss(GetSrc(Op->Cmp1.ID()), GetSrc(Op->Cmp2.ID()));
} else {
ucomisd(GetSrc(Op->Cmp1.ID()), GetSrc(Op->Cmp2.ID()));
}
}
auto [_, __, JCC] = GetCC(Op->Cond);
(this->*JCC)(*TrueTargetLabel, T_NEAR);
PendingTargetLabel = &JumpTargets.try_emplace(Op->FalseBlock.ID()).first->second;
}
DEF_OP(Syscall) {
auto Op = IROp->C<IR::IROp_Syscall>();
// XXX: This is very terrible, but I don't care for right now
FEXCore::IR::SyscallFlags Flags = Op->Flags;
auto NumPush = RA64.size();
for (auto &Reg : RA64)
push(Reg);
// Syscall ABI for x86-64
// this: rdi
// Thread: rsi
// ArgPointer: rdx (Stack)
//
// Result: RAX
// These are pushed in reverse order because stacks
for (uint32_t i = FEXCore::HLE::SyscallArguments::MAX_ARGS; i > 0; --i) {
if (Op->Header.Args[i - 1].IsInvalid()) continue;
push(GetSrc<RA_64>(Op->Header.Args[i - 1].ID()));
++NumPush;
}
mov(rsi, STATE); // Move thread in to rsi
mov(rdi, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SyscallHandlerObj)]);
mov(rdx, rsp);
if (NumPush & 1)
sub(rsp, 8); // Align
// {rdi, rsi, rdx}
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SyscallHandlerFunc)]);
if (NumPush & 1)
add(rsp, 8); // Align
// Reload arguments just in case they are sill live after the fact
for (uint32_t i = 0; i < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++i) {
if (Op->Header.Args[i].IsInvalid()) continue;
pop(GetSrc<RA_64>(Op->Header.Args[i].ID()));
}
for (uint32_t i = RA64.size(); i > 0; --i)
pop(RA64[i - 1]);
if ((Flags & FEXCore::IR::SyscallFlags::NORETURNEDRESULT) != FEXCore::IR::SyscallFlags::NORETURNEDRESULT) {
// Move result to its destination register.
// Only if `NORETURNEDRESULT` wasn't set, otherwise we might overwrite the CPUState refilled with `FillStaticRegs`
mov (GetDst<RA_64>(Node), rax);
}
}
DEF_OP(Thunk) {
auto Op = IROp->C<IR::IROp_Thunk>();
auto NumPush = RA64.size();
for (auto &Reg : RA64)
push(Reg);
if (NumPush & 1)
sub(rsp, 8); // Align
mov(rdi, GetSrc<RA_64>(Op->ArgPtr.ID()));
auto thunkFn = static_cast<Context::ContextImpl*>(ThreadState->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
mov(rax, reinterpret_cast<uintptr_t>(thunkFn));
call(rax);
if (NumPush & 1)
add(rsp, 8); // Align
for (uint32_t i = RA64.size(); i > 0; --i)
pop(RA64[i - 1]);
}
DEF_OP(ValidateCode) {
auto Op = IROp->C<IR::IROp_ValidateCode>();
const auto* OldCode = (const uint8_t*)&Op->CodeOriginalLow;
int len = Op->CodeLength;
int idx = 0;
xor_(GetDst<RA_64>(Node), GetDst<RA_64>(Node));
mov(rax, Entry + Op->Offset);
mov(rbx, 1);
while (len >= 4) {
cmp(dword[rax + idx], *(const uint32_t*)(OldCode + idx));
cmovne(GetDst<RA_64>(Node), rbx);
len-=4;
idx+=4;
}
while (len >= 2) {
mov(rcx, *(const uint16_t*)(OldCode + idx));
cmp(word[rax + idx], cx);
cmovne(GetDst<RA_64>(Node), rbx);
len-=2;
idx+=2;
}
while (len >= 1) {
cmp(byte[rax + idx], *(const uint8_t*)(OldCode + idx));
cmovne(GetDst<RA_64>(Node), rbx);
len-=1;
idx+=1;
}
}
DEF_OP(ThreadRemoveCodeEntry) {
auto NumPush = RA64.size();
for (auto &Reg : RA64)
push(Reg);
if (NumPush & 1)
sub(rsp, 8); // Align
mov(rdi, STATE);
mov(rax, Entry); // imm64 move
mov(rsi, rax);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadRemoveCodeEntryFromJIT)]);
if (NumPush & 1)
add(rsp, 8); // Align
for (uint32_t i = RA64.size(); i > 0; --i)
pop(RA64[i - 1]);
}
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
for (auto &Reg : RA64)
push(Reg);
// CPUID ABI
// this: rdi
// Function: rsi
//
// Result: RAX, RDX. 4xi32
// rsi can be in the source registers, so copy argument to edx first
mov (edx, GetSrc<RA_32>(Op->Leaf.ID()));
mov (esi, GetSrc<RA_32>(Op->Function.ID()));
mov (rdi, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.CPUIDObj)]);
auto NumPush = RA64.size();
if (NumPush & 1)
sub(rsp, 8); // Align
// {rdi, rsi, rdx}
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.CPUIDFunction)]);
if (NumPush & 1)
add(rsp, 8); // Align
for (uint32_t i = RA64.size(); i > 0; --i)
pop(RA64[i - 1]);
auto Dst = GetSrcPair<RA_64>(Node);
mov(Dst.first, rax);
mov(Dst.second, rdx);
}
DEF_OP(XGETBV) {
auto Op = IROp->C<IR::IROp_XGetBV>();
for (auto &Reg : RA64)
push(Reg);
// CPUID ABI
// this: rdi
// Function: rsi
//
// Result: RAX, RDX. 4xi32
// rsi can be in the source registers, so copy argument to edx first
mov (esi, GetSrc<RA_32>(Op->Function.ID()));
mov (rdi, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.CPUIDObj)]);
auto NumPush = RA64.size();
if (NumPush & 1)
sub(rsp, 8); // Align
// {rdi, rsi, rdx}
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.XCRFunction)]);
if (NumPush & 1)
add(rsp, 8); // Align
for (uint32_t i = RA64.size(); i > 0; --i)
pop(RA64[i - 1]);
auto Dst = GetSrcPair<RA_64>(Node);
mov(Dst.first.cvt32(), eax);
mov(Dst.second, rax);
shr(Dst.second, 32);
}
#undef DEF_OP
void X86JITCore::RegisterBranchHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
REGISTER_OP(EXITFUNCTION, ExitFunction);
REGISTER_OP(JUMP, Jump);
REGISTER_OP(CONDJUMP, CondJump);
REGISTER_OP(SYSCALL, Syscall);
REGISTER_OP(THUNK, Thunk);
REGISTER_OP(VALIDATECODE, ValidateCode);
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
REGISTER_OP(CPUID, CPUID);
REGISTER_OP(XGETBV, XGETBV);
#undef REGISTER_OP
}
}
@@ -1,417 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/LogManager.h>
#include <array>
#include <stdint.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(VInsGPR) {
const auto Op = IROp->C<IR::IROp_VInsGPR>();
const auto OpSize = IROp->Size;
const auto Dst = GetDst(Node);
const auto DestVector = GetSrc(Op->DestVector.ID());
const auto DestIdx = Op->DestIdx;
const auto ElementSize = Op->Header.ElementSize;
const auto ElementSizeBits = ElementSize * 8;
const auto Offset = ElementSizeBits * DestIdx;
constexpr auto SSEBitSize = Core::CPUState::XMM_SSE_REG_SIZE * 8;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto InUpperLane = Offset >= SSEBitSize;
if (InUpperLane && !Is256Bit) {
LOGMAN_MSG_A_FMT("Attempt to access upper 128-bit lane in 128-bit operation! Offset={}",
Offset);
return;
}
if (Is256Bit) {
vmovapd(ToYMM(Dst), ToYMM(DestVector));
} else {
vmovapd(Dst, DestVector);
}
const auto Insert = [&](const Xbyak::Xmm& reg, int index) {
switch (ElementSize) {
case 1: {
if (InUpperLane) {
index -= 16;
}
pinsrb(reg, GetSrc<RA_32>(Op->Src.ID()), index);
break;
}
case 2: {
if (InUpperLane) {
index -= 8;
}
pinsrw(reg, GetSrc<RA_32>(Op->Src.ID()), index);
break;
}
case 4: {
if (InUpperLane) {
index -= 4;
}
pinsrd(reg, GetSrc<RA_32>(Op->Src.ID()), index);
break;
}
case 8: {
if (InUpperLane) {
index -= 2;
}
pinsrq(reg, GetSrc<RA_64>(Op->Src.ID()), index);
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
break;
}
};
if (InUpperLane) {
vextracti128(xmm15, ToYMM(Dst), 1);
Insert(xmm15, DestIdx);
vinserti128(ToYMM(Dst), ToYMM(Dst), xmm15, 1);
} else {
Insert(Dst, DestIdx);
}
}
DEF_OP(VCastFromGPR) {
auto Op = IROp->C<IR::IROp_VCastFromGPR>();
switch (Op->Header.ElementSize) {
case 1:
movzx(rax, GetSrc<RA_8>(Op->Src.ID()));
vmovq(GetDst(Node), rax);
break;
case 2:
movzx(rax, GetSrc<RA_16>(Op->Src.ID()));
vmovq(GetDst(Node), rax);
break;
case 4:
vmovd(GetDst(Node), GetSrc<RA_32>(Op->Src.ID()).cvt32());
break;
case 8:
vmovq(GetDst(Node), GetSrc<RA_64>(Op->Src.ID()).cvt64());
break;
default: LOGMAN_MSG_A_FMT("Unknown VCastFromGPR element size: {}", 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 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>();
const uint16_t ElementSize = Op->Header.ElementSize;
const uint16_t Conv = (ElementSize << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0404: { // Float <- int32_t
cvtsi2ss(GetDst(Node), GetSrc<RA_32>(Op->Src.ID()));
break;
}
case 0x0408: { // Float <- int64_t
cvtsi2ss(GetDst(Node), GetSrc<RA_64>(Op->Src.ID()));
break;
}
case 0x0804: { // Double <- int32_t
cvtsi2sd(GetDst(Node), GetSrc<RA_32>(Op->Src.ID()));
break;
}
case 0x0808: { // Double <- int64_t
cvtsi2sd(GetDst(Node), GetSrc<RA_64>(Op->Src.ID()));
break;
}
default:
LOGMAN_MSG_A_FMT("Unhandled conversion mask: Mask=0x{:04x}, ElementSize={}, SrcElementSize={}",
Conv, ElementSize, Op->SrcElementSize);
break;
}
}
DEF_OP(Float_FToF) {
auto Op = IROp->C<IR::IROp_Float_FToF>();
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
switch (Conv) {
case 0x0804: { // Double <- Float
cvtss2sd(GetDst(Node), GetSrc(Op->Scalar.ID()));
break;
}
case 0x0408: { // Float <- Double
cvtsd2ss(GetDst(Node), GetSrc(Op->Scalar.ID()));
break;
}
default: LOGMAN_MSG_A_FMT("Unknown Float_FToF sizes: 0x{:x}", Conv);
}
}
DEF_OP(Vector_SToF) {
const auto Op = IROp->C<IR::IROp_Vector_SToF>();
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 Vector = GetSrc(Op->Vector.ID());
switch (ElementSize) {
case 4:
if (Is256Bit) {
vcvtdq2ps(ToYMM(Dst), ToYMM(Vector));
} else {
vcvtdq2ps(Dst, Vector);
}
break;
case 8:
// This operation is a bit disgusting in x86
// There is no vector form of this instruction until AVX512VL + AVX512DQ (vcvtqq2pd)
// 1) First extract the top 64bits
// 2) Do a scalar conversion on each
// 3) Make sure to merge them together at the end
pextrq(rax, Vector, 1);
pextrq(rcx, Vector, 0);
cvtsi2sd(Dst, rcx);
cvtsi2sd(xmm15, rax);
if (Is256Bit) {
movlhps(Dst, xmm15);
vextracti128(xmm15, ToYMM(Vector), 1);
pextrq(rax, xmm15, 1);
pextrq(rcx, xmm15, 0);
cvtsi2sd(xmm15, rcx);
cvtsi2sd(xmm14, rax);
movlhps(xmm15, xmm14);
vinserti128(ToYMM(Dst), ToYMM(Dst), xmm15, 1);
} else {
vmovlhps(Dst, Dst, xmm15);
}
break;
default:
LOGMAN_MSG_A_FMT("Unknown Vector_SToF element size: {}", ElementSize);
break;
}
}
DEF_OP(Vector_FToZS) {
const auto Op = IROp->C<IR::IROp_Vector_FToZS>();
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 Vector = GetSrc(Op->Vector.ID());
switch (ElementSize) {
case 4:
if (Is256Bit) {
vcvttps2dq(ToYMM(Dst), ToYMM(Vector));
} else {
vcvttps2dq(Dst, Vector);
}
break;
case 8:
if (Is256Bit) {
vcvttpd2dq(ToYMM(Dst), ToYMM(Vector));
} else {
vcvttpd2dq(Dst, Vector);
}
break;
default:
LOGMAN_MSG_A_FMT("Unknown Vector_FToZS element size: {}", ElementSize);
break;
}
}
DEF_OP(Vector_FToS) {
const auto Op = IROp->C<IR::IROp_Vector_FToS>();
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 Vector = GetSrc(Op->Vector.ID());
switch (ElementSize) {
case 4:
if (Is256Bit) {
vcvtps2dq(ToYMM(Dst), ToYMM(Vector));
} else {
vcvtps2dq(Dst, Vector);
}
break;
case 8:
if (Is256Bit) {
vcvtpd2dq(ToYMM(Dst), ToYMM(Vector));
} else {
vcvtpd2dq(Dst, Vector);
}
break;
default:
LOGMAN_MSG_A_FMT("Unknown Vector_FToS element size: {}", ElementSize);
break;
}
}
DEF_OP(Vector_FToF) {
const auto Op = IROp->C<IR::IROp_Vector_FToF>();
const auto OpSize = IROp->Size;
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Conv = (ElementSize << 8) | Op->SrcElementSize;
const auto Dst = GetDst(Node);
const auto Vector = GetSrc(Op->Vector.ID());
switch (Conv) {
case 0x0804: { // Double <- Float
if (Is256Bit) {
vcvtps2pd(ToYMM(Dst), Vector);
} else {
vcvtps2pd(Dst, Vector);
}
break;
}
case 0x0408: { // Float <- Double
if (Is256Bit) {
vcvtpd2ps(Dst, ToYMM(Vector));
} else {
vcvtpd2ps(Dst, Vector);
}
break;
}
default:
LOGMAN_MSG_A_FMT("Unknown Vector_FToF conversion type : 0x{:04x}", Conv);
break;
}
}
DEF_OP(Vector_FToI) {
const auto Op = IROp->C<IR::IROp_Vector_FToI>();
const auto OpSize = IROp->Size;
const uint8_t RoundMode = [Op] {
switch (Op->Round) {
case FEXCore::IR::Round_Nearest.Val:
return 0b0000'0'0'00;
case FEXCore::IR::Round_Negative_Infinity.Val:
return 0b0000'0'0'01;
case FEXCore::IR::Round_Positive_Infinity.Val:
return 0b0000'0'0'10;
case FEXCore::IR::Round_Towards_Zero.Val:
return 0b0000'0'0'11;
case FEXCore::IR::Round_Host.Val:
return 0b0000'0'1'00;
default:
LOGMAN_MSG_A_FMT("Unhandled rounding mode");
return 0;
}
}();
const auto ElementSize = Op->Header.ElementSize;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
const auto Dst = GetDst(Node);
const auto Vector = GetSrc(Op->Vector.ID());
switch (ElementSize) {
case 4:
if (Is256Bit) {
vroundps(ToYMM(Dst), ToYMM(Vector), RoundMode);
} else {
vroundps(Dst, Vector, RoundMode);
}
break;
case 8:
if (Is256Bit) {
vroundpd(ToYMM(Dst), ToYMM(Vector), RoundMode);
} else {
vroundpd(Dst, Vector, RoundMode);
}
break;
default:
LOGMAN_MSG_A_FMT("Unhandled element size: {}", ElementSize);
break;
}
}
#undef DEF_OP
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);
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
#undef REGISTER_OP
}
}
@@ -1,160 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include <FEXCore/IR/IR.h>
#include <array>
#include <stdint.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(AESImc) {
auto Op = IROp->C<IR::IROp_VAESImc>();
vaesimc(GetDst(Node), GetSrc(Op->Vector.ID()));
}
DEF_OP(AESEnc) {
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) {
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) {
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) {
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) {
auto Op = IROp->C<IR::IROp_VAESKeyGenAssist>();
vaeskeygenassist(GetDst(Node), GetSrc(Op->Src.ID()), Op->RCON);
}
DEF_OP(CRC32) {
auto Op = IROp->C<IR::IROp_CRC32>();
switch (IROp->Size) {
case 4:
mov(TMP1, GetSrc<RA_32>(Op->Src2.ID()));
mov(GetDst<RA_32>(Node), GetSrc<RA_32>(Op->Src1.ID()));
break;
case 8:
mov(TMP1, GetSrc<RA_64>(Op->Src2.ID()));
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Src1.ID()));
break;
default: LOGMAN_MSG_A_FMT("Unknown CRC32 size: {}", IROp->Size);
}
switch (Op->SrcSize) {
case 1:
crc32(GetDst<RA_32>(Node).cvt32(), TMP1.cvt8());
break;
case 2:
crc32(GetDst<RA_32>(Node).cvt32(), TMP1.cvt16());
break;
case 4:
crc32(GetDst<RA_32>(Node).cvt32(), TMP1.cvt32());
break;
case 8:
crc32(GetDst<RA_64>(Node).cvt64(), TMP1.cvt64());
break;
}
}
DEF_OP(PCLMUL) {
const auto Op = IROp->C<IR::IROp_PCLMUL>();
const auto OpSize = IROp->Size;
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
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:
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);
break;
}
}
#undef DEF_OP
void X86JITCore::RegisterEncryptionHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(VAESIMC, AESImc);
REGISTER_OP(VAESENC, AESEnc);
REGISTER_OP(VAESENCLAST, AESEncLast);
REGISTER_OP(VAESDEC, AESDec);
REGISTER_OP(VAESDECLAST, AESDecLast);
REGISTER_OP(VAESKEYGENASSIST, AESKeyGenAssist);
REGISTER_OP(CRC32, CRC32);
REGISTER_OP(PCLMUL, PCLMUL);
#undef REGISTER_OP
}
}
@@ -1,34 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include <FEXCore/IR/IR.h>
#include <array>
#include <stdint.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(GetHostFlag) {
auto Op = IROp->C<IR::IROp_GetHostFlag>();
mov(rax, GetSrc<RA_64>(Op->Value.ID()));
shr(rax, Op->Flag);
and_(rax, 1);
mov(GetDst<RA_64>(Node), rax);
}
#undef DEF_OP
void X86JITCore::RegisterFlagHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
#undef REGISTER_OP
}
}
@@ -1,873 +0,0 @@
/*
$info$
tags: backend|x86-64
desc: Main glue logic of the x86-64 splatter backend
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/IR/PassManager.h"
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include "Utils/MemberFunctionToPointer.h"
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/IR/RegisterAllocationData.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/fextl/sstream.h>
#include <algorithm>
#include <array>
#include <memory>
#include <stddef.h>
#include <stdint.h>
#include <signal.h>
#include <tuple>
#include <unordered_map>
#include <utility>
// #define DEBUG_RA 1
// #define DEBUG_CYCLES
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 256;
namespace {
static void PrintValue(uint64_t Value) {
LogMan::Msg::DFmt("Value: 0x{:x}", Value);
}
static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) {
LogMan::Msg::DFmt("Value: 0x{:016x}'{:016x}", ValueUpper, Value);
}
}
namespace FEXCore::CPU {
void X86JITCore::PushRegs() {
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
sub(rsp, AVXRegSize * RAXMM_x.size());
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
vmovups(ptr[rsp + i * AVXRegSize], ToYMM(RAXMM_x[i]));
}
for (const auto &Reg : RA64) {
push(Reg);
}
const auto NumPush = RA64.size();
if ((NumPush & 1) != 0) {
// Align
sub(rsp, 8);
}
}
void X86JITCore::PopRegs() {
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
const auto NumPush = RA64.size();
if ((NumPush & 1) != 0) {
// Align
add(rsp, 8);
}
for (uint32_t i = RA64.size(); i > 0; --i) {
pop(RA64[i - 1]);
}
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
vmovups(ToYMM(RAXMM_x[i]), ptr[rsp + i * AVXRegSize]);
}
add(rsp, AVXRegSize * RAXMM_x.size());
}
void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
FallbackInfo Info;
if (!InterpreterOps::GetFallbackHandler(IROp, &Info)) {
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
LOGMAN_MSG_A_FMT("Unhandled IR Op: {}", FEXCore::IR::GetName(IROp->Op));
#endif
} else {
switch(Info.ABI) {
case FABI_VOID_U16: {
PushRegs();
mov(edi, GetSrc<RA_32>(IROp->Args[0].ID()));
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
break;
}
case FABI_F80_F32:{
PushRegs();
movss(xmm0, GetSrc(IROp->Args[0].ID()));
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
pxor(GetDst(Node), GetDst(Node));
movq(GetDst(Node), rax);
pinsrw(GetDst(Node), edx, 4);
}
break;
case FABI_F80_F64:{
PushRegs();
movsd(xmm0, GetSrc(IROp->Args[0].ID()));
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
pxor(GetDst(Node), GetDst(Node));
movq(GetDst(Node), rax);
pinsrw(GetDst(Node), edx, 4);
}
break;
case FABI_F80_I16:
case FABI_F80_I32: {
PushRegs();
if (Info.ABI == FABI_F80_I16) {
movsx(rdi, GetSrc<RA_32>(IROp->Args[0].ID()).cvt16());
}
else {
mov(edi, GetSrc<RA_32>(IROp->Args[0].ID()));
}
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
pxor(GetDst(Node), GetDst(Node));
movq(GetDst(Node), rax);
pinsrw(GetDst(Node), edx, 4);
}
break;
case FABI_F32_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
movss(GetDst(Node), xmm0);
}
break;
case FABI_F64_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
movsd(GetDst(Node), xmm0);
}
break;
case FABI_F64_F64: {
PushRegs();
movsd(xmm0, GetSrc(IROp->Args[0].ID()));
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
movsd(GetDst(Node), xmm0);
}
break;
case FABI_F64_F64_F64: {
PushRegs();
movsd(xmm0, GetSrc(IROp->Args[0].ID()));
movsd(xmm1, GetSrc(IROp->Args[1].ID()));
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
movsd(GetDst(Node), xmm0);
}
break;
case FABI_I16_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
movsx(GetDst<RA_64>(Node), ax);
}
break;
case FABI_I32_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
mov(GetDst<RA_32>(Node), eax);
}
break;
case FABI_I64_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
mov(GetDst<RA_64>(Node), rax);
}
break;
case FABI_I64_F80_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
movq(rdx, GetSrc(IROp->Args[1].ID()));
pextrq(rcx, GetSrc(IROp->Args[1].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
mov(GetDst<RA_64>(Node), rax);
}
break;
case FABI_F80_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
pxor(GetDst(Node), GetDst(Node));
movq(GetDst(Node), rax);
pinsrw(GetDst(Node), edx, 4);
}
break;
case FABI_F80_F80_F80:{
PushRegs();
movq(rdi, GetSrc(IROp->Args[0].ID()));
pextrq(rsi, GetSrc(IROp->Args[0].ID()), 1);
movq(rdx, GetSrc(IROp->Args[1].ID()));
pextrq(rcx, GetSrc(IROp->Args[1].ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
pxor(GetDst(Node), GetDst(Node));
movq(GetDst(Node), rax);
pinsrw(GetDst(Node), edx, 4);
}
break;
case FABI_I32_I64_I64_I128_I128_I16: {
PushRegs();
const auto Op = IROp->C<IR::IROp_VPCMPESTRX>();
const auto Control = Op->Control;
const auto LHS = GetSrc(Op->LHS.ID());
const auto RHS = GetSrc(Op->RHS.ID());
const auto SrcRAX = GetSrc<RA_64>(Op->RAX.ID());
const auto SrcRDX = GetSrc<RA_64>(Op->RDX.ID());
mov(rdi, SrcRAX);
mov(rsi, SrcRDX);
movq(rdx, LHS);
pextrq(rcx, LHS, 1);
movq(r8, RHS);
pextrq(r9, RHS, 1);
sub(rsp, 16);
mov(dword [rsp], Control);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
add(rsp, 16);
PopRegs();
mov(GetDst<RA_32>(Node), rax);
break;
}
case FABI_I32_I128_I128_I16: {
PushRegs();
const auto Op = IROp->C<IR::IROp_VPCMPISTRX>();
const auto LHS = GetSrc(Op->LHS.ID());
const auto RHS = GetSrc(Op->RHS.ID());
const auto Control = Op->Control;
movq(rdi, LHS);
pextrq(rsi, LHS, 1);
movq(rdx, RHS);
pextrq(rcx, RHS, 1);
mov(r8, Control);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
PopRegs();
mov(GetDst<RA_32>(Node), rax);
break;
}
case FABI_UNKNOWN:
default:
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}",
IR::GetName(IROp->Op), ToUnderlying(Info.ABI));
#endif
break;
}
}
}
static uint64_t X86JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame, uint64_t *record) {
auto Thread = Frame->Thread;
auto GuestRip = record[1];
auto HostCode = Thread->LookupCache->FindBlock(GuestRip);
if (!HostCode) {
Thread->CurrentFrame->State.rip = GuestRip;
return Frame->Pointers.Common.DispatcherLoopTop;
}
auto LinkerAddress = Frame->Pointers.Common.ExitFunctionLinker;
Context::ContextImpl::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
// undo the link
record[0] = LinkerAddress;
});
record[0] = HostCode;
return HostCode;
}
void X86JITCore::Op_NoOp(IR::IROp_Header *IROp, IR::NodeID Node) {
}
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} {
RAPass = Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA");
RAPass->AllocateRegisterSet(RegisterClasses);
RAPass->AddRegisters(FEXCore::IR::GPRClass, NumGPRs);
RAPass->AddRegisters(FEXCore::IR::FPRClass, NumXMMs);
RAPass->AddRegisters(FEXCore::IR::GPRPairClass, NumGPRPairs);
for (uint32_t i = 0; i < NumGPRPairs; ++i) {
RAPass->AddRegisterConflict(FEXCore::IR::GPRClass, i * 2, FEXCore::IR::GPRPairClass, i);
RAPass->AddRegisterConflict(FEXCore::IR::GPRClass, i * 2 + 1, FEXCore::IR::GPRPairClass, i);
}
for (uint32_t i = 0; i < FEXCore::IR::IROps::OP_LAST + 1; ++i) {
OpHandlers[i] = &X86JITCore::Op_Unhandled;
}
RegisterALUHandlers();
RegisterAtomicHandlers();
RegisterBranchHandlers();
RegisterConversionHandlers();
RegisterFlagHandlers();
RegisterMemoryHandlers();
RegisterMiscHandlers();
RegisterMoveHandlers();
RegisterVectorHandlers();
RegisterEncryptionHandlers();
{
auto &Common = ThreadState->CurrentFrame->Pointers.Common;
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadRemoveCodeEntryFromJit);
Common.CPUIDObj = reinterpret_cast<uint64_t>(&CTX->CPUID);
{
FEXCore::Utils::MemberFunctionToPointerCast PMF(&FEXCore::CPUIDEmu::RunFunction);
Common.CPUIDFunction = PMF.GetConvertedPointer();
}
{
FEXCore::Utils::MemberFunctionToPointerCast PMF(&FEXCore::CPUIDEmu::RunXCRFunction);
Common.XCRFunction = PMF.GetConvertedPointer();
}
Common.SyscallHandlerObj = reinterpret_cast<uint64_t>(CTX->SyscallHandler);
Common.SyscallHandlerFunc = reinterpret_cast<uint64_t>(FEXCore::Context::HandleSyscall);
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadExitFunctionLink<X86JITCore_ExitFunctionLink>);
// Fill in the fallback handlers
InterpreterOps::FillFallbackIndexPointers(Common.FallbackHandlerPointers);
}
// Must be done after Dispatcher init
ClearCache();
}
X86JITCore::~X86JITCore() {
}
void X86JITCore::EmitDetectionString() {
const char JITString[] = "FEXJIT::X86JITCore::";
for (char c : JITString) {
db(c);
}
}
void X86JITCore::ClearCache() {
auto CodeBuffer = GetEmptyCodeBuffer();
setNewBuffer(CodeBuffer->Ptr, CodeBuffer->Size);
EmitDetectionString();
}
IR::PhysicalRegister X86JITCore::GetPhys(IR::NodeID Node) const {
auto PhyReg = RAData->GetNodeRegister(Node);
LOGMAN_THROW_AA_FMT(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa{}. Class: {}", Node, PhyReg.Class);
return PhyReg;
}
bool X86JITCore::IsFPR(IR::NodeID Node) const {
return RAData->GetNodeRegister(Node).Class == IR::FPRClass.Val;
}
bool X86JITCore::IsGPR(IR::NodeID Node) const {
return RAData->GetNodeRegister(Node).Class == IR::GPRClass.Val;
}
template<uint8_t RAType>
Xbyak::Reg X86JITCore::GetSrc(IR::NodeID Node) const {
// rax, rcx, rdx, rsi, r8, r9,
// r10
// Callee Saved
// rbx, rbp, r12, r13, r14, r15
auto PhyReg = GetPhys(Node);
if constexpr (RAType == RA_64)
return RA64[PhyReg.Reg].cvt64();
else if constexpr (RAType == RA_XMM)
return RAXMM[PhyReg.Reg];
else if constexpr (RAType == RA_32)
return RA64[PhyReg.Reg].cvt32();
else if constexpr (RAType == RA_16)
return RA64[PhyReg.Reg].cvt16();
else if constexpr (RAType == RA_8)
return RA64[PhyReg.Reg].cvt8();
}
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_64>(IR::NodeID Node) const;
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_32>(IR::NodeID Node) const;
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_16>(IR::NodeID Node) const;
template
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_8>(IR::NodeID Node) const;
Xbyak::Xmm X86JITCore::GetSrc(IR::NodeID Node) const {
auto PhyReg = GetPhys(Node);
return RAXMM_x[PhyReg.Reg];
}
template<uint8_t RAType>
Xbyak::Reg X86JITCore::GetDst(IR::NodeID Node) const {
auto PhyReg = GetPhys(Node);
if constexpr (RAType == RA_64)
return RA64[PhyReg.Reg].cvt64();
else if constexpr (RAType == RA_XMM)
return RAXMM[PhyReg.Reg];
else if constexpr (RAType == RA_32)
return RA64[PhyReg.Reg].cvt32();
else if constexpr (RAType == RA_16)
return RA64[PhyReg.Reg].cvt16();
else if constexpr (RAType == RA_8)
return RA64[PhyReg.Reg].cvt8();
}
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_64>(IR::NodeID Node) const;
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_32>(IR::NodeID Node) const;
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_16>(IR::NodeID Node) const;
template
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_8>(IR::NodeID Node) const;
template<uint8_t RAType>
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair(IR::NodeID Node) const {
auto PhyReg = GetPhys(Node);
if constexpr (RAType == RA_64)
return RA64Pair[PhyReg.Reg];
else if constexpr (RAType == RA_32)
return {RA64Pair[PhyReg.Reg].first.cvt32(), RA64Pair[PhyReg.Reg].second.cvt32()};
}
template
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_64>(IR::NodeID Node) const;
template
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_32>(IR::NodeID Node) const;
Xbyak::Xmm X86JITCore::GetDst(IR::NodeID Node) const {
auto PhyReg = GetPhys(Node);
return RAXMM_x[PhyReg.Reg];
}
bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) {
auto Op = OpHeader->C<IR::IROp_InlineConstant>();
if (Value) {
*Value = Op->Constant;
}
return true;
} else {
return false;
}
}
bool X86JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) {
auto Op = OpHeader->C<IR::IROp_InlineEntrypointOffset>();
if (Value) {
uint64_t Mask = ~0ULL;
uint8_t OpSize = OpHeader->Size;
if (OpSize == 4) {
Mask = 0xFFFF'FFFFULL;
}
*Value = (Entry + Op->Offset) & Mask;
}
return true;
} else {
return false;
}
}
std::tuple<X86JITCore::SetCC, X86JITCore::CMovCC, X86JITCore::JCC> X86JITCore::GetCC(IR::CondClassType cond) {
switch (cond.Val) {
case FEXCore::IR::COND_EQ: return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
case FEXCore::IR::COND_NEQ: return { &CodeGenerator::setne, &CodeGenerator::cmovne, &CodeGenerator::jne };
case FEXCore::IR::COND_SGE: return { &CodeGenerator::setge, &CodeGenerator::cmovge, &CodeGenerator::jge };
case FEXCore::IR::COND_SLT: return { &CodeGenerator::setl , &CodeGenerator::cmovl , &CodeGenerator::jl };
case FEXCore::IR::COND_SGT: return { &CodeGenerator::setg , &CodeGenerator::cmovg , &CodeGenerator::jg };
case FEXCore::IR::COND_SLE: return { &CodeGenerator::setle, &CodeGenerator::cmovle, &CodeGenerator::jle };
case FEXCore::IR::COND_UGE: return { &CodeGenerator::setae, &CodeGenerator::cmovae, &CodeGenerator::jae };
case FEXCore::IR::COND_ULT: return { &CodeGenerator::setb , &CodeGenerator::cmovb , &CodeGenerator::jb };
case FEXCore::IR::COND_UGT: return { &CodeGenerator::seta , &CodeGenerator::cmova , &CodeGenerator::ja };
case FEXCore::IR::COND_ULE: return { &CodeGenerator::setna, &CodeGenerator::cmovna, &CodeGenerator::jna };
case FEXCore::IR::COND_FLU: return { &CodeGenerator::setb , &CodeGenerator::cmovb , &CodeGenerator::jb };
case FEXCore::IR::COND_FGE: return { &CodeGenerator::setae, &CodeGenerator::cmovae, &CodeGenerator::jae };
case FEXCore::IR::COND_FLEU: return { &CodeGenerator::setna, &CodeGenerator::cmovna, &CodeGenerator::jna };
case FEXCore::IR::COND_FGT: return { &CodeGenerator::seta , &CodeGenerator::cmova , &CodeGenerator::ja };
case FEXCore::IR::COND_FU: return { &CodeGenerator::setp , &CodeGenerator::cmovp , &CodeGenerator::jp };
case FEXCore::IR::COND_FNU: return { &CodeGenerator::setnp, &CodeGenerator::cmovnp, &CodeGenerator::jnp };
case FEXCore::IR::COND_MI:
case FEXCore::IR::COND_PL:
case FEXCore::IR::COND_VS:
case FEXCore::IR::COND_VC:
default:
LOGMAN_MSG_A_FMT("Unsupported compare type");
break;
}
// Hope for the best
return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
}
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();
uint32_t SSACount = IR->GetSSACount();
this->Entry = Entry;
this->RAData = RAData;
this->DebugData = DebugData;
// Fairly excessive buffer range to make sure we don't overflow
uint32_t BufferRange = SSACount * 16 + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
if ((getSize() + BufferRange) > CurrentCodeBuffer->Size) {
CTX->ClearCodeCache(ThreadState);
}
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(CodeData.BlockBegin, Entry);
setSize(getSize() + GDBSize);
}
LOGMAN_THROW_AA_FMT(RAData != nullptr, "Needs RA");
SpillSlots = RAData->SpillSlots();
if (SpillSlots) {
sub(rsp, SpillSlots * MaxSpillSlotSize);
}
#ifdef BLOCKSTATS
BlockSamplingData::BlockData *SamplingData = CTX->BlockData->GetBlockData(Entry);
if (GetSamplingData) {
mov(rcx, reinterpret_cast<uintptr_t>(SamplingData));
rdtsc();
shl(rdx, 32);
or_(rax, rdx);
mov(qword [rcx + offsetof(BlockSamplingData::BlockData, Start)], rax);
}
auto ExitBlock = [&]() {
if (GetSamplingData) {
mov(rcx, reinterpret_cast<uintptr_t>(SamplingData));
// Get time
rdtsc();
shl(rdx, 32);
or_(rax, rdx);
// Calculate time spent in block
mov(rdx, qword [rcx + offsetof(BlockSamplingData::BlockData, Start)]);
sub(rax, rdx);
// Add time to total time
add(qword [rcx + offsetof(BlockSamplingData::BlockData, TotalTime)], rax);
// Increment call count
inc(qword [rcx + offsetof(BlockSamplingData::BlockData, TotalCalls)]);
// Calculate min
mov(rdx, qword [rcx + offsetof(BlockSamplingData::BlockData, Min)]);
cmp(rdx, rax);
cmova(rdx, rax);
mov(qword [rcx + offsetof(BlockSamplingData::BlockData, Min)], rdx);
// Calculate max
mov(rdx, qword [rcx + offsetof(BlockSamplingData::BlockData, Max)]);
cmp(rdx, rax);
cmovb(rdx, rax);
mov(qword [rcx + offsetof(BlockSamplingData::BlockData, Max)], rdx);
}
};
#endif
PendingTargetLabel = nullptr;
for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) {
using namespace FEXCore::IR;
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
#endif
auto BlockStartHostCode = getCurr<uint8_t *>();
{
const auto Node = IR->GetID(BlockNode);
const auto IsTarget = JumpTargets.try_emplace(Node).first;
// if there is a pending branch, and it is not fall-through
if (PendingTargetLabel && PendingTargetLabel != &IsTarget->second) {
jmp(*PendingTargetLabel, T_NEAR);
}
PendingTargetLabel = nullptr;
L(IsTarget->second);
}
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
#ifdef DEBUG_RA
if (IROp->Op != IR::OP_BEGINBLOCK &&
IROp->Op != IR::OP_CONDJUMP &&
IROp->Op != IR::OP_JUMP) {
fextl::stringstream Inst;
auto Name = FEXCore::IR::GetName(IROp->Op);
if (IROp->HasDest) {
uint64_t PhysReg = RAPass->GetNodeRegister(Node);
if (PhysReg >= GPRPairBase)
Inst << "\tPair" << GetPhys(Node) << " = " << Name << " ";
else if (PhysReg >= XMMBase)
Inst << "\tXMM" << GetPhys(Node) << " = " << Name << " ";
else
Inst << "\tReg" << GetPhys(Node) << " = " << Name << " ";
}
else {
Inst << "\t" << Name << " ";
}
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
for (uint8_t i = 0; i < NumArgs; ++i) {
const auto ArgNode = IROp->Args[i].ID();
const uint64_t PhysReg = RAPass->GetNodeRegister(ArgNode);
if (PhysReg >= GPRPairBase)
Inst << "Pair" << GetPhys(ArgNode) << (i + 1 == NumArgs ? "" : ", ");
else if (PhysReg >= XMMBase)
Inst << "XMM" << GetPhys(ArgNode) << (i + 1 == NumArgs ? "" : ", ");
else
Inst << "Reg" << GetPhys(ArgNode) << (i + 1 == NumArgs ? "" : ", ");
}
LogMan::Msg::DFmt("{}", Inst.str());
}
#endif
const auto ID = IR->GetID(CodeNode);
// Execute handler
OpHandler Handler = OpHandlers[IROp->Op];
(this->*Handler)(IROp, ID);
}
if (DebugData) {
DebugData->Subblocks.push_back({
static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockBegin),
static_cast<uint32_t>(getCurr<uint8_t *>() - BlockStartHostCode)
});
}
}
// Make sure last branch is generated. It certainly can't be eliminated here.
if (PendingTargetLabel)
{
jmp(*PendingTargetLabel, T_NEAR);
}
PendingTargetLabel = nullptr;
// Add the JitCodeTail
auto JITBlockTailLocation = getCurr<uint8_t *>();
auto JITBlockTail = getCurr<JITCodeTail*>();
setSize(getSize() + sizeof(JITCodeTail));
auto JITRIPEntriesLocation = getCurr<uint8_t *>();
auto JITRIPEntries = getCurr<JITRIPReconstructEntries*>();
setSize(getSize() + sizeof(JITRIPReconstructEntries) * DebugData->GuestOpcodes.size());
// 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;
{
// Store the RIP entries.
JITBlockTail->NumberOfRIPEntries = DebugData->GuestOpcodes.size();
JITBlockTail->OffsetToRIPEntries = JITRIPEntriesLocation - JITBlockTailLocation;
uintptr_t CurrentRIPOffset = 0;
uint64_t CurrentPCOffset = 0;
for (size_t i = 0; i < DebugData->GuestOpcodes.size(); i++) {
const auto &GuestOpcode = DebugData->GuestOpcodes[i];
auto &RIPEntry = JITRIPEntries[i];
RIPEntry.HostPCOffset = GuestOpcode.HostEntryOffset - CurrentPCOffset;
RIPEntry.GuestRIPOffset = GuestOpcode.GuestEntryOffset - CurrentRIPOffset;
CurrentPCOffset = GuestOpcode.HostEntryOffset;
CurrentRIPOffset = GuestOpcode.GuestEntryOffset;
}
}
CodeHeader->OffsetToBlockTail = JITBlockTailLocation - CodeData.BlockBegin;
CodeData.Size = getCurr<uint8_t*>() - CodeData.BlockBegin;
JITBlockTail->Size = CodeData.Size;
this->IR = nullptr;
ready();
if (DebugData) {
DebugData->HostCodeSize = CodeData.Size;
DebugData->Relocations = &Relocations;
}
return CodeData;
}
fextl::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
return fextl::make_unique<X86JITCore>(ctx, Thread);
}
CPUBackendFeatures GetX86JITBackendFeatures() {
return CPUBackendFeatures { };
}
}
@@ -1,471 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#pragma once
#include <FEXCore/IR/RegisterAllocationData.h>
#include "Interface/Core/BlockSamplingData.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/ObjectCache/Relocations.h"
using namespace Xbyak;
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/CPUBackend.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/unordered_map.h>
#include <FEXCore/fextl/vector.h>
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include <tuple>
namespace FEXCore::CPU {
// Temp registers
// rax, rcx, rdx, rsi, r8, r9,
// r10, r11
//
// Callee Saved
// rbx, rbp, r12, r13, r14, r15
//
// 1St Argument: rdi <ThreadState>
// XMM:
// All temp
#define STATE r14
#define TMP1 rax
#define TMP2 rcx
#define TMP3 rdx
#define TMP4 rdi
#define TMP5 rbx
using namespace Xbyak::util;
const std::array<Xbyak::Reg, 9> RA64 = { rsi, r8, r9, r10, r11, rbp, r12, r13, r15 };
const std::array<std::pair<Xbyak::Reg, Xbyak::Reg>, 4> RA64Pair = {{ {rsi, r8}, {r9, r10}, {r11, rbp}, {r12, r13} }};
const std::array<Xbyak::Reg, 11> RAXMM = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
const std::array<Xbyak::Xmm, 11> RAXMM_x = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
class X86JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
public:
explicit X86JITCore(FEXCore::Context::ContextImpl *ctx,
FEXCore::Core::InternalThreadState *Thread);
~X86JITCore() override;
[[nodiscard]] fextl::string GetName() override { return "JIT"; }
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
FEXCore::IR::IRListView const *IR,
FEXCore::Core::DebugData *DebugData,
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
[[nodiscard]] void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
[[nodiscard]] bool NeedsOpDispatch() override { return true; }
void ClearCache() override;
void ClearRelocations() override { Relocations.clear(); }
private:
/**
* @name Relocations
* @{ */
uint64_t GetNamedSymbolLiteral(FEXCore::CPU::RelocNamedSymbolLiteral::NamedSymbol Op);
void LoadConstantWithPadding(Xbyak::Reg Reg, uint64_t Constant);
/**
* @brief A literal pair relocation object for named symbol literals
*/
struct NamedSymbolLiteralPair {
Label Offset;
Relocation MoveABI{};
};
/**
* @brief Inserts a thunk relocation
*
* @param Reg - The GPR to move the thunk handler in to
* @param Sum - The hash of the thunk
*/
void InsertNamedThunkRelocation(Xbyak::Reg Reg, const IR::SHA256Sum &Sum);
/**
* @brief Inserts a guest GPR move relocation
*
* @param Reg - The GPR to move the guest RIP in to
* @param Constant - The guest RIP that will be relocated
*/
void InsertGuestRIPMove(Xbyak::Reg Reg, uint64_t Constant);
/**
* @brief Inserts a named symbol as a literal in memory
*
* Need to use `PlaceNamedSymbolLiteral` with the return value to place the literal in the desired location
*
* @param Op The named symbol to place
*
* @return A temporary `NamedSymbolLiteralPair`
*/
NamedSymbolLiteralPair InsertNamedSymbolLiteral(FEXCore::CPU::RelocNamedSymbolLiteral::NamedSymbol Op);
/**
* @brief Place the named symbol literal relocation in memory
*
* @param Lit - Which literal to place
*/
void PlaceNamedSymbolLiteral(NamedSymbolLiteralPair &Lit);
fextl::vector<FEXCore::CPU::Relocation> Relocations;
///< Relocation code loading
bool ApplyRelocations(uint64_t GuestEntry, uint64_t CodeEntry, uint64_t CursorEntry, size_t NumRelocations, const char* EntryRelocations);
/**
* @brief Current guest RIP entrypoint
*/
uint64_t CursorEntry{};
/** @} */
Label* PendingTargetLabel{};
FEXCore::Context::ContextImpl *CTX;
FEXCore::IR::IRListView const *IR;
uint64_t Entry;
CPUBackend::CompiledCode CodeData{};
fextl::unordered_map<IR::NodeID, Label> JumpTargets;
Xbyak::util::Cpu Features{};
bool MemoryDebug = false;
/**
* @name Register Allocation
* @{ */
constexpr static uint32_t NumGPRs = RA64.size(); // 4 is the minimum required for GPR ops
constexpr static uint32_t NumXMMs = RAXMM.size();
constexpr static uint32_t NumGPRPairs = RA64Pair.size();
constexpr static uint32_t RegisterClasses = 6;
constexpr static uint64_t GPRBase = (0ULL << 32);
constexpr static uint64_t XMMBase = (1ULL << 32);
constexpr static uint64_t GPRPairBase = (2ULL << 32);
/** @} */
constexpr static uint8_t RA_8 = 0;
constexpr static uint8_t RA_16 = 1;
constexpr static uint8_t RA_32 = 2;
constexpr static uint8_t RA_64 = 3;
constexpr static uint8_t RA_XMM = 4;
[[nodiscard]] IR::PhysicalRegister GetPhys(IR::NodeID Node) const;
[[nodiscard]] bool IsFPR(IR::NodeID Node) const;
[[nodiscard]] bool IsGPR(IR::NodeID Node) const;
template<uint8_t RAType>
[[nodiscard]] Xbyak::Reg GetSrc(IR::NodeID Node) const;
template<uint8_t RAType>
[[nodiscard]] std::pair<Xbyak::Reg, Xbyak::Reg> GetSrcPair(IR::NodeID Node) const;
template<uint8_t RAType>
[[nodiscard]] Xbyak::Reg GetDst(IR::NodeID Node) const;
[[nodiscard]] Xbyak::Xmm GetSrc(IR::NodeID Node) const;
[[nodiscard]] Xbyak::Xmm GetDst(IR::NodeID Node) const;
[[nodiscard]] static Xbyak::Ymm ToYMM(const Xbyak::Xmm& xmm) {
return Xbyak::Ymm{xmm.getIdx()};
}
[[nodiscard]] Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset,
IR::MemOffsetType OffsetType, uint8_t OffsetScale) const;
[[nodiscard]] bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr) const;
[[nodiscard]] bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const;
IR::RegisterAllocationPass *RAPass;
FEXCore::IR::RegisterAllocationData *RAData;
FEXCore::Core::DebugData *DebugData;
#ifdef BLOCKSTATS
bool GetSamplingData {true};
#endif
static uint64_t ExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame, uint64_t *record);
// This is purely a debugging aid for developers to see if they are in JIT code space when inspecting raw memory
void EmitDetectionString();
uint32_t SpillSlots{};
using SetCC = void (X86JITCore::*)(const Operand& op);
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
using JCC = void (X86JITCore::*)(const Label& label, LabelType type);
std::tuple<SetCC, CMovCC, JCC> GetCC(IR::CondClassType cond);
using OpHandler = void (X86JITCore::*)(IR::IROp_Header *IROp, IR::NodeID Node);
std::array<OpHandler, IR::IROps::OP_LAST + 1> OpHandlers {};
void RegisterALUHandlers();
void RegisterAtomicHandlers();
void RegisterBranchHandlers();
void RegisterConversionHandlers();
void RegisterFlagHandlers();
void RegisterMemoryHandlers();
void RegisterMiscHandlers();
void RegisterMoveHandlers();
void RegisterVectorHandlers();
void RegisterEncryptionHandlers();
void PushRegs();
void PopRegs();
#define DEF_OP(x) void Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
///< Unhandled handler
DEF_OP(Unhandled);
///< No-op Handler
DEF_OP(NoOp);
///< ALU Ops
DEF_OP(TruncElementPair);
DEF_OP(Constant);
DEF_OP(EntrypointOffset);
DEF_OP(InlineConstant);
DEF_OP(InlineEntrypointOffset);
DEF_OP(CycleCounter);
DEF_OP(Add);
DEF_OP(Sub);
DEF_OP(Neg);
DEF_OP(Mul);
DEF_OP(UMul);
DEF_OP(Div);
DEF_OP(UDiv);
DEF_OP(Rem);
DEF_OP(URem);
DEF_OP(MulH);
DEF_OP(UMulH);
DEF_OP(Or);
DEF_OP(And);
DEF_OP(Andn);
DEF_OP(Xor);
DEF_OP(Lshl);
DEF_OP(Lshr);
DEF_OP(Ashr);
DEF_OP(Rol);
DEF_OP(Ror);
DEF_OP(Extr);
DEF_OP(PDep);
DEF_OP(PExt);
DEF_OP(LDiv);
DEF_OP(LUDiv);
DEF_OP(LRem);
DEF_OP(LURem);
DEF_OP(Zext);
DEF_OP(Not);
DEF_OP(Popcount);
DEF_OP(FindLSB);
DEF_OP(FindMSB);
DEF_OP(FindTrailingZeros);
DEF_OP(CountLeadingZeroes);
DEF_OP(Rev);
DEF_OP(Bfi);
DEF_OP(Bfe);
DEF_OP(Sbfe);
DEF_OP(Select);
DEF_OP(VExtractToGPR);
DEF_OP(Float_ToGPR_ZS);
DEF_OP(Float_ToGPR_S);
DEF_OP(FCmp);
DEF_OP(F80Cmp);
///< Atomic ops
DEF_OP(CASPair);
DEF_OP(CAS);
DEF_OP(AtomicAdd);
DEF_OP(AtomicSub);
DEF_OP(AtomicAnd);
DEF_OP(AtomicOr);
DEF_OP(AtomicXor);
DEF_OP(AtomicSwap);
DEF_OP(AtomicFetchAdd);
DEF_OP(AtomicFetchSub);
DEF_OP(AtomicFetchAnd);
DEF_OP(AtomicFetchOr);
DEF_OP(AtomicFetchXor);
DEF_OP(AtomicFetchNeg);
///< Branch ops
DEF_OP(CallbackReturn);
DEF_OP(ExitFunction);
DEF_OP(Jump);
DEF_OP(CondJump);
DEF_OP(Syscall);
DEF_OP(Thunk);
DEF_OP(ValidateCode);
DEF_OP(ThreadRemoveCodeEntry);
DEF_OP(CPUID);
DEF_OP(XGETBV);
///< Conversion ops
DEF_OP(VInsGPR);
DEF_OP(VCastFromGPR);
DEF_OP(VDupFromGPR);
DEF_OP(Float_FromGPR_S);
DEF_OP(Float_FToF);
DEF_OP(Vector_UToF);
DEF_OP(Vector_SToF);
DEF_OP(Vector_FToZS);
DEF_OP(Vector_FToS);
DEF_OP(Vector_FToF);
DEF_OP(Vector_FToI);
///< Flag ops
DEF_OP(GetHostFlag);
///< Memory ops
DEF_OP(LoadContext);
DEF_OP(StoreContext);
DEF_OP(LoadRegister);
DEF_OP(StoreRegister);
DEF_OP(LoadContextIndexed);
DEF_OP(StoreContextIndexed);
DEF_OP(SpillRegister);
DEF_OP(FillRegister);
DEF_OP(LoadFlag);
DEF_OP(StoreFlag);
DEF_OP(LoadMem);
DEF_OP(StoreMem);
DEF_OP(VLoadVectorMasked);
DEF_OP(VStoreVectorMasked);
DEF_OP(MemSet);
DEF_OP(MemCpy);
DEF_OP(CacheLineClear);
DEF_OP(CacheLineClean);
DEF_OP(CacheLineZero);
///< Misc ops
DEF_OP(GuestOpcode);
DEF_OP(Fence);
DEF_OP(Break);
DEF_OP(Phi);
DEF_OP(PhiValue);
DEF_OP(Print);
DEF_OP(GetRoundingMode);
DEF_OP(SetRoundingMode);
DEF_OP(ProcessorID);
DEF_OP(RDRAND);
DEF_OP(Yield);
///< Move ops
DEF_OP(ExtractElementPair);
DEF_OP(CreateElementPair);
///< Vector ops
DEF_OP(VectorZero);
DEF_OP(VectorImm);
DEF_OP(VMov);
DEF_OP(VAnd);
DEF_OP(VBic);
DEF_OP(VOr);
DEF_OP(VXor);
DEF_OP(VAdd);
DEF_OP(VSub);
DEF_OP(VUQAdd);
DEF_OP(VUQSub);
DEF_OP(VSQAdd);
DEF_OP(VSQSub);
DEF_OP(VAddP);
DEF_OP(VAddV);
DEF_OP(VUMinV);
DEF_OP(VURAvg);
DEF_OP(VAbs);
DEF_OP(VPopcount);
DEF_OP(VFAdd);
DEF_OP(VFAddP);
DEF_OP(VFSub);
DEF_OP(VFMul);
DEF_OP(VFDiv);
DEF_OP(VFMin);
DEF_OP(VFMax);
DEF_OP(VFRecp);
DEF_OP(VFSqrt);
DEF_OP(VFRSqrt);
DEF_OP(VNeg);
DEF_OP(VFNeg);
DEF_OP(VNot);
DEF_OP(VUMin);
DEF_OP(VSMin);
DEF_OP(VUMax);
DEF_OP(VSMax);
DEF_OP(VZip);
DEF_OP(VZip2);
DEF_OP(VUnZip);
DEF_OP(VUnZip2);
DEF_OP(VTrn);
DEF_OP(VTrn2);
DEF_OP(VBSL);
DEF_OP(VCMPEQ);
DEF_OP(VCMPEQZ);
DEF_OP(VCMPGT);
DEF_OP(VCMPGTZ);
DEF_OP(VCMPLTZ);
DEF_OP(VFCMPEQ);
DEF_OP(VFCMPNEQ);
DEF_OP(VFCMPLT);
DEF_OP(VFCMPGT);
DEF_OP(VFCMPLE);
DEF_OP(VFCMPORD);
DEF_OP(VFCMPUNO);
DEF_OP(VUShl);
DEF_OP(VUShr);
DEF_OP(VSShr);
DEF_OP(VUShlS);
DEF_OP(VUShrS);
DEF_OP(VSShrS);
DEF_OP(VInsElement);
DEF_OP(VDupElement);
DEF_OP(VExtr);
DEF_OP(VUShrI);
DEF_OP(VSShrI);
DEF_OP(VShlI);
DEF_OP(VUShrNI);
DEF_OP(VUShrNI2);
DEF_OP(VSXTL);
DEF_OP(VSXTL2);
DEF_OP(VUXTL);
DEF_OP(VUXTL2);
DEF_OP(VSQXTN);
DEF_OP(VSQXTN2);
DEF_OP(VSQXTUN);
DEF_OP(VSQXTUN2);
DEF_OP(VMul);
DEF_OP(VUMull);
DEF_OP(VSMull);
DEF_OP(VUMull2);
DEF_OP(VSMull2);
DEF_OP(VUABDL);
DEF_OP(VTBL1);
DEF_OP(VRev64);
///< Encryption ops
DEF_OP(AESImc);
DEF_OP(AESEnc);
DEF_OP(AESEncLast);
DEF_OP(AESDec);
DEF_OP(AESDecLast);
DEF_OP(AESKeyGenAssist);
DEF_OP(CRC32);
DEF_OP(PCLMUL);
#undef DEF_OP
};
}
File diff suppressed because it is too large. Load diff
@@ -1,191 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "FEXCore/Debug/InternalThreadState.h"
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/IR/IR.h>
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(GuestOpcode) {
auto Op = IROp->C<IR::IROp_GuestOpcode>();
// metadata
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, getCurr<uint8_t*>() - CodeData.BlockBegin});
}
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
switch (Op->Fence) {
case IR::Fence_Load.Val:
lfence();
break;
case IR::Fence_LoadStore.Val:
mfence();
break;
case IR::Fence_Store.Val:
sfence();
break;
default: LOGMAN_MSG_A_FMT("Unknown Fence: {}", Op->Fence); break;
}
}
#ifndef _WIN32
DEF_OP(Break) {
auto Op = IROp->C<IR::IROp_Break>();
if (SpillSlots) {
add(rsp, SpillSlots * MaxSpillSlotSize);
}
Core::CpuStateFrame::SynchronousFaultDataStruct State = {
.FaultToTopAndGeneratedException = 1,
.Signal = Op->Reason.Signal,
.TrapNo = Op->Reason.TrapNumber,
.si_code = Op->Reason.si_code,
.err_code = Op->Reason.ErrorRegister,
};
uint64_t Constant{};
memcpy(&Constant, &State, sizeof(State));
mov(TMP1, Constant);
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData)], TMP1);
switch (Op->Reason.Signal) {
case SIGILL:
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGILL)]);
break;
case SIGTRAP:
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP)]);
break;
case SIGSEGV:
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGSEGV)]);
break;
default:
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP)]);
break;
}
}
#else
DEF_OP(Break) {
ERROR_AND_DIE_FMT("Unsupported");
}
#endif
DEF_OP(GetRoundingMode) {
auto Dst = GetDst<RA_32>(Node);
sub(rsp, 4);
// Only stores to memory
stmxcsr(dword [rsp]);
mov(Dst, dword [rsp]);
add(rsp, 4);
shr(Dst, 13);
}
DEF_OP(SetRoundingMode) {
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
auto Src = GetSrc<RA_32>(Op->RoundMode.ID());
// Load old mxcsr
// Only stores to memory
sub(rsp, 4);
stmxcsr(dword [rsp]);
mov(TMP1.cvt32(), dword [rsp]);
// Insert the new rounding mode
and_(TMP1.cvt32(), ~(0b111 << 13));
mov(TMP2.cvt32(), Src);
shl(TMP2.cvt32(), 13);
or_(TMP1.cvt32(), TMP2.cvt32());
// Store it to mxcsr
// Only loads from memory
mov(dword [rsp], TMP1.cvt32());
ldmxcsr(dword [rsp]);
add(rsp, 4);
}
DEF_OP(Print) {
auto Op = IROp->C<IR::IROp_Print>();
PushRegs();
if (IsGPR(Op->Value.ID())) {
mov (rdi, GetSrc<RA_64>(Op->Value.ID()));
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.PrintValue)]);
}
else {
pextrq(rdi, GetSrc(Op->Value.ID()), 0);
pextrq(rsi, GetSrc(Op->Value.ID()), 1);
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.PrintVectorValue)]);
}
PopRegs();
}
DEF_OP(ProcessorID) {
// Cyclecounter in EDX:EAX
// IA32_TSC_AUX in ECX
rdtscp();
mov (GetDst<RA_32>(Node), ecx);
}
DEF_OP(RDRAND) {
auto Op = IROp->C<IR::IROp_RDRAND>();
auto Dst = GetSrcPair<RA_64>(Node);
if (Op->GetReseeded) {
rdrand(Dst.first);
}
else {
rdseed(Dst.first);
}
// In the case of RDRAND or RDSEED returning a valid number then CF = 1, else 0
mov (Dst.second, 0);
setc(Dst.second.cvt8());
}
DEF_OP(Yield) {
pause();
}
#undef DEF_OP
void X86JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(DUMMY, NoOp);
REGISTER_OP(IRHEADER, NoOp);
REGISTER_OP(CODEBLOCK, NoOp);
REGISTER_OP(BEGINBLOCK, NoOp);
REGISTER_OP(ENDBLOCK, NoOp);
REGISTER_OP(GUESTOPCODE, GuestOpcode);
REGISTER_OP(FENCE, Fence);
REGISTER_OP(BREAK, Break);
REGISTER_OP(PHI, NoOp);
REGISTER_OP(PHIVALUE, NoOp);
REGISTER_OP(PRINT, Print);
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
REGISTER_OP(INVALIDATEFLAGS, NoOp);
REGISTER_OP(PROCESSORID, ProcessorID);
REGISTER_OP(RDRAND, RDRAND);
REGISTER_OP(YIELD, Yield);
#undef REGISTER_OP
}
}
@@ -1,84 +0,0 @@
/*
$info$
tags: backend|x86-64
$end_info$
*/
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/IR/IR.h>
#include <array>
#include <stdint.h>
#include <utility>
namespace FEXCore::CPU {
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
DEF_OP(ExtractElementPair) {
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
switch (Op->Header.Size) {
case 4: {
auto Src = GetSrcPair<RA_32>(Op->Pair.ID());
std::array<Xbyak::Reg, 2> Regs = {Src.first, Src.second};
mov (GetDst<RA_32>(Node), Regs[Op->Element]);
break;
}
case 8: {
auto Src = GetSrcPair<RA_64>(Op->Pair.ID());
std::array<Xbyak::Reg, 2> Regs = {Src.first, Src.second};
mov (GetDst<RA_64>(Node), Regs[Op->Element]);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown Size"); break;
}
}
DEF_OP(CreateElementPair) {
auto Op = IROp->C<IR::IROp_CreateElementPair>();
std::pair<Xbyak::Reg, Xbyak::Reg> Dst;
Xbyak::Reg RegFirst;
Xbyak::Reg RegSecond;
Xbyak::Reg RegTmp;
switch (IROp->ElementSize) {
case 4: {
Dst = GetSrcPair<RA_32>(Node);
RegFirst = GetSrc<RA_32>(Op->Lower.ID());
RegSecond = GetSrc<RA_32>(Op->Upper.ID());
RegTmp = eax;
break;
}
case 8: {
Dst = GetSrcPair<RA_64>(Node);
RegFirst = GetSrc<RA_64>(Op->Lower.ID());
RegSecond = GetSrc<RA_64>(Op->Upper.ID());
RegTmp = rax;
break;
}
default: LOGMAN_MSG_A_FMT("Unknown Size"); break;
}
if (Dst.first != RegSecond) {
mov(Dst.first, RegFirst);
mov(Dst.second, RegSecond);
} else if (Dst.second != RegFirst) {
mov(Dst.second, RegSecond);
mov(Dst.first, RegFirst);
} else {
mov(RegTmp, RegFirst);
mov(Dst.second, RegSecond);
mov(Dst.first, RegTmp);
}
}
#undef DEF_OP
void X86JITCore::RegisterMoveHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
#undef REGISTER_OP
}
}
File diff suppressed because it is too large. Load diff
@@ -1,139 +0,0 @@
/*
$info$
tags: backend|x86-64
desc: relocation logic of the x86-64 splatter backend
$end_info$
*/
#include "Interface/Context/Context.h"
#include "Interface/Core/JIT/x86_64/JITClass.h"
#include "Interface/HLE/Thunks/Thunks.h"
namespace FEXCore::CPU {
uint64_t X86JITCore::GetNamedSymbolLiteral(FEXCore::CPU::RelocNamedSymbolLiteral::NamedSymbol Op) {
switch (Op) {
case FEXCore::CPU::RelocNamedSymbolLiteral::NamedSymbol::SYMBOL_LITERAL_EXITFUNCTION_LINKER:
return ThreadState->CurrentFrame->Pointers.Common.ExitFunctionLinker;
break;
default:
ERROR_AND_DIE_FMT("Unknown named symbol literal: {}", static_cast<uint32_t>(Op));
break;
}
return ~0ULL;
}
void X86JITCore::LoadConstantWithPadding(Xbyak::Reg Reg, uint64_t Constant) {
// The maximum size a move constant can be in bytes
// Need to NOP pad to this size to ensure backpatching is always the same size
// Calculated as:
// [Rex]
// [Mov op]
// [8 byte constant]
//
// All other move types are smaller than this. xbyak will use a NOP slide which is quite quick
constexpr static size_t MAX_MOVE_SIZE = 10;
auto StartingOffset = getSize();
mov(Reg, Constant);
auto MoveSize = getSize() - StartingOffset;
auto NOPPadSize = MAX_MOVE_SIZE - MoveSize;
nop(NOPPadSize);
}
X86JITCore::NamedSymbolLiteralPair X86JITCore::InsertNamedSymbolLiteral(FEXCore::CPU::RelocNamedSymbolLiteral::NamedSymbol Op) {
NamedSymbolLiteralPair Lit {
.MoveABI = {
.NamedSymbolLiteral = {
.Header = {
.Type = FEXCore::CPU::RelocationTypes::RELOC_NAMED_SYMBOL_LITERAL,
},
.Symbol = Op,
.Offset = 0,
},
},
};
return Lit;
}
void X86JITCore::PlaceNamedSymbolLiteral(NamedSymbolLiteralPair &Lit) {
// Offset is the offset from the entrypoint of the block
auto CurrentCursor = getSize();
Lit.MoveABI.NamedSymbolLiteral.Offset = CurrentCursor - CursorEntry;
uint64_t Pointer = GetNamedSymbolLiteral(Lit.MoveABI.NamedSymbolLiteral.Symbol);
L(Lit.Offset);
dq(Pointer);
Relocations.emplace_back(Lit.MoveABI);
}
void X86JITCore::InsertGuestRIPMove(Xbyak::Reg Reg, uint64_t Constant) {
Relocation MoveABI{};
MoveABI.GuestRIPMove.Header.Type = FEXCore::CPU::RelocationTypes::RELOC_GUEST_RIP_MOVE;
// Offset is the offset from the entrypoint of the block
auto CurrentCursor = getSize();
MoveABI.GuestRIPMove.Offset = CurrentCursor - CursorEntry;
MoveABI.GuestRIPMove.GuestRIP = Constant;
MoveABI.GuestRIPMove.RegisterIndex = Reg.getIdx();
if (CTX->Config.CacheObjectCodeCompilation()) {
LoadConstantWithPadding(Reg, Constant);
}
else {
mov(Reg, Constant);
}
Relocations.emplace_back(MoveABI);
}
bool X86JITCore::ApplyRelocations(uint64_t GuestEntry, uint64_t CodeEntry, uint64_t CursorEntry, size_t NumRelocations, const char* EntryRelocations) {
size_t DataIndex{};
for (size_t j = 0; j < NumRelocations; ++j) {
const FEXCore::CPU::Relocation *Reloc = reinterpret_cast<const FEXCore::CPU::Relocation *>(&EntryRelocations[DataIndex]);
LOGMAN_THROW_AA_FMT((DataIndex % alignof(Relocation)) == 0, "Alignment of relocation wasn't adhered to");
switch (Reloc->Header.Type) {
case FEXCore::CPU::RelocationTypes::RELOC_NAMED_SYMBOL_LITERAL: {
uint64_t Pointer = GetNamedSymbolLiteral(Reloc->NamedSymbolLiteral.Symbol);
// Relocation occurs at the cursorEntry + offset relative to that cursor.
setSize(CursorEntry + Reloc->NamedSymbolLiteral.Offset);
// Place the pointer
dq(Pointer);
DataIndex += sizeof(Reloc->NamedSymbolLiteral);
break;
}
case FEXCore::CPU::RelocationTypes::RELOC_NAMED_THUNK_MOVE: {
uint64_t Pointer = reinterpret_cast<uint64_t>(CTX->ThunkHandler->LookupThunk(Reloc->NamedThunkMove.Symbol));
if (Pointer == ~0ULL) {
return false;
}
// Relocation occurs at the cursorEntry + offset relative to that cursor.
setSize(CursorEntry + Reloc->NamedThunkMove.Offset);
LoadConstantWithPadding(Xbyak::Reg64(Reloc->NamedThunkMove.RegisterIndex), Pointer);
DataIndex += sizeof(Reloc->NamedThunkMove);
break;
}
case FEXCore::CPU::RelocationTypes::RELOC_GUEST_RIP_MOVE:
// XXX: Reenable once the JIT Object Cache is upstream
// XXX: Should spin the relocation list, create a list of guest RIP moves, and ask for them all once, reduces lock contention.
uint64_t Pointer = ~0ULL; // EmitterCTX->JITObjectCache->FindRelocatedRIP(Reloc->GuestRIPMove.GuestRIP);
if (Pointer == ~0ULL) {
return false;
}
// Relocation occurs at the cursorEntry + offset relative to that cursor.
setSize(CursorEntry + Reloc->GuestRIPMove.Offset);
LoadConstantWithPadding(Xbyak::Reg64(Reloc->GuestRIPMove.RegisterIndex), Pointer);
DataIndex += sizeof(Reloc->GuestRIPMove);
break;
}
}
return true;
}
}
File diff suppressed because it is too large. Load diff
@@ -1,60 +0,0 @@
#include <FEXCore/Core/SignalDelegator.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXHeaderUtils/Syscalls.h>
#include <unistd.h>
#include <signal.h>
namespace FEXCore {
struct ThreadState {
FEXCore::Core::InternalThreadState *Thread{};
};
thread_local ThreadState ThreadData{};
FEXCore::Core::InternalThreadState *SignalDelegator::GetTLSThread() {
return ThreadData.Thread;
}
void SignalDelegator::RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) {
ThreadData.Thread = Thread;
RegisterFrontendTLSState(Thread);
}
void SignalDelegator::UninstallTLSState(FEXCore::Core::InternalThreadState *Thread) {
UninstallFrontendTLSState(Thread);
ThreadData.Thread = nullptr;
}
void SignalDelegator::RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
SetHostSignalHandler(Signal, Func, Required);
FrontendRegisterHostSignalHandler(Signal, Func, Required);
}
void SignalDelegator::HandleSignal(int Signal, void *Info, void *UContext) {
// Let the host take first stab at handling the signal
auto Thread = GetTLSThread();
HostSignalHandler &Handler = HostHandlers[Signal];
if (!Thread) {
LogMan::Msg::AFmt("[{}] Thread has received a signal and hasn't registered itself with the delegate! Programming error!", FHU::Syscalls::gettid());
}
else {
for (auto &Handler : Handler.Handlers) {
if (Handler(Thread, Signal, Info, UContext)) {
// If the host handler handled the fault then we can continue now
return;
}
}
if (Handler.FrontendHandler &&
Handler.FrontendHandler(Thread, Signal, Info, UContext)) {
return;
}
// Now let the frontend handle the signal
// It's clearly a guest signal and this ends up being an OS specific issue
HandleGuestSignal(Thread, Signal, Info, UContext);
}
}
}
-83
View File
@@ -1,83 +0,0 @@
#ifndef NDEBUG
#include "Interface/Core/X86Tables/X86Tables.h"
#include <FEXCore/Utils/LogManager.h>
#include <tuple>
namespace FEXCore::X86Tables::X86InstDebugInfo {
void InstallDebugInfo() {
const std::tuple<uint8_t, uint8_t, Flags> BaseOpTable[] = {
{0x50, 8, {FLAGS_MEM_ACCESS}},
{0x58, 8, {FLAGS_MEM_ACCESS}},
{0x68, 1, {FLAGS_MEM_ACCESS}},
{0x6A, 1, {FLAGS_MEM_ACCESS}},
{0xAA, 4, {FLAGS_MEM_ACCESS}},
{0xC8, 1, {FLAGS_MEM_ACCESS}},
{0xCC, 2, {FLAGS_DEBUG}},
{0xD7, 1, {FLAGS_MEM_ACCESS}},
{0xF1, 1, {FLAGS_DEBUG}},
{0xF4, 1, {FLAGS_DEBUG}},
};
const std::tuple<uint8_t, uint8_t, Flags> TwoByteOpTable[] = {
{0x0B, 1, {FLAGS_DEBUG}},
{0x19, 7, {FLAGS_DEBUG}},
{0x28, 2, {FLAGS_MEM_ALIGN_16}},
{0x31, 1, {FLAGS_DEBUG}},
{0xA2, 1, {FLAGS_DEBUG}},
{0xA3, 1, {FLAGS_MEM_ACCESS}},
{0xAB, 1, {FLAGS_MEM_ACCESS}},
{0xB3, 1, {FLAGS_MEM_ACCESS}},
{0xBB, 1, {FLAGS_MEM_ACCESS}},
{0xFF, 1, {FLAGS_DEBUG}},
};
const std::tuple<uint8_t, uint8_t, Flags> PrimaryGroupOpTable[] = {
#define OPD(group, prefix, Reg) (((group - FEXCore::X86Tables::TYPE_GROUP_1) << 6) | (prefix) << 3 | (Reg))
{OPD(TYPE_GROUP_3, OpToIndex(0xF6), 6), 2, {FLAGS_DIVIDE}},
{OPD(TYPE_GROUP_3, OpToIndex(0xF7), 6), 2, {FLAGS_DIVIDE}},
#undef OPD
};
const std::tuple<uint16_t, uint8_t, Flags> SecondaryExtensionOpTable[] = {
#define PF_NONE 0
#define PF_F3 1
#define PF_66 2
#define PF_F2 3
#define OPD(group, prefix, Reg) (((group - FEXCore::X86Tables::TYPE_GROUP_6) << 5) | (prefix) << 3 | (Reg))
{OPD(TYPE_GROUP_15, PF_NONE, 2), 1, {FLAGS_DEBUG}},
{OPD(TYPE_GROUP_15, PF_NONE, 3), 1, {FLAGS_DEBUG}},
#undef PF_F3
#undef PF_66
#undef PF_F2
#undef OPD
};
auto GenerateDebugTable = [](auto& FinalTable, auto& LocalTable) {
for (auto Op : LocalTable) {
auto OpNum = std::get<0>(Op);
auto DebugInfo = std::get<2>(Op);
for (uint8_t i = 0; i < std::get<1>(Op); ++i) {
memcpy(&FinalTable[OpNum+i].DebugInfo, &DebugInfo, sizeof(X86InstDebugInfo::Flags));
}
}
};
GenerateDebugTable(BaseOps, BaseOpTable);
GenerateDebugTable(SecondBaseOps, TwoByteOpTable);
GenerateDebugTable(PrimaryInstGroupOps, PrimaryGroupOpTable);
GenerateDebugTable(SecondInstGroupOps, SecondaryExtensionOpTable);
}
}
#endif
@@ -1,87 +0,0 @@
/*
$info$
tags: ir|opts
$end_info$
*/
#include "Interface/IR/PassManager.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IREmitter.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/Profiler.h>
#include <memory>
namespace FEXCore::IR {
class DeadCodeElimination final : public FEXCore::IR::Pass {
bool Run(IREmitter *IREmit) override;
private:
void markUsed(OrderedNodeWrapper *CodeOp, IROp_Header *IROp);
};
bool DeadCodeElimination::Run(IREmitter *IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::DCE");
auto CurrentIR = IREmit->ViewIR();
int NumRemoved = 0;
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
// Reverse iteration is not yet working with the iterators
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
// We grab these nodes this way so we can iterate easily
auto CodeBegin = CurrentIR.at(BlockIROp->Begin);
auto CodeLast = CurrentIR.at(BlockIROp->Last);
while (1) {
auto [CodeNode, IROp] = CodeLast();
bool HasSideEffects = IR::HasSideEffects(IROp->Op);
if (IROp->Op == OP_SYSCALL ||
IROp->Op == OP_INLINESYSCALL) {
FEXCore::IR::SyscallFlags Flags{};
if (IROp->Op == OP_SYSCALL) {
auto Op = IROp->C<IR::IROp_Syscall>();
Flags = Op->Flags;
}
else {
auto Op = IROp->C<IR::IROp_InlineSyscall>();
Flags = Op->Flags;
}
if ((Flags & FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) == FEXCore::IR::SyscallFlags::NOSIDEEFFECTS) {
HasSideEffects = false;
}
}
// Skip over anything that has side effects
// Use count tracking can't safely remove anything with side effects
if (!HasSideEffects) {
if (CodeNode->GetUses() == 0) {
NumRemoved++;
IREmit->Remove(CodeNode);
}
}
if (CodeLast == CodeBegin) {
break;
}
--CodeLast;
}
}
return NumRemoved != 0;
}
void DeadCodeElimination::markUsed(OrderedNodeWrapper *CodeOp, IROp_Header *IROp) {
}
fextl::unique_ptr<FEXCore::IR::Pass> CreatePassDeadCodeElimination() {
return fextl::make_unique<DeadCodeElimination>();
}
}
@@ -1,77 +0,0 @@
/*
$info$
tags: ir|opts
desc: Sanity checking pass
$end_info$
*/
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IREmitter.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/fextl/sstream.h>
#include "Interface/IR/PassManager.h"
#include <memory>
namespace FEXCore::IR::Validation {
class PhiValidation final : public FEXCore::IR::Pass {
public:
bool Run(IREmitter *IREmit) override;
};
bool PhiValidation::Run(IREmitter *IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::PHIValidation");
bool HadError = false;
auto CurrentIR = IREmit->ViewIR();
fextl::ostringstream Errors;
// Walk the list and calculate the control flow
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
bool FoundNonPhi{};
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
switch (IROp->Op) {
// BEGINBLOCK doesn't matter for us
case IR::OP_BEGINBLOCK: break;
case IR::OP_PHIVALUE:
case IR::OP_PHI: {
if (FoundNonPhi) {
// If we have found a non-phi IR op and then had a Phi or PhiValue value then this is a programming mistake
// PHI values MUST be defined at the top of the block only
HadError |= true;
Errors << "Phi %ssa" << CurrentIR.GetID(CodeNode) << ": Was defined after non-phi operations. Which is invalid!" << std::endl;
}
// Check all the phi values to ensure they have the same type
break;
}
default:
FoundNonPhi = true;
break;
}
}
}
if (HadError) {
fextl::stringstream Out;
FEXCore::IR::Dump(&Out, &CurrentIR, nullptr);
Out << "Errors:" << std::endl << Errors.str() << std::endl;
LogMan::Msg::EFmt("{}", Out.str());
}
return false;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreatePhiValidation() {
return fextl::make_unique<PhiValidation>();
}
}
@@ -1,89 +0,0 @@
/*
$info$
tags: ir|opts
desc: Removes unused arguments if known syscall number
$end_info$
*/
#include "Interface/IR/PassManager.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IREmitter.h>
#include <FEXCore/IR/IntrusiveIRList.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/Utils/Profiler.h>
#include <memory>
#include <stdint.h>
namespace FEXCore::IR {
class SyscallOptimization final : public FEXCore::IR::Pass {
public:
bool Run(IREmitter *IREmit) override;
};
bool SyscallOptimization::Run(IREmitter *IREmit) {
FEXCORE_PROFILE_SCOPED("PassManager::SyscallOpt");
bool Changed = false;
auto CurrentIR = IREmit->ViewIR();
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
if (IROp->Op == FEXCore::IR::OP_SYSCALL) {
auto Op = IROp->CW<IR::IROp_Syscall>();
// Is the first argument a constant?
uint64_t Constant;
if (IREmit->IsValueConstant(Op->SyscallID, &Constant)) {
auto SyscallDef = Manager->SyscallHandler->GetSyscallABI(Constant);
auto SyscallFlags = Manager->SyscallHandler->GetSyscallFlags(Constant);
// Update the syscall flags
Op->Flags = SyscallFlags;
// XXX: Once we have the ability to do real function calls then we can call directly in to the syscall handler
if (SyscallDef.NumArgs < FEXCore::HLE::SyscallArguments::MAX_ARGS) {
// If the number of args are less than what the IR op supports then we can remove arg usage
// We need +1 since we are still passing in syscall number here
for (uint8_t Arg = (SyscallDef.NumArgs + 1); Arg < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++Arg) {
IREmit->ReplaceNodeArgument(CodeNode, Arg, IREmit->Invalid());
}
#ifdef _M_ARM_64
// Replace syscall with inline passthrough syscall if we can
if (SyscallDef.HostSyscallNumber != -1) {
IREmit->SetWriteCursor(CodeNode);
// Skip Args[0] since that is the syscallid
auto InlineSyscall = IREmit->_InlineSyscall(
CurrentIR.GetNode(IROp->Args[1]),
CurrentIR.GetNode(IROp->Args[2]),
CurrentIR.GetNode(IROp->Args[3]),
CurrentIR.GetNode(IROp->Args[4]),
CurrentIR.GetNode(IROp->Args[5]),
CurrentIR.GetNode(IROp->Args[6]),
SyscallDef.HostSyscallNumber,
Op->Flags);
// Replace all syscall uses with this inline one
IREmit->ReplaceAllUsesWith(CodeNode, InlineSyscall);
// We must remove here since DCE can't remove a IROp with sideeffects
IREmit->Remove(CodeNode);
}
#endif
}
Changed = true;
}
}
}
return Changed;
}
fextl::unique_ptr<FEXCore::IR::Pass> CreateSyscallOptimization() {
return fextl::make_unique<SyscallOptimization>();
}
}
@@ -1,247 +0,0 @@
#pragma once
#include <FEXCore/Debug/InternalThreadState.h>
#include <atomic>
#include <cstdint>
#include <mutex>
#include <shared_mutex>
#include <signal.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace FEXCore {
#ifndef _WIN32
// Replacement for std::mutexes to deal with unlocking issues in the face of Linux fork() semantics.
//
// A fork() only clones the parent's calling thread. Other threads are silently dropped, which permanently leaves any mutexes owned by them locked.
// To address this issue, ForkableUniqueMutex and ForkableSharedMutex provide a way to forcefully remove any dangling locks and reset the mutexes to their default state.
class ForkableUniqueMutex final {
public:
ForkableUniqueMutex()
: Mutex (PTHREAD_MUTEX_INITIALIZER) {
}
// Move-only type
ForkableUniqueMutex(const ForkableUniqueMutex&) = delete;
ForkableUniqueMutex& operator=(const ForkableUniqueMutex&) = delete;
ForkableUniqueMutex(ForkableUniqueMutex &&rhs) = default;
ForkableUniqueMutex& operator=(ForkableUniqueMutex &&) = default;
void lock() {
[[maybe_unused]] const auto Result = pthread_mutex_lock(&Mutex);
LOGMAN_THROW_A_FMT(Result == 0, "{} failed to lock with {}", __func__, Result);
}
void unlock() {
[[maybe_unused]] const auto Result = pthread_mutex_unlock(&Mutex);
LOGMAN_THROW_A_FMT(Result == 0, "{} failed to unlock with {}", __func__, Result);
}
// Initialize the internal pthread object to its default initializer state.
// Should only ever be used in the child process when a Linux fork() has occured.
void StealAndDropActiveLocks() {
Mutex = PTHREAD_MUTEX_INITIALIZER;
}
private:
pthread_mutex_t Mutex;
};
class ForkableSharedMutex final {
public:
ForkableSharedMutex()
: Mutex (PTHREAD_RWLOCK_INITIALIZER) {
}
// Move-only type
ForkableSharedMutex(const ForkableSharedMutex&) = delete;
ForkableSharedMutex& operator=(const ForkableSharedMutex&) = delete;
ForkableSharedMutex(ForkableSharedMutex &&rhs) = default;
ForkableSharedMutex& operator=(ForkableSharedMutex &&) = default;
void lock() {
[[maybe_unused]] const auto Result = pthread_rwlock_wrlock(&Mutex);
LOGMAN_THROW_A_FMT(Result == 0, "{} failed to lock with {}", __func__, Result);
}
void unlock() {
[[maybe_unused]] const auto Result = pthread_rwlock_unlock(&Mutex);
LOGMAN_THROW_A_FMT(Result == 0, "{} failed to unlock with {}", __func__, Result);
}
void lock_shared() {
[[maybe_unused]] const auto Result = pthread_rwlock_rdlock(&Mutex);
LOGMAN_THROW_A_FMT(Result == 0, "{} failed to lock with {}", __func__, Result);
}
void unlock_shared() {
unlock();
}
bool try_lock() {
const auto Result = pthread_rwlock_trywrlock(&Mutex);
return Result == 0;
}
bool try_lock_shared() {
const auto Result = pthread_rwlock_tryrdlock(&Mutex);
return Result == 0;
}
// Initialize the internal pthread object to its default initializer state.
// Should only ever be used in the child process when a Linux fork() has occured.
void StealAndDropActiveLocks() {
Mutex = PTHREAD_RWLOCK_INITIALIZER;
}
private:
pthread_rwlock_t Mutex;
};
#else
// Windows doesn't support forking, so these can be standard mutexes.
using ForkableUniqueMutex = std::mutex;
using ForkableSharedMutex = std::shared_mutex;
#endif
template<typename MutexType, void (MutexType::*lock_fn)(), void (MutexType::*unlock_fn)()>
class ScopedDeferredSignalWithMutexBase final {
public:
ScopedDeferredSignalWithMutexBase(MutexType &_Mutex, FEXCore::Core::InternalThreadState *Thread)
: Mutex {&_Mutex}
, Thread {Thread} {
// Needs to be atomic so that operations can't end up getting reordered around this.
Thread->CurrentFrame->State.DeferredSignalRefCount.Increment(1);
// Lock the mutex
(Mutex->*lock_fn)();
}
// No copy or assignment possible
ScopedDeferredSignalWithMutexBase(const ScopedDeferredSignalWithMutexBase&) = delete;
ScopedDeferredSignalWithMutexBase& operator=(ScopedDeferredSignalWithMutexBase&) = delete;
// Only move
ScopedDeferredSignalWithMutexBase(ScopedDeferredSignalWithMutexBase &&rhs)
: Mutex {rhs.Mutex}
, Thread {rhs.Thread} {
rhs.Mutex = nullptr;
}
~ScopedDeferredSignalWithMutexBase() {
if (Mutex != nullptr) {
// Unlock the mutex
(Mutex->*unlock_fn)();
#ifdef _M_X86_64
// Needs to be atomic so that operations can't end up getting reordered around this.
// Without this, the recount and the signal access could get reordered.
auto Result = Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1);
// X86-64 must do an additional check around the store.
if ((Result - 1) == 0) {
// Must happen after the refcount store
Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0);
}
#else
Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1);
Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0);
#endif
}
}
private:
MutexType *Mutex;
FEXCore::Core::InternalThreadState *Thread;
};
using ScopedDeferredSignalWithMutex = ScopedDeferredSignalWithMutexBase<std::mutex, &std::mutex::lock, &std::mutex::unlock>;
using ScopedDeferredSignalWithSharedLock = ScopedDeferredSignalWithMutexBase<std::shared_mutex, &std::shared_mutex::lock_shared, &std::shared_mutex::unlock_shared>;
using ScopedDeferredSignalWithUniqueLock = ScopedDeferredSignalWithMutexBase<std::shared_mutex, &std::shared_mutex::lock, &std::shared_mutex::unlock>;
// Forkable variant
using ScopedDeferredSignalWithForkableMutex = ScopedDeferredSignalWithMutexBase<
FEXCore::ForkableUniqueMutex,
&FEXCore::ForkableUniqueMutex::lock,
&FEXCore::ForkableUniqueMutex::unlock>;
using ScopedDeferredSignalWithForkableSharedLock = ScopedDeferredSignalWithMutexBase<
FEXCore::ForkableSharedMutex,
&FEXCore::ForkableSharedMutex::lock_shared,
&FEXCore::ForkableSharedMutex::unlock_shared>;
using ScopedDeferredSignalWithForkableUniqueLock = ScopedDeferredSignalWithMutexBase<
FEXCore::ForkableSharedMutex,
&FEXCore::ForkableSharedMutex::lock,
&FEXCore::ForkableSharedMutex::unlock>;
template<typename MutexType, void (MutexType::*lock_fn)(), void (MutexType::*unlock_fn)()>
class ScopedPotentialDeferredSignalWithMutexBase final {
public:
ScopedPotentialDeferredSignalWithMutexBase(MutexType &_Mutex, FEXCore::Core::InternalThreadState *Thread, uint64_t Mask = ~0ULL)
: Mutex {&_Mutex}
, Thread {Thread} {
if (Thread) {
Thread->CurrentFrame->State.DeferredSignalRefCount.Increment(1);
}
else {
// Mask all signals, storing the original incoming mask
::syscall(SYS_rt_sigprocmask, SIG_SETMASK, &Mask, &OriginalMask, sizeof(OriginalMask));
}
// Lock the mutex
(Mutex->*lock_fn)();
}
// No copy or assignment possible
ScopedPotentialDeferredSignalWithMutexBase(const ScopedPotentialDeferredSignalWithMutexBase&) = delete;
ScopedPotentialDeferredSignalWithMutexBase& operator=(ScopedPotentialDeferredSignalWithMutexBase&) = delete;
// Only move
ScopedPotentialDeferredSignalWithMutexBase(ScopedPotentialDeferredSignalWithMutexBase &&rhs)
: Mutex {rhs.Mutex}
, Thread {rhs.Thread} {
rhs.Mutex = nullptr;
}
~ScopedPotentialDeferredSignalWithMutexBase() {
if (Mutex != nullptr) {
// Unlock the mutex
(Mutex->*unlock_fn)();
if (Thread) {
#ifdef _M_X86_64
// Needs to be atomic so that operations can't end up getting reordered around this.
// Without this, the refcount and the signal access could get reordered.
auto Result = Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1);
// X86-64 must do an additional check around the store.
if ((Result - 1) == 0) {
// Must happen after the refcount store
Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0);
}
#else
Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1);
Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0);
#endif
}
else {
// Unmask back to the original signal mask
::syscall(SYS_rt_sigprocmask, SIG_SETMASK, &OriginalMask, nullptr, sizeof(OriginalMask));
}
}
}
private:
MutexType *Mutex;
uint64_t OriginalMask{};
FEXCore::Core::InternalThreadState *Thread;
};
using ScopedPotentialDeferredSignalWithMutex = ScopedPotentialDeferredSignalWithMutexBase<std::mutex, &std::mutex::lock, &std::mutex::unlock>;
using ScopedPotentialDeferredSignalWithSharedLock = ScopedPotentialDeferredSignalWithMutexBase<std::shared_mutex, &std::shared_mutex::lock_shared, &std::shared_mutex::unlock_shared>;
using ScopedPotentialDeferredSignalWithUniqueLock = ScopedPotentialDeferredSignalWithMutexBase<std::shared_mutex, &std::shared_mutex::lock, &std::shared_mutex::unlock>;
// Forkable variant
using ScopedPotentialDeferredSignalWithForkableMutex = ScopedPotentialDeferredSignalWithMutexBase<
FEXCore::ForkableUniqueMutex,
&FEXCore::ForkableUniqueMutex::lock,
&FEXCore::ForkableUniqueMutex::unlock>;
using ScopedPotentialDeferredSignalWithForkableSharedLock = ScopedPotentialDeferredSignalWithMutexBase<
FEXCore::ForkableSharedMutex,
&FEXCore::ForkableSharedMutex::lock_shared,
&FEXCore::ForkableSharedMutex::unlock_shared>;
using ScopedPotentialDeferredSignalWithForkableUniqueLock = ScopedPotentialDeferredSignalWithMutexBase<
FEXCore::ForkableSharedMutex,
&FEXCore::ForkableSharedMutex::lock,
&FEXCore::ForkableSharedMutex::unlock>;
}
-13
View File
@@ -1,13 +0,0 @@
#pragma once
#include <cstdint>
namespace FEXCore {
[[nodiscard]] constexpr uint64_t AlignUp(uint64_t value, uint64_t size) {
return value + (size - value % size) % size;
}
[[nodiscard]] constexpr uint64_t AlignDown(uint64_t value, uint64_t size) {
return value - value % size;
}
} // namespace FEXCore
-13
View File
@@ -1,13 +0,0 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2018 Ryan Houdek <Sonicadvance1@gmail.com>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
+1 -1
+1 -1
@@ -13,15 +13,6 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
set(_M_ARM_64 1)
endif()
if (ENABLE_VIXL_SIMULATOR)
# If the vixl simulator is enabled then we are using the ARM64 JIT
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" FALSE)
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" TRUE)
else()
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" ${_M_X86_64})
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" ${_M_ARM_64})
endif()
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
@@ -33,6 +24,27 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(CheckCXXCompilerFlag)
include(CheckIncludeFileCXX)
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -Wattributes -Werror=attributes")
check_cxx_source_compiles(
"
__attribute__((preserve_all))
void Testy() {
}
int main() {
return 0;
}"
HAS_CLANG_PRESERVE_ALL)
unset(CMAKE_REQUIRED_FLAGS)
if (HAS_CLANG_PRESERVE_ALL)
if (MINGW_BUILD)
message(STATUS "Ignoring broken clang::preserve_all support")
set(HAS_CLANG_PRESERVE_ALL FALSE)
else()
message(STATUS "Has clang::preserve_all")
endif()
endif ()
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
# Useful to have for freestanding libFEXCore
File renamed without changes.
File renamed without changes.
@@ -27,7 +27,9 @@ def print_header():
#ifndef OPT_STRARRAY
#define OPT_STRARRAY(group, enum, json, default) OPT_BASE(fextl::string, group, enum, json, default)
#endif
#ifndef OPT_STRENUM
#define OPT_STRENUM(group, enum, json, default) OPT_BASE(uint64_t, group, enum, json, default)
#endif
'''
output_file.write(header)
@@ -40,6 +42,7 @@ def print_tail():
#undef OPT_UINT64
#undef OPT_STR
#undef OPT_STRARRAY
#undef OPT_STRENUM
'''
output_file.write(tail)
@@ -127,12 +130,13 @@ def print_man_options(options):
short = op_vals["ShortArg"]
default = op_vals["Default"]
value_type = op_vals["Type"]
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = op_vals["TextDefault"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
if (value_type == "str" or value_type == "strarray" or value_type == "strenum"):
# Wrap the string argument in quotes
default = "'" + default + "'"
print_man_option(
@@ -141,6 +145,12 @@ def print_man_options(options):
op_vals["Desc"],
default
)
if (value_type == "strenum"):
Enums = op_vals["Enums"]
output_man.write("\\fBAvailable Options:\\fR\n")
for enum_op_key, enum_op_vals in Enums.items():
output_man.write("{}, ".format(enum_op_vals))
output_man.write("\n")
output_man.write(".El\n")
@@ -150,12 +160,13 @@ def print_man_environment(options):
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
value_type = op_vals["Type"]
# Textual default rather than enum based
if ("TextDefault" in op_vals):
default = op_vals["TextDefault"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
if (value_type == "str" or value_type == "strarray" or value_type == "strenum"):
# Wrap the string argument in quotes
default = "'" + default + "'"
print_man_env_option(
@@ -165,6 +176,13 @@ def print_man_environment(options):
False
)
if (value_type == "strenum"):
Enums = op_vals["Enums"]
output_man.write("\\fBAvailable Options:\\fR\n")
for enum_op_key, enum_op_vals in Enums.items():
output_man.write("{}, ".format(enum_op_vals))
output_man.write("\n")
print_man_environment_tail()
output_man.write(".El\n")
@@ -334,7 +352,7 @@ def print_argloader_options(options):
for op_key, op_vals in group_vals.items():
default = op_vals["Default"]
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray" or op_vals["Type"] == "strenum"):
# Wrap the string argument in quotes
default = "\"" + default + "\""
@@ -382,7 +400,10 @@ def print_parse_argloader_options(options):
# boolean values need a decimal specifier. Otherwise fmt prints strings.
conversion_func = "fextl::fmt::format(\"{:d}\", "
if (value_type == "strarray"):
if (value_type == "strenum"):
output_argloader.write("\tfextl::string UserValue = Options[\"{0}\"];\n".format(op_key))
output_argloader.write("\tSet(FEXCore::Config::ConfigOption::CONFIG_{}, FEXCore::Config::EnumParser<FEXCore::Config::{}ConfigPair>(FEXCore::Config::{}_EnumPairs, UserValue));\n".format(op_key.upper(), op_key, op_key, op_key))
elif (value_type == "strarray"):
# these need a bit more help
output_argloader.write("\tauto Array = Options.all(\"{0}\");\n".format(op_key))
output_argloader.write("\tfor (auto iter = Array.begin(); iter != Array.end(); ++iter) {\n")
@@ -407,13 +428,56 @@ def print_parse_envloader_options(options):
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
value_type = op_vals["Type"]
if (value_type == "strenum"):
output_argloader.write("else if (Key == \"FEX_{0}\") {{\n".format(op_key.upper()))
output_argloader.write("Value = FEXCore::Config::EnumParser<FEXCore::Config::{}ConfigPair>(FEXCore::Config::{}_EnumPairs, Value_View);\n".format(op_key, op_key, op_key))
output_argloader.write("}\n")
if ("ArgumentHandler" in op_vals):
conversion_func = "FEXCore::Config::Handler::{0}".format(op_vals["ArgumentHandler"])
output_argloader.write("else if (Key == \"FEX_{0}\") {{\n".format(op_key.upper()))
output_argloader.write("Value = {0}(Value);\n".format(conversion_func))
output_argloader.write("Value = {0}(Value_View);\n".format(conversion_func))
output_argloader.write("}\n")
output_argloader.write("#endif\n")
def print_parse_enum_options(options):
output_argloader.write("#ifdef ENUMDEFINES\n")
output_argloader.write("#undef ENUMDEFINES\n")
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
if (op_vals["Type"] == "strenum"):
output_argloader.write("enum class {} : uint64_t {{\n".format(op_key))
Enums = op_vals["Enums"]
i = 0
# Always have an OFF.
output_argloader.write("\tOFF = 0,\n")
for enum_op_key, enum_op_vals in Enums.items():
output_argloader.write("\t{} = 1ULL << {},\n".format(enum_op_key.upper(), i))
i += 1
output_argloader.write("};\n")
output_argloader.write("FEX_DEF_NUM_OPS({})\n".format(op_key))
for op_group, group_vals in options.items():
for op_key, op_vals in group_vals.items():
if (op_vals["Type"] == "strenum"):
Enums = op_vals["Enums"]
output_argloader.write("using {}ConfigPair = std::pair<std::string_view, FEXCore::Config::{}>;\n".format(op_key, op_key))
output_argloader.write("constexpr static std::array<{}ConfigPair, {}> {}_EnumPairs = {{{{\n".format(op_key, len(Enums) + 1, op_key))
i = 0
# Always have an OFF.
output_argloader.write("\t{{ \"off\", FEXCore::Config::{}::OFF }},\n".format(op_key))
for enum_op_key, enum_op_vals in Enums.items():
output_argloader.write("\t{{ \"{}\", FEXCore::Config::{}::{} }},\n".format(enum_op_vals, op_key, enum_op_key.upper()))
i += 1
output_argloader.write("}};\n")
output_argloader.write("#endif\n")
def check_for_duplicate_options(options):
short_map = []
long_map = []
@@ -492,4 +556,7 @@ print_parse_argloader_options(options);
# Generate environment loader code
print_parse_envloader_options(options);
# Generate enum variable options
print_parse_enum_options(options);
output_argloader.close()
@@ -46,11 +46,15 @@ class OpDefinition:
NumElements: str
OpClass: str
HasSideEffects: bool
ImplicitFlagClobber: bool
RAOverride: int
SwitchGen: bool
ArgPrinter: bool
SSAArgNum: int
NonSSAArgNum: int
DynamicDispatch: bool
JITDispatch: bool
JITDispatchOverride: str
Arguments: list
EmitValidation: list
Desc: list
@@ -64,11 +68,15 @@ class OpDefinition:
self.OpClass = None
self.OpSize = 0
self.HasSideEffects = False
self.ImplicitFlagClobber = False
self.RAOverride = -1
self.SwitchGen = True
self.ArgPrinter = True
self.SSAArgNum = 0
self.NonSSAArgNum = 0
self.DynamicDispatch = False
self.JITDispatch = True
self.JITDispatchOverride = None
self.Arguments = []
self.EmitValidation = []
self.Desc = []
@@ -144,7 +152,7 @@ def parse_ops(ops):
Argument = Argument.strip()
OpArg = OpArgument()
Split = Argument.split(":")
Split = Argument.split(":", 1)
if len(Split) != 2:
ExitError("Error parsing argument. Missing Type and name colon split")
@@ -213,6 +221,9 @@ def parse_ops(ops):
if "HasSideEffects" in op_val:
OpDef.HasSideEffects = bool(op_val["HasSideEffects"])
if "ImplicitFlagClobber" in op_val:
OpDef.ImplicitFlagClobber = bool(op_val["ImplicitFlagClobber"])
if "ArgPrinter" in op_val:
OpDef.ArgPrinter = bool(op_val["ArgPrinter"])
@@ -228,6 +239,15 @@ def parse_ops(ops):
if "Desc" in op_val:
OpDef.Desc = op_val["Desc"]
if "DynamicDispatch" in op_val:
OpDef.DynamicDispatch = bool(op_val["DynamicDispatch"])
if "JITDispatch" in op_val:
OpDef.JITDispatch = bool(op_val["JITDispatch"])
if "JITDispatchOverride" in op_val:
OpDef.JITDispatchOverride = op_val["JITDispatchOverride"]
# Do some fixups of the data here
if len(OpDef.EmitValidation) != 0:
for i in range(len(OpDef.EmitValidation)):
@@ -251,11 +271,8 @@ def parse_ops(ops):
# Print out enum values
def print_enums():
if len(IROps) > 255:
ExitError("We have more than uint8_t ops. We have {}. Time to upgrade to uint16_t".format(len(IROps)))
output_file.write("#ifdef IROP_ENUM\n")
output_file.write("enum IROps : uint8_t {\n")
output_file.write("enum IROps : uint16_t {\n")
for op in IROps:
output_file.write("\tOP_{},\n" .format(op.Name.upper()))
@@ -282,7 +299,6 @@ def print_ir_structs(defines):
output_file.write("\tIROps Op;\n\n")
output_file.write("\tuint8_t Size;\n")
output_file.write("\tuint8_t ElementSize;\n")
output_file.write("\tuint8_t _pad;\n")
output_file.write("\ttemplate<typename T>\n")
output_file.write("\tT const* C() const { return reinterpret_cast<T const*>(Data); }\n")
@@ -361,6 +377,7 @@ def print_ir_sizes():
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] uint8_t GetRAArgs(IROps Op);\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] bool HasSideEffects(IROps Op);\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] bool ImplicitFlagClobber(IROps Op);\n")
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] bool GetHasDest(IROps Op);\n")
output_file.write("#undef IROP_SIZES\n")
@@ -454,15 +471,17 @@ def print_ir_getraargs():
def print_ir_hassideeffects():
output_file.write("#ifdef IROP_HASSIDEEFFECTS_IMPL\n")
output_file.write("constexpr std::array<uint8_t, OP_LAST + 1> SideEffects = {\n")
for op in IROps:
output_file.write("\t{},\n".format(("true" if op.HasSideEffects else "false")))
for array, prop in [("SideEffects", "HasSideEffects"),
("ImplicitFlagClobbers", "ImplicitFlagClobber")]:
output_file.write(f"constexpr std::array<uint8_t, OP_LAST + 1> {array} = {{\n")
for op in IROps:
output_file.write("\t{},\n".format(("true" if getattr(op, prop) else "false")))
output_file.write("};\n\n")
output_file.write("};\n\n")
output_file.write("bool HasSideEffects(IROps Op) {\n")
output_file.write(" return SideEffects[Op];\n")
output_file.write("}\n")
output_file.write(f"bool {prop}(IROps Op) {{\n")
output_file.write(f" return {array}[Op];\n")
output_file.write("}\n")
output_file.write("#undef IROP_HASSIDEEFFECTS_IMPL\n")
output_file.write("#endif\n\n")
@@ -631,6 +650,10 @@ def print_ir_allocator_helpers():
output_file.write(") {\n")
# Save NZCV if needed before clobbering NZCV
if op.ImplicitFlagClobber:
output_file.write("\t\tSaveNZCV(IROps::OP_{});".format(op.Name.upper()))
output_file.write("\t\tauto Op = AllocateOp<IROp_{}, IROps::OP_{}>();\n".format(op.Name, op.Name.upper()))
if op.SSAArgNum != 0:
@@ -679,7 +702,8 @@ def print_ir_allocator_helpers():
output_file.write("\t\t#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED\n")
for Validation in op.EmitValidation:
output_file.write("\t\tassert({});\n".format(Validation))
Sanitized = Validation.replace("\"", "\\\"")
output_file.write("\tLOGMAN_THROW_A_FMT({}, \"{}\");\n".format(Validation, Sanitized))
output_file.write("\t\t#endif\n")
output_file.write("\t\treturn Op;\n")
@@ -734,10 +758,38 @@ def print_ir_parser_switch_helper():
output_file.write("#undef IROP_PARSER_SWITCH_HELPERS\n")
output_file.write("#endif\n")
if (len(sys.argv) < 3):
def print_ir_dispatcher_defs():
output_dispatch_file.write("#ifdef IROP_DISPATCH_DEFS\n")
for op in IROps:
if op.Name != "Last" and op.SwitchGen and op.JITDispatch and op.JITDispatchOverride == None:
output_dispatch_file.write("DEF_OP({});\n".format(op.Name))
output_dispatch_file.write("#undef IROP_DISPATCH_DEFS\n")
output_dispatch_file.write("#endif\n")
def print_ir_dispatcher_dispatch():
output_dispatch_file.write("#ifdef IROP_DISPATCH_DISPATCH\n")
for op in IROps:
if op.Name != "Last" and op.JITDispatch:
DispatchName = op.Name
if op.JITDispatchOverride != None:
DispatchName = op.JITDispatchOverride
if (op.DynamicDispatch):
output_dispatch_file.write("REGISTER_OP_RT({}, {});\n".format(op.Name.upper(), DispatchName))
else:
output_dispatch_file.write("REGISTER_OP({}, {});\n".format(op.Name.upper(), DispatchName))
output_dispatch_file.write("#undef IROP_DISPATCH_DISPATCH\n")
output_dispatch_file.write("#endif\n")
if (len(sys.argv) < 4):
ExitError()
output_filename = sys.argv[2]
output_dispatcher_filename = sys.argv[3]
json_file = open(sys.argv[1], "r")
json_text = json_file.read()
json_file.close()
@@ -767,3 +819,10 @@ print_ir_allocator_helpers()
print_ir_parser_switch_helper()
output_file.close()
output_dispatch_file = open(output_dispatcher_filename, "w")
print_ir_dispatcher_defs()
print_ir_dispatcher_dispatch()
output_dispatch_file.close()
@@ -1,4 +1,4 @@
set (MAN_DIR ${CMAKE_INSTALL_PREFIX}/share/man CACHE PATH "MAN_DIR")
set (MAN_DIR share/man CACHE PATH "MAN_DIR")
set (FEXCORE_BASE_SRCS
Interface/Config/Config.cpp
@@ -90,7 +90,6 @@ set (SRCS
Interface/Core/CPUBackend.cpp
Interface/Core/CPUID.cpp
Interface/Core/Frontend.cpp
Interface/Core/GdbServer.cpp
Interface/Core/HostFeatures.cpp
Interface/Core/ObjectCache/JobHandling.cpp
Interface/Core/ObjectCache/NamedRegionObjectHandler.cpp
@@ -101,15 +100,23 @@ set (SRCS
Interface/Core/OpcodeDispatcher/X87.cpp
Interface/Core/OpcodeDispatcher/X87F64.cpp
Interface/Core/OpcodeDispatcher.cpp
Interface/Core/SignalDelegator.cpp
Interface/Core/X86Tables.cpp
Interface/Core/X86DebugInfo.cpp
Interface/Core/X86HelperGen.cpp
Interface/Core/ArchHelpers/Arm64Emitter.cpp
Interface/Core/Dispatcher/Dispatcher.cpp
Interface/Core/Dispatcher/X86Dispatcher.cpp
Interface/Core/Dispatcher/Arm64Dispatcher.cpp
Interface/Core/Interpreter/Fallbacks/InterpreterFallbacks.cpp
Interface/Core/JIT/Arm64/JIT.cpp
Interface/Core/JIT/Arm64/ALUOps.cpp
Interface/Core/JIT/Arm64/AtomicOps.cpp
Interface/Core/JIT/Arm64/BranchOps.cpp
Interface/Core/JIT/Arm64/ConversionOps.cpp
Interface/Core/JIT/Arm64/EncryptionOps.cpp
Interface/Core/JIT/Arm64/FlagOps.cpp
Interface/Core/JIT/Arm64/MemoryOps.cpp
Interface/Core/JIT/Arm64/MiscOps.cpp
Interface/Core/JIT/Arm64/MoveOps.cpp
Interface/Core/JIT/Arm64/VectorOps.cpp
Interface/Core/JIT/Arm64/Arm64Relocations.cpp
Interface/Core/X86Tables/BaseTables.cpp
Interface/Core/X86Tables/DDDTables.cpp
Interface/Core/X86Tables/EVEXTables.cpp
@@ -133,15 +140,15 @@ set (SRCS
Interface/IR/Passes/DeadCodeElimination.cpp
Interface/IR/Passes/DeadContextStoreElimination.cpp
Interface/IR/Passes/IRCompaction.cpp
Interface/IR/Passes/IRDumperPass.cpp
Interface/IR/Passes/IRValidation.cpp
Interface/IR/Passes/RAValidation.cpp
Interface/IR/Passes/LongDivideRemovalPass.cpp
Interface/IR/Passes/ValueDominanceValidation.cpp
Interface/IR/Passes/PhiValidation.cpp
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
Interface/IR/Passes/DeadStoreElimination.cpp
Interface/IR/Passes/RegisterAllocationPass.cpp
Interface/IR/Passes/SyscallOptimization.cpp
Interface/IR/Passes/InlineCallOptimization.cpp
Utils/NetStream.cpp
Utils/Telemetry.cpp
Utils/Threads.cpp
@@ -159,24 +166,7 @@ if (ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT)
Utils/AllocatorOverride.cpp)
endif()
if (ENABLE_INTERPRETER)
list(APPEND SRCS
Interface/Core/Interpreter/InterpreterCore.cpp
Interface/Core/Interpreter/InterpreterOps.cpp
Interface/Core/Interpreter/ALUOps.cpp
Interface/Core/Interpreter/AtomicOps.cpp
Interface/Core/Interpreter/BranchOps.cpp
Interface/Core/Interpreter/ConversionOps.cpp
Interface/Core/Interpreter/EncryptionOps.cpp
Interface/Core/Interpreter/F80Ops.cpp
Interface/Core/Interpreter/FlagOps.cpp
Interface/Core/Interpreter/MemoryOps.cpp
Interface/Core/Interpreter/MiscOps.cpp
Interface/Core/Interpreter/MoveOps.cpp
Interface/Core/Interpreter/VectorOps.cpp)
endif()
set(DEFINES -DTHREAD_LOCAL=_Thread_local)
set(DEFINES -DTHREAD_LOCAL=_Thread_local -DJIT_ARM64)
if (_M_X86_64)
list(APPEND DEFINES -D_M_X86_64=1)
@@ -195,41 +185,14 @@ if (ENABLE_VIXL_DISASSEMBLER)
list(APPEND DEFINES -DVIXL_DISASSEMBLER=1)
endif()
if (ENABLE_JIT_X86_64)
list(APPEND SRCS
Interface/Core/JIT/x86_64/JIT.cpp
Interface/Core/JIT/x86_64/ALUOps.cpp
Interface/Core/JIT/x86_64/AtomicOps.cpp
Interface/Core/JIT/x86_64/BranchOps.cpp
Interface/Core/JIT/x86_64/ConversionOps.cpp
Interface/Core/JIT/x86_64/EncryptionOps.cpp
Interface/Core/JIT/x86_64/FlagOps.cpp
Interface/Core/JIT/x86_64/MemoryOps.cpp
Interface/Core/JIT/x86_64/MiscOps.cpp
Interface/Core/JIT/x86_64/MoveOps.cpp
Interface/Core/JIT/x86_64/VectorOps.cpp
Interface/Core/JIT/x86_64/x64Relocations.cpp
)
list(APPEND DEFINES -DJIT_X86_64)
if (_M_ARM_64 AND HAS_CLANG_PRESERVE_ALL)
list(APPEND DEFINES "-DFEXCORE_PRESERVE_ALL_ATTR=__attribute__((preserve_all));-DFEXCORE_HAS_PRESERVE_ALL_ATTR=1")
else()
list(APPEND DEFINES "-DFEXCORE_PRESERVE_ALL_ATTR=;-DFEXCORE_HAS_PRESERVE_ALL_ATTR=0")
endif()
if (ENABLE_JIT_ARM64)
list(APPEND DEFINES -DJIT_ARM64)
list(APPEND SRCS
Interface/Core/JIT/Arm64/JIT.cpp
Interface/Core/JIT/Arm64/ALUOps.cpp
Interface/Core/JIT/Arm64/AtomicOps.cpp
Interface/Core/JIT/Arm64/BranchOps.cpp
Interface/Core/JIT/Arm64/ConversionOps.cpp
Interface/Core/JIT/Arm64/EncryptionOps.cpp
Interface/Core/JIT/Arm64/FlagOps.cpp
Interface/Core/JIT/Arm64/MemoryOps.cpp
Interface/Core/JIT/Arm64/MiscOps.cpp
Interface/Core/JIT/Arm64/MoveOps.cpp
Interface/Core/JIT/Arm64/VectorOps.cpp
Interface/Core/JIT/Arm64/Arm64Relocations.cpp
)
endif()
# Some defines for the softfloat library
list(APPEND DEFINES "-DSOFTFLOAT_BUILTIN_CLZ")
set (LIBS fmt::fmt vixl xxhash FEXHeaderUtils)
@@ -255,15 +218,16 @@ configure_file(
# Generate IR include file
set(OUTPUT_IR_FOLDER "${CMAKE_BINARY_DIR}/include/FEXCore/IR")
set(OUTPUT_NAME "${OUTPUT_IR_FOLDER}/IRDefines.inc")
set(OUTPUT_DISPATCHER_NAME "${OUTPUT_IR_FOLDER}/IRDefines_Dispatch.inc")
set(INPUT_NAME "${CMAKE_CURRENT_SOURCE_DIR}/Interface/IR/IR.json")
file(MAKE_DIRECTORY "${OUTPUT_IR_FOLDER}")
add_custom_command(
OUTPUT "${OUTPUT_NAME}"
OUTPUT "${OUTPUT_NAME}" "${OUTPUT_DISPATCHER_NAME}"
DEPENDS "${INPUT_NAME}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/json_ir_generator.py"
COMMAND "python3" "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/json_ir_generator.py" "${INPUT_NAME}" "${OUTPUT_NAME}"
COMMAND "python3" "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/json_ir_generator.py" "${INPUT_NAME}" "${OUTPUT_NAME}" "${OUTPUT_DISPATCHER_NAME}"
)
set_source_files_properties(${OUTPUT_NAME} PROPERTIES
@@ -395,6 +359,7 @@ function(AddObject Name Type)
add_library(${Name} ${Type} ${SRCS})
target_link_libraries(${Name} FEXCore_Base)
target_compile_options(${Name} PRIVATE ${FEX_TUNE_COMPILE_FLAGS})
AddDefaultOptionsToTarget(${Name})
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
@@ -403,6 +368,7 @@ endfunction()
function(AddLibrary Name Type)
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
target_link_libraries(${Name} FEXCore_Base)
target_compile_options(${Name} PRIVATE ${FEX_TUNE_COMPILE_FLAGS})
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
if (MINGW_BUILD)
# Mingw build isn't building a linux shared library, so it can't have a SONAME.
@@ -1,3 +1,4 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <FEXCore/Utils/Allocator.h>
+139
View File
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: MIT
#include <FEXCore/fextl/fmt.h>
#include "Common/JitSymbols.h"
#include <fcntl.h>
#include <unistd.h>
namespace FEXCore {
JITSymbols::JITSymbols() {
}
JITSymbols::~JITSymbols() {
if (fd != -1) {
close(fd);
}
}
void JITSymbols::InitFile() {
// We can't use FILE here since we must be robust against forking processes closing our FD from under us.
#ifdef __ANDROID__
// Android simpleperf looks in /data/local/tmp instead of /tmp
const auto PerfMap = fextl::fmt::format("/data/local/tmp/perf-{}.map", getpid());
#else
const auto PerfMap = fextl::fmt::format("/tmp/perf-{}.map", getpid());
#endif
fd = open(PerfMap.c_str(), O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
}
void JITSymbols::RegisterNamedRegion(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto Buffer = fextl::fmt::format("{} {:x} {}\n", HostAddr, CodeSize, Name);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
void JITSymbols::RegisterJITSpace(const void *HostAddr, uint32_t CodeSize) {
if (fd == -1) return;
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto Buffer = fextl::fmt::format("{} {:x} FEXJIT\n", HostAddr, CodeSize);
auto Result = write(fd, Buffer.c_str(), Buffer.size());
if (Result == -1 && errno == EBADF) {
fd = -1;
}
}
// Buffered JIT symbols.
void JITSymbols::Register(Core::JITSymbolBuffer *Buffer, const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
if (fd == -1) return;
// Calculate remaining sizes.
const auto RemainingSize = Buffer->BUFFER_SIZE - Buffer->Offset;
const auto CurrentBufferOffset = &Buffer->Buffer[Buffer->Offset];
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto FMTResult = fmt::format_to_n(CurrentBufferOffset, RemainingSize, "{} {:x} JIT_0x{:x}_{}\n", HostAddr, CodeSize, GuestAddr, HostAddr);
if (FMTResult.out >= &Buffer->Buffer[Buffer->BUFFER_SIZE]) {
// Couldn't fit, need to force a write.
WriteBuffer(Buffer, true);
// Rerun
Register(Buffer, HostAddr, GuestAddr, CodeSize);
return;
}
Buffer->Offset += FMTResult.size;
WriteBuffer(Buffer);
}
void JITSymbols::Register(Core::JITSymbolBuffer *Buffer, const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset) {
if (fd == -1) return;
// Calculate remaining sizes.
const auto RemainingSize = Buffer->BUFFER_SIZE - Buffer->Offset;
const auto CurrentBufferOffset = &Buffer->Buffer[Buffer->Offset];
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto FMTResult = fmt::format_to_n(CurrentBufferOffset, RemainingSize, "{} {:x} {}+0x{:x} ({})\n", HostAddr, CodeSize, Name, Offset, HostAddr);
if (FMTResult.out >= &Buffer->Buffer[Buffer->BUFFER_SIZE]) {
// Couldn't fit, need to force a write.
WriteBuffer(Buffer, true);
// Rerun
Register(Buffer, HostAddr, CodeSize, Name, Offset);
return;
}
Buffer->Offset += FMTResult.size;
WriteBuffer(Buffer);
}
void JITSymbols::RegisterNamedRegion(Core::JITSymbolBuffer *Buffer, const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
if (fd == -1) return;
// Calculate remaining sizes.
const auto RemainingSize = Buffer->BUFFER_SIZE - Buffer->Offset;
const auto CurrentBufferOffset = &Buffer->Buffer[Buffer->Offset];
// Linux perf format is very straightforward
// `<HostPtr> <Size> <Name>\n`
const auto FMTResult = fmt::format_to_n(CurrentBufferOffset, RemainingSize, "{} {:x} {}\n", HostAddr, CodeSize, Name);
if (FMTResult.out >= &Buffer->Buffer[Buffer->BUFFER_SIZE]) {
// Couldn't fit, need to force a write.
WriteBuffer(Buffer, true);
// Rerun
RegisterNamedRegion(Buffer, HostAddr, CodeSize, Name);
return;
}
Buffer->Offset += FMTResult.size;
WriteBuffer(Buffer);
}
void JITSymbols::WriteBuffer(Core::JITSymbolBuffer *Buffer, bool ForceWrite) {
auto Now = std::chrono::steady_clock::now();
if (!ForceWrite) {
if (((Buffer->LastWrite - Now) < Buffer->MAXIMUM_THRESHOLD) &&
Buffer->Offset < Buffer->NEEDS_WRITE_DISTANCE) {
// Still buffering, no need to write.
return;
}
}
Buffer->LastWrite = Now;
auto Result = write(fd, Buffer->Buffer, Buffer->Offset);
if (Result == -1 && errno == EBADF) {
fd = -1;
}
Buffer->Offset = 0;
}
} // namespace FEXCore
+36
View File
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <FEXCore/fextl/memory.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <string_view>
namespace FEXCore {
class JITSymbols final {
public:
JITSymbols();
~JITSymbols();
void InitFile();
void RegisterNamedRegion(const void *HostAddr, uint32_t CodeSize, std::string_view Name);
void RegisterJITSpace(const void *HostAddr, uint32_t CodeSize);
// Allocate JIT buffer.
static fextl::unique_ptr<Core::JITSymbolBuffer> AllocateBuffer() {
return fextl::make_unique<Core::JITSymbolBuffer>();
}
void Register(Core::JITSymbolBuffer *Buffer, const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize);
void Register(Core::JITSymbolBuffer *Buffer, const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset);
void RegisterNamedRegion(Core::JITSymbolBuffer *Buffer, const void *HostAddr, uint32_t CodeSize, std::string_view Name);
private:
int fd{-1};
void WriteBuffer(Core::JITSymbolBuffer *Buffer, bool ForceWrite = false);
};
}
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "internals.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t extF80_add( extFloat80_t a, extFloat80_t b )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t extF80_div( extFloat80_t a, extFloat80_t b )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
bool extF80_eq( extFloat80_t a, extFloat80_t b )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
bool extF80_lt( extFloat80_t a, extFloat80_t b )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t extF80_mul( extFloat80_t a, extFloat80_t b )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t extF80_rem( extFloat80_t a, extFloat80_t b )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t
extF80_roundToInt( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
{
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t extF80_sqrt( extFloat80_t a )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "internals.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t extF80_sub( extFloat80_t a, extFloat80_t b )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
float128_t extF80_to_f128( extFloat80_t a )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
float32_t extF80_to_f32( extFloat80_t a )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
float64_t extF80_to_f64( extFloat80_t a )
{
union { struct extFloat80M s; extFloat80_t f; } uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
int_fast32_t
extF80_to_i32( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
{
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
int_fast64_t
extF80_to_i64( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
{
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
uint_fast64_t
extF80_to_ui64( extFloat80_t a, uint_fast8_t roundingMode, bool exact )
{
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t f128_to_extF80( float128_t a )
{
union ui128_f128 uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t f32_to_extF80( float32_t a )
{
union ui32_f32 uA;
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "specialize.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t f64_to_extF80( float64_t a )
{
union ui64_f64 uA;
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "internals.h"
#include "softfloat.h"
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t i32_to_extF80( int32_t a )
{
uint_fast16_t uiZ64;
@@ -68,9 +68,11 @@ uint_fast64_t
uint_fast64_t softfloat_roundMToUI64( bool, uint32_t *, uint_fast8_t, bool );
#endif
FEXCORE_PRESERVE_ALL_ATTR
int_fast32_t softfloat_roundToI32( bool, uint_fast64_t, uint_fast8_t, bool );
#ifdef SOFTFLOAT_FAST_INT64
FEXCORE_PRESERVE_ALL_ATTR
int_fast64_t
softfloat_roundToI64(
bool, uint_fast64_t, uint_fast64_t, uint_fast8_t, bool );
@@ -109,8 +111,10 @@ float16_t
#define isNaNF32UI( a ) (((~(a) & 0x7F800000) == 0) && ((a) & 0x007FFFFF))
struct exp16_sig32 { int_fast16_t exp; uint_fast32_t sig; };
FEXCORE_PRESERVE_ALL_ATTR
struct exp16_sig32 softfloat_normSubnormalF32Sig( uint_fast32_t );
FEXCORE_PRESERVE_ALL_ATTR
float32_t softfloat_roundPackToF32( bool, int_fast16_t, uint_fast32_t );
float32_t softfloat_normRoundPackToF32( bool, int_fast16_t, uint_fast32_t );
@@ -130,8 +134,10 @@ float32_t
#define isNaNF64UI( a ) (((~(a) & UINT64_C( 0x7FF0000000000000 )) == 0) && ((a) & UINT64_C( 0x000FFFFFFFFFFFFF )))
struct exp16_sig64 { int_fast16_t exp; uint_fast64_t sig; };
FEXCORE_PRESERVE_ALL_ATTR
struct exp16_sig64 softfloat_normSubnormalF64Sig( uint_fast64_t );
FEXCORE_PRESERVE_ALL_ATTR
float64_t softfloat_roundPackToF64( bool, int_fast16_t, uint_fast64_t );
float64_t softfloat_normRoundPackToF64( bool, int_fast16_t, uint_fast64_t );
@@ -155,11 +161,14 @@ float64_t
*----------------------------------------------------------------------------*/
struct exp32_sig64 { int_fast32_t exp; uint64_t sig; };
FEXCORE_PRESERVE_ALL_ATTR
struct exp32_sig64 softfloat_normSubnormalExtF80Sig( uint_fast64_t );
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t
softfloat_roundPackToExtF80(
bool, int_fast32_t, uint_fast64_t, uint_fast64_t, uint_fast8_t );
FEXCORE_PRESERVE_ALL_ATTR
extFloat80_t
softfloat_normRoundPackToExtF80(
bool, int_fast32_t, uint_fast64_t, uint_fast64_t, uint_fast8_t );
@@ -181,6 +190,7 @@ extFloat80_t
#define isNaNF128UI( a64, a0 ) (((~(a64) & UINT64_C( 0x7FFF000000000000 )) == 0) && (a0 || ((a64) & UINT64_C( 0x0000FFFFFFFFFFFF ))))
struct exp32_sig128 { int_fast32_t exp; struct uint128 sig; };
FEXCORE_PRESERVE_ALL_ATTR
struct exp32_sig128
softfloat_normSubnormalF128Sig( uint_fast64_t, uint_fast64_t );
Loaded 100 of 834 files, more files were not shown because too many files have changed in this diff. Show more