Compare commits

..
Author SHA1 Message Date
Ryan Houdek 320c5f1847 Docs: Update for release FEX-2510 2025-10-08 17:03:06 -07:00
Ryan Houdek 3b88947cbe Merge pull request #4950 from lioncash/syscall
SyscallHandler: Shrink definition struct from 24 to 16 bytes
2025-10-08 14:03:57 -07:00
Ryan Houdek f05be0120e Merge pull request #4949 from lioncash/thread
ThreadManager: Make StatAlloc instance private
2025-10-08 13:44:43 -07:00
Lioncache 10c3b2fb5d SyscallHandler: Shrink definition struct from 24 to 16 bytes
Just a minor space saving, given how many syscall definitions we'll
have.
2025-10-08 16:40:20 -04:00
Lioncache 74ae89865c SHMStats: Add missing virtual destructor
This should be present for universally consistent behavior, regardless
of how the class is used.
2025-10-08 16:17:42 -04:00
Lioncache a2445904e7 ThreadManager: Make StatAlloc instance private
This doesn't need to be public.
2025-10-08 16:13:23 -04:00
Ryan Houdek bd5188e7bc Merge pull request #4948 from lioncash/fmt2
EnumUtils: Remove fmt include
2025-10-07 13:04:22 -07:00
Lioncache d89c119d9d EnumUtils: Remove fmt include
Forgot to remove this in the previous PR.
2025-10-07 12:54:32 -04:00
Ryan Houdek 802f3bed47 Merge pull request #4947 from lioncash/format
EnumUtils: Further simplify enum passthrough formatting
2025-10-07 06:47:56 -07:00
Ryan Houdek 9c606a278d Merge pull request #4946 from lioncash/sys
SysCalls: Remove unimplemented prototypes
2025-10-07 06:46:59 -07:00
Lioncache 7aa5bc0503 EnumUtils: Further simplify enum passthrough formatting
Turns out a simpler way was added to the docs at some point and I never
noticed.

Before:
   text     data      bss      dec      hex  filename
4159895  1471360  4336824  9968079   9819cf  Bin/FEX

After:
   text     data      bss      dec      hex  filename
4157159  1471360  4336824  9965343   980f1f  Bin/FEX
2025-10-07 01:52:05 -04:00
Lioncache d94b9fae94 SourceCodeResolver: Pass string_view by value for GenerateMap()
Generally this should be passed as a value type unless there's a good
reason not to.
2025-10-07 01:27:15 -04:00
Lioncache 05fcaaa758 Syscalls: Remove unimplemented prototypes 2025-10-07 01:27:11 -04:00
Ryan Houdek 9626a64340 Merge pull request #4944 from lioncash/type
Addressing: Shave 8 bytes off AddressMode
2025-10-06 14:01:09 -07:00
Lioncache c1cfd4db83 Addressing: Shave 8 bytes off AddressMode
Just a trivial rearrangement, Drops the struct down to 40 bytes.
We can also make some functions take it by const reference so we
aren't churning some copies.

Before:
   text     data      bss      dec      hex  filename
4160559  1471360  4336824  9968743   981c67  Bin/FEX

After:
   text     data      bss      dec      hex  filename
4159927  1471360  4336824  9968111   9819ef  Bin/FEX
2025-10-06 15:52:15 -04:00
Ryan Houdek bfda91ac16 Merge pull request #4943 from lioncash/table
X86Tables: Remove unused LateInitCopyTable()
2025-10-06 12:17:39 -07:00
Lioncache b7117b86ea X86Tables: Remove unused LateInitCopyTable() 2025-10-06 15:04:36 -04:00
LC a94059ad81 Merge pull request #4942 from Sonicadvance1/fix_map
ThreadManager: Fix mmap usage to not take guest space
2025-10-06 14:59:27 -04:00
Ryan Houdek 2cab87fbd0 ThreadManager: Fix mmap usage to not take guest space
SHM stats and CallRet stack was accidentally using system mmap which
means it would take VA space from the guest. Ensure it uses the
Allocator helpers so that doesn't happen.

Gives 32-bit guests 8-ish megabyte of VA space back.
2025-10-06 11:20:00 -07:00
Ryan Houdek 2017100ff3 Merge pull request #4941 from lioncash/unused
OpcodeDispatcher: Remove unimplemented function prototypes
2025-10-06 10:18:18 -07:00
Lioncache 72cb29d36b OpcodeDispatcher: Remove unimplemented function prototypes
Just cleans out the interface a little.
2025-10-06 13:08:07 -04:00
Ryan Houdek a2b0d594fb Merge pull request #4939 from lioncash/invalid
OpcodeDispatcher: Default alignment parameters for store helpers
2025-10-06 09:11:49 -07:00
Lioncache a16d4ff1f3 OpcodeDispatcher: Deduplicate in LEAOp/SMSWOp
We can shorten a few lines here by just storing the op addr value
to a local variable.
2025-10-06 11:13:00 -04:00
Lioncache 305b1ecf2d OpcodeDispatcher: Default alignment parameters for store helpers
Avoids actively doing this wonky thing where we're passing
iInvalid all over the place to mean variable alignment depending
on store element size or GPR size.

Makes using the API a little more visibly straightforward and makes
cases where alignment matters more explicit.
2025-10-06 11:04:12 -04:00
Ryan Houdek 3cc0cae249 Merge pull request #4938 from lioncash/prctl
PrctlUtils: Move to include folder
2025-10-05 23:27:38 -07:00
Lioncache 137aa59254 PrctlUtils: Move to include folder
We can group more prctl value handling in here.
2025-10-06 02:12:29 -04:00
LC 5f8cb0dc54 Merge pull request #4937 from Sonicadvance1/name_vma
Allocator: Name FEX's VMA regions for allocation
2025-10-05 22:40:58 -04:00
Ryan Houdek 45978474f3 Allocator: Name FEX's VMA regions for allocation
Will allow external tools to track how much memory FEX allocates.
Necessary since we can't use traditional memory usage tools to track FEX
memory allocation independently of guest allocations. Plus most tools
like heaptrack hook allocation symbols, which break under jemalloc.

Using this information I can see with Steam loaded with my library that
FEX consumes ~825MB. Total process resident memory is 1204M, accounting
for around 379MB being used by steam itself. This is /relatively/ close
to my desktop running steam at around 261MB. There's a bit of variance
due to what Steam chooses to do at startup.

This tracking will be the first step towards seeing where our memory
usage is going.
2025-10-05 19:30:59 -07:00
Ryan Houdek 75206c7a51 Merge pull request #4936 from lioncash/str
Common/StringConv: std::stoull -> std::strtoull for enum handler
2025-10-05 13:14:02 -07:00
Ryan Houdek 06ff0a45a7 Merge pull request #4935 from lioncash/move
IR: Remove Swap1/Swap2 ops
2025-10-05 11:57:35 -07:00
Ryan Houdek c948d532a7 Merge pull request #4934 from lioncash/select
OpcodeDispatcher: Move off implicit _Select
2025-10-05 11:54:59 -07:00
Ryan Houdek 3825483f79 Merge pull request #4933 from lioncash/reg
RedundantFlagCalculationElimination: Eliminate unnecessary vector copy
2025-10-05 11:53:56 -07:00
Lioncache 0b238d942e Common/StringConv: std::stoull -> std::strtoull for enum handler
Missed this when simplifying the conversion facilities, but
we should be using std::strtoull here, as per the programming
concerns.
2025-10-05 14:49:07 -04:00
Lioncache 16b09a9dd3 OpcodeDispatcher: Move off implicit _Select
Resolves a lingering TODO.
2025-10-05 13:57:36 -04:00
Lioncache 4f6800b768 IR: Remove Swap1/Swap2 ops
These are no longer used.
2025-10-05 13:26:51 -04:00
Lioncache c84801adb3 RedundantFlagCalc: Avoid vector copy in OptimizeParity()
Previously this was making a copy of the vector, when we only
need to read from it.
2025-10-05 12:17:45 -04:00
Lioncache 69f990c1f8 RedundantFlagCalc: Organize headers
Also remove incorrect comment that this pass isn't used.
2025-10-05 12:17:45 -04:00
Lioncache 8a83564678 RedundantFlagCalc: Remove unimplemented prototype
Just tidies the interface a little.
2025-10-05 12:17:45 -04:00
Lioncache 43d93f836f RedundantFlagCalc: Mark members as const where applicable
These don't affect member state.
2025-10-05 03:14:21 -04:00
Ryan Houdek 7c2c0f7fe5 Merge pull request #4932 from lioncash/python
json_ir_generator: Minor cleanup
2025-10-04 11:44:08 -07:00
Lioncache 3ff47cf677 json_ir_generator: Add missing error message
ExitError() expects a string argument. Unlikely error case to be hit,
but we should still make the error useful to the reader.
2025-10-04 11:00:05 -04:00
Lioncache 3165dce89e json_ir_generator: Turn IROpNameMap into a set
This is only used to track for duplicates, so we only really need
the key name, since the values are never used.
2025-10-04 11:00:00 -04:00
Lioncache d4fad0a3b0 json_ir_generator: Simplify some looping
We can use enumerate to collapse a few of these
2025-10-04 10:37:34 -04:00
Lioncache 15fcaa794f json_ir_generator: Simplify is_ssa_type
Same behavior, but a little more straightforward
2025-10-04 08:47:08 -04:00
Lioncache 6a1a505336 json_ir_generator: Add type annotations for top level vars
Makes the types a little more explicit and also makes member
suggestions in IDEs (vscode, etc) work a little better.
2025-10-04 08:45:08 -04:00
LC 85937a7bfb Merge pull request #4931 from Sonicadvance1/sse4a
Implement remaining SSE4a instructions
2025-10-04 08:22:58 -04:00
Ryan Houdek 3baa598b9e HostFeatures: Adds flag for SSE4a 2025-10-04 02:51:28 -07:00
Ryan Houdek 6f83b10af6 InstcountCI: Update 2025-10-04 02:46:27 -07:00
Ryan Houdek f52bcb49ab unittests/ASM: Adds new variable SSE4a tests 2025-10-04 02:46:14 -07:00
Ryan Houdek 9cef8ff7ce OpcodeDispatcher: Implement support for SSE4a variable extrq/insertq 2025-10-04 02:44:56 -07:00
Ryan Houdek a499ad6404 IR: Implement two new IR ops
rbit is useful in generic algorithms.
`MaskGenerateFromBitWidth` is only really useful for SSE4a, but
implemented in the OpcodeDispatcher is rough, so add an operation.
2025-10-04 02:43:39 -07:00
Ryan Houdek 7c8767ab32 OpcodeDispatcher: Minor improvement to inserting constant to vector 2025-10-04 02:43:21 -07:00
Ryan Houdek 38a8559943 Merge pull request #4930 from lioncash/forward
IR: Move RegisterAllocationPass forward decl to JITClass
2025-10-03 21:22:17 -07:00
Lioncache 48c6acbc87 IR: Move RegisterAllocationPass forward decl to JITClass
This isn't actually used anywhere in the IR header, so we can
move it to where it's actually used.

Now the IR interface header doesn't have anything related to the
independent passes in it.
2025-10-04 00:08:58 -04:00
Ryan Houdek b5d93bc4a0 Merge pull request #4928 from lioncash/passthru
EnumUtils: Add define for default passthrough formatting
2025-10-03 19:45:41 -07:00
LC a9fffe771b Merge pull request #4929 from Sonicadvance1/sse4a_imm
OpcodeDispatcher: Implement support for imm variants of SSE4a extrq/insertq
2025-10-03 17:40:19 -04:00
Ryan Houdek 69f3ab14b9 InstcountCI: Update 2025-10-03 13:51:51 -07:00
Ryan Houdek a7b9a34ec9 unittests: Adds imm extrq/insertq tests 2025-10-03 13:34:32 -07:00
Ryan Houdek f45be1f59e OpcodeDispatcher: Implement support for imm extrq/insertq
Fairly straightforward to implement, but not exciting in their
performance.
2025-10-03 13:05:43 -07:00
Ryan Houdek 012c2ba851 IR: Fixes vector 64-bit binops
We were only supporting operating size of 256-bit and 128-bit. These
also support 64-bit which wasn't wired up.

SSE4a will want to use 64-bit.
2025-10-03 13:04:09 -07:00
Lioncache a0c2ce0068 EnumUtils: Add define for default passthrough formatting
Handles a normal case where printing an enum type as an integral value
is still desirable.

Mainly just a way to reduce boilerplate.
2025-10-03 14:21:19 -04:00
Ryan Houdek 86898035da Merge pull request #4926 from lioncash/enum
IR: Convert RegisterClassType to an enum class
2025-10-03 10:35:23 -07:00
Ryan Houdek 05eeffe0f7 Merge pull request #4927 from bylaws/ffds
Windows: Avoid redeclaring strtoll as unimplemented
2025-10-03 10:32:39 -07:00
Billy Laws 70cffce5eb Windows: Avoid redeclaring strtoll as unimplemented
Included as part of the musl code we pull in. Fixes ARM64EC/WOW64 FEX.
2025-10-03 16:30:41 +02:00
Lioncache 0258e914e0 IR: Convert RegisterClassType to an enum class
Removes the last of the wrapper value types in favor of enum classes.
2025-10-03 03:54:56 -04:00
Ryan Houdek c35b81d2c5 Merge pull request #4925 from lioncash/regclass
IREmitter: Add register class helpers
2025-10-02 22:14:41 -07:00
Lioncache fc63dcedb6 IR: Migrate to new helpers
Reduces a bunch of noise related to the register classes and hoists them
out so that converting the classes over to enums should be fairly
straightforward.
2025-10-03 00:53:02 -04:00
Lioncache c879650c4b IR: Add GPR/FPR helpers
These will be utilized in follow up PRs
2025-10-02 23:53:50 -04:00
Ryan Houdek c4d019939b Merge pull request #4924 from lioncash/irdump 2025-10-02 10:36:12 -07:00
Lioncache 3841cc4aa5 IRDumper: Add remaining missing enum values
Now that the compiler can warn against these, we can fill the
remaining list in.
2025-10-02 12:18:36 -04:00
Lioncache 468f2f2d2d IRDumper: Convert remaining printers to lambda style invocations
This allows easily moving the default case out of the switch, making it
easier for compilers to warn about missing values in switches if any
enum members are added in the future but aren't added to the
formatters.
2025-10-02 12:18:28 -04:00
Lioncache be5db91c13 IRDumper: Add missing CheckTF BranchHint 2025-10-02 03:01:11 -04:00
Lioncache 6e7d0a520a IRDumper: Fix error message for FloatCompareOp
If ever printed this would give a misleading error that it was an
unrecognized OpSize type.
2025-10-02 02:57:29 -04:00
Lioncache e37996873a IRDumper: Add missing FenceType entry 2025-10-02 02:55:34 -04:00
Lioncache 25e851b570 IRDumper: Add missing SyscallFlags entry 2025-10-02 02:55:31 -04:00
Ryan Houdek d41b21a626 Merge pull request #4923 from lioncash/py5
IR: Remove TypeDefinition struct
2025-10-01 22:03:00 -07:00
Lioncache d65c54ba21 IR: Remove TypeDefinition struct
These aren't used at all.
2025-10-02 00:50:51 -04:00
Ryan Houdek a5d3bf0f48 Merge pull request #4922 from lioncash/py4
IR: Convert MemOffsetType to enum class
2025-10-01 21:41:54 -07:00
Lioncache f76e8c7185 IR: Convert MemOffsetType to enum class
Gets rid of another wrapper struct.
2025-10-02 00:24:42 -04:00
Ryan Houdek 96145e8e3f Merge pull request #4921 from lioncash/py3
IR: Convert rounding modes to enum class
2025-10-01 21:03:15 -07:00
Lioncache 530821fc4a IR: Convert rounding modes to enum class
Same behavior, but more compact
2025-10-01 23:37:57 -04:00
Ryan Houdek e4a4a529dd Merge pull request #4920 from lioncash/py2 2025-10-01 20:28:23 -07:00
Lioncache 863d1e0007 IR: Convert FenceType to an enum class
Same thing minus an extra struct lingering around.
2025-10-01 23:13:08 -04:00
Ryan Houdek c64d6f2b36 Merge pull request #4919 from lioncash/python
IR: Convert CondClassType over to enum class
2025-10-01 19:28:50 -07:00
Lioncache a798880ac8 IR: Convert CondClassType over to enum class
Instead of having this sort of odd indirection through a struct type,
we can add support for defining custom enums in the IR description.

This lets us both get strong typing (and allow for weak typing, should
any enum in the future need it), without needing a struct for a basic
value type.

Even then, if we do need a struct for anything in the future, then
we still allow strong typing for values themselves while allowing
them to be used in various ways.
2025-10-01 10:45:00 -04:00
Ryan Houdek 9db9be9612 Merge pull request #4918 from lioncash/bitcast
General: Migrate to std::bit_cast
2025-09-30 19:32:51 -07:00
Lioncache 6a19c77184 General: Migrate to std::bit_cast
We already have a few cases where we already use bit_cast in the
emitter, so we may as well move all of our temporary helper instances
over as well.
2025-09-30 22:20:10 -04:00
Ryan Houdek c17b9dea30 Merge pull request #4917 from lioncash/collapse
StringConv: Merge integral-handling facilities together
2025-09-30 18:58:13 -07:00
Lioncache 7564b6c69a StringConv: Merge integral-handling facilities together
Same behavior, but we condense all of the integral handling
into one place.
2025-09-30 21:43:57 -04:00
Ryan Houdek 5858c5041f Merge pull request #4916 from lioncash/type
StringConv: Fix std::enable_if usage
2025-09-30 17:13:43 -07:00
LC 18d0dec416 Merge pull request #4911 from Sonicadvance1/evmd_linux
Linux: Implement support for extended volatile metadata
2025-09-30 20:01:26 -04:00
LC 752046108c Merge pull request #4910 from Sonicadvance1/fix_wow64_evmd
Wow64: Ensure extended volatile metadata is removed
2025-09-30 20:01:12 -04:00
Lioncache 773ddbf5c4 StringConv: Fix std::enable_if usage
This neglected to use the ::type qualifier, so this candidate was
always being considered in overload resolution.
2025-09-30 19:53:45 -04:00
Ryan Houdek 88e90a6f3a Merge pull request #4915 from lioncash/emitter
Arm64Emitter: Cull unnecessary includes
2025-09-30 10:50:04 -07:00
Lioncache 84325d6b5c Arm64Emitter: Cull unnecessary includes
Also fixes an indirect include.
2025-09-30 09:32:25 -04:00
Ryan Houdek 1b8e03e1de Merge pull request #4914 from lioncash/header
fextl/string: Correct <filesystem> include to <functional>
2025-09-29 21:26:39 -07:00
Lioncache 569d7297f0 fextl/string: Correct <filesystem> include to <functional>
This was unintentionally putting all the filesystem utilities into headers implicitly.
2025-09-29 23:54:08 -04:00
Ryan Houdek d9520c9498 Merge pull request #4913 from lioncash/help
JITClass: Mark functions as static where applicable
2025-09-29 20:53:09 -07:00
Lioncache 6b61093037 JITClass: Mark functions as static where applicable
These don't depend on any class state.
2025-09-29 23:42:43 -04:00
LC 74b09d5581 Merge pull request #4912 from Sonicadvance1/avx_32bit_overflow
FEXCore: Implement support for AVX gathers with overflow
2025-09-29 21:10:42 -04:00
Ryan Houdek 08da8f6f5c unittests/ASM: Adds gather overflow tests 2025-09-29 16:49:34 -07:00
Ryan Houdek 7f8fffbb24 FEXCore: Implement support for AVX gathers with overflow
Falls down the emulated path so we can zero extend the address
calculation. No way for SVE gathers to use 32-bit addressing.
2025-09-29 16:06:02 -07:00
Ryan Houdek b08e5f9821 Linux: Implement support for extended volatile metadata
Allows applications running entirely under Linux to use the same
extended volatile metadata as Windows.

For example `FEX_EXTENDEDVOLATILEMETADATA=iw4sp.exe\;0xe9da0-0xe9ec7`
this configuration works for both wow64 and Linux to disable the TSO
emulation on the memcpy routine in that game that consumes around 80% of
CPU time in TSO emulation.

Works with Linux native games as well of course.
2025-09-29 14:36:51 -07:00
Ryan Houdek 1758648f56 Common: Move ApplyFEXExtendedVolatileMetadata to Common
It is going to be used by Linux.

Also adds support for file offset handling, which is required on Linux
but not on win32.
2025-09-29 14:36:50 -07:00
Ryan Houdek 418ce0aed9 Wow64: Ensure extended volatile metadata is removed
Was missing this, matches the arm64ec side.
2025-09-29 14:13:44 -07:00
LC 4e926076c9 Merge pull request #4909 from zeyi2/main
Docs: Fix wrong link in `Readme_CN.md`
2025-09-29 10:30:38 -04:00
mtx 3f0aaeba68 Docs: Fix wrong link in Readme_CN.md 2025-09-29 16:53:31 +08:00
LC 6ecec77a74 Merge pull request #4908 from FEX-Emu/bylaws-patch-1
Change required Python version from 3.10 to 3.9
2025-09-28 13:03:45 -04:00
Billy Laws 79917dc9bb Change required Python version from 3.10 to 3.9
Debian bullseye is a supported build environment which only provides 3.9
2025-09-28 16:34:50 +02:00
Ryan Houdek b0b60fb761 Merge pull request #4907 from neobrain/feature_nix_update
Build: Update toolchain for WoA builds
2025-09-25 13:43:23 -07:00
Tony Wasserka 7463152f50 Build: Update toolchain for WoA builds 2025-09-25 22:17:44 +02:00
Ryan Houdek cbd8217c60 Merge pull request #4904 from bylaws/tesrgr
TestHarnessRunner: Don't attempt to build on MinGW
2025-09-24 13:16:47 -07:00
Ryan Houdek 4de1762c21 Merge pull request #4903 from bylaws/fee
vixl: Update submodule
2025-09-24 12:41:00 -07:00
Billy Laws 73962402df TestHarnessRunner: Don't attempt to build on MinGW 2025-09-24 20:28:33 +01:00
Billy Laws d90ccceec7 vixl: Update submodule 2025-09-24 20:26:28 +01:00
Ryan Houdek 0c5ef1ce1d Merge pull request #4902 from lioncash/fmt
Update fmt to 12.0.0
2025-09-23 12:13:34 -07:00
Ryan Houdek e5680e031d Merge pull request #4835 from pmatos/fix/classify
Fix quiet and signalling nan propagation
2025-09-23 11:17:37 -07:00
Paulo Matos eee0cffa57 instcountci: Fix quiet and signalling nan propagation 2025-09-23 13:48:45 +02:00
Paulo Matos c480b0ba41 asm_tests: Fix quiet and signalling nan propagation 2025-09-23 13:48:45 +02:00
Paulo Matos e7a47a647c Fix quiet and signalling nan propagation
This adds a new mode X87StrictReducedPrecision.
The strict reduced precision is like the reduced precision but adds extra checks,
like the currently implemented nan and snan propagations.

Fix for __builtin_issignaling() test of SPEC2017 classify test.
2025-09-23 13:48:45 +02:00
Paulo Matos 500c82bbf8 Do not format NASM include files 2025-09-23 13:48:45 +02:00
Lioncache 008528af91 Update fmt to 12.0.0
Moves us over to the next major version release.
Changelog here: https://github.com/fmtlib/fmt/releases/tag/12.0.0
2025-09-23 09:50:11 +02:00
Tony Wasserka 13199ad203 Merge pull request #4898 from Sonicadvance1/a_snake_a_snake
CMake: Update our python minspec to 3.10
2025-09-23 09:29:33 +02:00
LC 533dd1a54b Merge pull request #4897 from Sonicadvance1/fix_telem
OpcodeDispatcher: Fixes telemetry on legacy segment read
2025-09-23 08:11:10 +02:00
Ryan Houdek b6f2e10416 CMake: Update our python minspec to 3.10 2025-09-22 12:31:29 -07:00
Ryan Houdek c58db36a7c OpcodeDispatcher: Fixes telemetry on legacy segment read 2025-09-22 11:57:34 -07:00
163 changed files with 13721 additions and 3476 deletions

No files matched your search

+3
View File
@@ -7,3 +7,6 @@ FEXCore/Source/Interface/Core/X86Tables/*
# Inline headers with list-like content that can't be processed individually
Source/Tools/LinuxEmulation/LinuxSyscalls/x*/SyscallsNames.inl
Source/Tools/LinuxEmulation/LinuxSyscalls/x*/Ioctl/*.inl
# Include files in unittests
unittests/*ASM/Includes/*.inc
+1 -1
View File
@@ -319,7 +319,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
endif()
find_package(PkgConfig REQUIRED)
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter)
find_package(Python 3.9 REQUIRED COMPONENTS Interpreter)
set(BUILD_SHARED_LIBS OFF)
+29 -47
View File
@@ -36,33 +36,24 @@ public:
DataProcessing_PCRel_Imm(Op, rd, Imm);
}
[[nodiscard]] BranchEncodeSucceeded adr(ARMEmitter::Register rd, const BackwardLabel* Label) {
void adr(ARMEmitter::Register rd, const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
if (IsADRRange(Imm)) [[likely]] {
constexpr uint32_t Op = 0b0001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, Imm);
return BranchEncodeSucceeded::Success;
}
// Can't encode.
return BranchEncodeSucceeded::Failure;
constexpr uint32_t Op = 0b0001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, Imm);
}
[[nodiscard]] BranchEncodeSucceeded adr(ARMEmitter::Register rd, ForwardLabel* Label) {
void adr(ARMEmitter::Register rd, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::ADR});
constexpr uint32_t Op = 0b0001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded adr(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
void adr(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return adr(rd, &Label->Backward);
adr(rd, &Label->Backward);
} else {
return adr(rd, &Label->Forward);
adr(rd, &Label->Forward);
}
}
@@ -71,42 +62,32 @@ public:
DataProcessing_PCRel_Imm(Op, rd, Imm);
}
[[nodiscard]] BranchEncodeSucceeded adrp(ARMEmitter::Register rd, const BackwardLabel* Label) {
void adrp(ARMEmitter::Register rd, const BackwardLabel* Label) {
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>() & ~0xFFFLL);
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
if (IsADRPRange(Imm) && IsADRPAligned(Imm)) [[likely]] {
constexpr uint32_t Op = 0b1001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, Imm);
return BranchEncodeSucceeded::Success;
}
// Can't encode.
return BranchEncodeSucceeded::Failure;
constexpr uint32_t Op = 0b1001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, Imm);
}
[[nodiscard]] BranchEncodeSucceeded adrp(ARMEmitter::Register rd, ForwardLabel* Label) {
void adrp(ARMEmitter::Register rd, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::ADRP});
constexpr uint32_t Op = 0b1001'0000 << 24;
DataProcessing_PCRel_Imm(Op, rd, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded adrp(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
void adrp(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return adrp(rd, &Label->Backward);
adrp(rd, &Label->Backward);
} else {
return adrp(rd, &Label->Forward);
adrp(rd, &Label->Forward);
}
}
[[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, const BackwardLabel* Label) {
void LongAddressGen(ARMEmitter::Register rd, const BackwardLabel* Label) {
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>());
if (IsADRRange(Imm)) {
// If the range is in ADR range then we can just use ADR.
return adr(rd, Label);
adr(rd, Label);
} else if (IsADRPRange(Imm)) {
int64_t ADRPImm = (reinterpret_cast<int64_t>(Label->Location) & ~0xFFFLL) - (GetCursorAddress<int64_t>() & ~0xFFFLL);
@@ -121,28 +102,23 @@ public:
// Now even an add
add(ARMEmitter::Size::i64Bit, rd, rd, AlignedOffset);
}
return BranchEncodeSucceeded::Success;
} else {
LOGMAN_MSG_A_FMT("Unscaled offset too large");
FEX_UNREACHABLE;
}
// Can't encode.
return BranchEncodeSucceeded::Failure;
}
[[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, ForwardLabel* Label) {
void LongAddressGen(ARMEmitter::Register rd, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::LONG_ADDRESS_GEN});
// Emit a register index and a nop. These will be backpatched.
dc32(rd.Idx());
nop();
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
void LongAddressGen(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return LongAddressGen(rd, &Label->Backward);
LongAddressGen(rd, &Label->Backward);
} else {
return LongAddressGen(rd, &Label->Forward);
LongAddressGen(rd, &Label->Forward);
}
}
@@ -886,6 +862,12 @@ public:
}
private:
static constexpr Condition InvertCondition(Condition cond) {
// These behave as always, so it makes no sense to allow inverting these.
LOGMAN_THROW_A_FMT(cond != Condition::CC_AL && cond != Condition::CC_NV, "Cannot invert CC_AL or CC_NV");
return static_cast<Condition>(FEXCore::ToUnderlying(cond) ^ 1);
}
void and_(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t n, uint32_t immr, uint32_t imms) {
constexpr uint32_t Op = 0b001'0010'00 << 22;
DataProcessing_Logical_Imm(Op, s, rd, rn, n, immr, imms);
+63 -123
View File
@@ -20,31 +20,23 @@ public:
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 0, Cond, Imm);
}
[[nodiscard]] BranchEncodeSucceeded b(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
void b(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 0, Cond, Imm >> 2);
return BranchEncodeSucceeded::Success;
}
// Can't encode.
return BranchEncodeSucceeded::Failure;
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 0, Cond, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded b(ARMEmitter::Condition Cond, ForwardLabel* Label) {
void b(ARMEmitter::Condition Cond, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 0, Cond, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded b(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
void b(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return b(Cond, &Label->Backward);
b(Cond, &Label->Backward);
} else {
return b(Cond, &Label->Forward);
b(Cond, &Label->Forward);
}
}
@@ -53,32 +45,24 @@ public:
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 1, Cond, Imm);
}
[[nodiscard]] BranchEncodeSucceeded bc(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
void bc(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 1, Cond, Imm >> 2);
return BranchEncodeSucceeded::Success;
}
// Can't encode.
return BranchEncodeSucceeded::Failure;
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 1, Cond, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded bc(ARMEmitter::Condition Cond, ForwardLabel* Label) {
void bc(ARMEmitter::Condition Cond, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
constexpr uint32_t Op = 0b0101'010 << 25;
Branch_Conditional(Op, 0, 1, Cond, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded bc(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
void bc(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return bc(Cond, &Label->Backward);
bc(Cond, &Label->Backward);
} else {
return bc(Cond, &Label->Forward);
bc(Cond, &Label->Forward);
}
}
@@ -114,32 +98,25 @@ public:
UnconditionalBranch(Op, Imm);
}
[[nodiscard]] BranchEncodeSucceeded b(const BackwardLabel* Label) {
void b(const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
if (Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b0001'01 << 26;
UnconditionalBranch(Op, Imm >> 2);
return BranchEncodeSucceeded::Success;
}
LOGMAN_THROW_A_FMT(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0), "Unscaled offset too large");
constexpr uint32_t Op = 0b0001'01 << 26;
// Can't encode.
return BranchEncodeSucceeded::Failure;
UnconditionalBranch(Op, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded b(ForwardLabel* Label) {
void b(ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::B});
constexpr uint32_t Op = 0b0001'01 << 26;
UnconditionalBranch(Op, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded b(BiDirectionalLabel* Label) {
void b(BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return b(&Label->Backward);
b(&Label->Backward);
} else {
return b(&Label->Forward);
b(&Label->Forward);
}
}
@@ -149,33 +126,25 @@ public:
UnconditionalBranch(Op, Imm);
}
[[nodiscard]] BranchEncodeSucceeded bl(const BackwardLabel* Label) {
void bl(const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
if (Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b1001'01 << 26;
UnconditionalBranch(Op, Imm >> 2);
LOGMAN_THROW_A_FMT(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0), "Unscaled offset too large");
constexpr uint32_t Op = 0b1001'01 << 26;
return BranchEncodeSucceeded::Success;
}
// Can't encode.
return BranchEncodeSucceeded::Failure;
UnconditionalBranch(Op, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded bl(ForwardLabel* Label) {
void bl(ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::B});
constexpr uint32_t Op = 0b1001'01 << 26;
UnconditionalBranch(Op, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded bl(BiDirectionalLabel* Label) {
void bl(BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return bl(&Label->Backward);
bl(&Label->Backward);
} else {
return bl(&Label->Forward);
bl(&Label->Forward);
}
}
@@ -186,35 +155,28 @@ public:
CompareAndBranch(Op, s, rt, Imm);
}
[[nodiscard]] BranchEncodeSucceeded cbz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
void cbz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b0011'0100 << 24;
CompareAndBranch(Op, s, rt, Imm >> 2);
return BranchEncodeSucceeded::Success;
}
constexpr uint32_t Op = 0b0011'0100 << 24;
// Can't encode.
return BranchEncodeSucceeded::Failure;
CompareAndBranch(Op, s, rt, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded cbz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
void cbz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
constexpr uint32_t Op = 0b0011'0100 << 24;
CompareAndBranch(Op, s, rt, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded cbz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
void cbz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return cbz(s, rt, &Label->Backward);
cbz(s, rt, &Label->Backward);
} else {
return cbz(s, rt, &Label->Forward);
cbz(s, rt, &Label->Forward);
}
}
@@ -224,35 +186,28 @@ public:
CompareAndBranch(Op, s, rt, Imm);
}
[[nodiscard]] BranchEncodeSucceeded cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
void cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b0011'0101 << 24;
CompareAndBranch(Op, s, rt, Imm >> 2);
return BranchEncodeSucceeded::Success;
}
constexpr uint32_t Op = 0b0011'0101 << 24;
// Can't encode.
return BranchEncodeSucceeded::Failure;
CompareAndBranch(Op, s, rt, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
void cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
constexpr uint32_t Op = 0b0011'0101 << 24;
CompareAndBranch(Op, s, rt, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
void cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return cbnz(s, rt, &Label->Backward);
cbnz(s, rt, &Label->Backward);
} else {
return cbnz(s, rt, &Label->Forward);
cbnz(s, rt, &Label->Forward);
}
}
@@ -262,35 +217,28 @@ public:
TestAndBranch(Op, rt, Bit, Imm);
}
[[nodiscard]] BranchEncodeSucceeded tbz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
void tbz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
LOGMAN_THROW_A_FMT(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0), "Unscaled offset too large");
if (Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b0011'0110 << 24;
TestAndBranch(Op, rt, Bit, Imm >> 2);
return BranchEncodeSucceeded::Success;
}
constexpr uint32_t Op = 0b0011'0110 << 24;
// Can't encode.
return BranchEncodeSucceeded::Failure;
TestAndBranch(Op, rt, Bit, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded tbz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
void tbz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::TEST_BRANCH});
constexpr uint32_t Op = 0b0011'0110 << 24;
TestAndBranch(Op, rt, Bit, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded tbz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
void tbz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return tbz(rt, Bit, &Label->Backward);
tbz(rt, Bit, &Label->Backward);
} else {
return tbz(rt, Bit, &Label->Forward);
tbz(rt, Bit, &Label->Forward);
}
}
@@ -299,35 +247,27 @@ public:
TestAndBranch(Op, rt, Bit, Imm);
}
[[nodiscard]] BranchEncodeSucceeded tbnz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
void tbnz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
LOGMAN_THROW_A_FMT(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0), "Unscaled offset too large");
if (Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0)) [[likely]] {
constexpr uint32_t Op = 0b0011'0111 << 24;
TestAndBranch(Op, rt, Bit, Imm >> 2);
return BranchEncodeSucceeded::Success;
}
constexpr uint32_t Op = 0b0011'0111 << 24;
// Can't encode.
return BranchEncodeSucceeded::Failure;
TestAndBranch(Op, rt, Bit, Imm >> 2);
}
[[nodiscard]] BranchEncodeSucceeded tbnz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
void tbnz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::TEST_BRANCH});
constexpr uint32_t Op = 0b0011'0111 << 24;
TestAndBranch(Op, rt, Bit, 0);
// Forward label doesn't know if it can encode until Bind.
return BranchEncodeSucceeded::Success;
}
[[nodiscard]] BranchEncodeSucceeded tbnz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
void tbnz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
if (Label->Backward.Location) {
return tbnz(rt, Bit, &Label->Backward);
tbnz(rt, Bit, &Label->Backward);
} else {
return tbnz(rt, Bit, &Label->Forward);
tbnz(rt, Bit, &Label->Forward);
}
}
+15 -52
View File
@@ -586,11 +586,6 @@ concept IsXOrWRegister = std::is_same_v<T, XRegister> || std::is_same_v<T, WRegi
template<typename T>
concept IsQOrDRegister = std::is_same_v<T, QRegister> || std::is_same_v<T, DRegister>;
enum class BranchEncodeSucceeded {
Success,
Failure,
};
// Whether or not a given set of vector registers are sequential
// in increasing order as far as the register file is concerned (modulo its size)
//
@@ -643,25 +638,19 @@ public:
// Bind a backward label to an address.
// Address that is bound is the current emitter location.
[[nodiscard]] bool Bind(BackwardLabel* Label) {
void Bind(BackwardLabel* Label) {
LOGMAN_THROW_A_FMT(Label->Location == nullptr, "Trying to bind a label twice");
Label->Location = GetCursorAddress<uint8_t*>();
// Always binds because it is only storing a location.
return true;
}
[[nodiscard]] bool Bind(const ForwardLabel::Reference* Label) {
void Bind(const ForwardLabel::Reference* Label) {
uint8_t* CurrentAddress = GetCursorAddress<uint8_t*>();
// Patch up the instructions
switch (Label->Type) {
case ForwardLabel::InstType::ADR: {
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
if (!IsADRRange(Imm)) [[unlikely]] {
// Can't bind.
return false;
}
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
uint32_t Inst = *Instruction & ~InstMask;
@@ -673,12 +662,7 @@ public:
case ForwardLabel::InstType::ADRP: {
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
if (!(IsADRPRange(Imm) && IsADRPAligned(Imm))) [[unlikely]] {
// Can't bind.
return false;
}
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
Imm >>= 12;
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
@@ -688,13 +672,11 @@ public:
*Instruction = Inst;
break;
}
case ForwardLabel::InstType::B: {
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
if (!(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0))) [[unlikely]] {
// Can't bind.
return false;
}
LOGMAN_THROW_A_FMT(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0), "Unscaled offset too large");
Imm >>= 2;
uint32_t InstMask = 0x3FF'FFFF;
uint32_t Offset = static_cast<uint32_t>(Imm) & InstMask;
@@ -704,13 +686,11 @@ public:
break;
}
case ForwardLabel::InstType::TEST_BRANCH: {
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
if (!(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0))) [[unlikely]] {
// Can't bind.
return false;
}
LOGMAN_THROW_A_FMT(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0), "Unscaled offset too large");
Imm >>= 2;
uint32_t InstMask = 0x3FFF;
uint32_t Offset = static_cast<uint32_t>(Imm) & InstMask;
@@ -724,10 +704,7 @@ public:
case ForwardLabel::InstType::RELATIVE_LOAD: {
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
if (!(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0))) [[unlikely]] {
// Can't bind.
return false;
}
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
Imm >>= 2;
uint32_t InstMask = 0x7'FFFF;
uint32_t Offset = static_cast<uint32_t>(Imm) & InstMask;
@@ -776,41 +753,27 @@ public:
}
default: LOGMAN_MSG_A_FMT("Unexpected inst type in label fixup");
}
return true;
}
// Bind a forward label to a location.
// This walks all the instructions in the label's vector.
// Then backpatching all instructions that have used the label.
[[nodiscard]] bool Bind(ForwardLabel* Label) {
bool Bound = true;
void Bind(ForwardLabel* Label) {
if (Label->FirstInst.Location) {
Bound &= Bind(&Label->FirstInst);
Bind(&Label->FirstInst);
}
for (auto& Inst : Label->Insts) {
Bound &= Bind(&Inst);
Bind(&Inst);
}
return Bound;
}
// Bind a bidirectional location to a location.
// Binds both forwards and backwards depending on how the label was used.
[[nodiscard]] bool Bind(BiDirectionalLabel* Label) {
bool Bound = true;
void Bind(BiDirectionalLabel* Label) {
if (!Label->Backward.Location) {
Bound &= Bind(&Label->Backward);
Bind(&Label->Backward);
}
Bound &= Bind(&Label->Forward);
return Bound;
}
static constexpr Condition InvertCondition(Condition cond) {
// These behave as always, so it makes no sense to allow inverting these.
LOGMAN_THROW_A_FMT(cond != Condition::CC_AL && cond != Condition::CC_NV, "Cannot invert CC_AL or CC_NV");
return static_cast<Condition>(FEXCore::ToUnderlying(cond) ^ 1);
Bind(&Label->Forward);
}
#include <CodeEmitter/VixlUtils.inl>
+3 -3
View File
@@ -5125,7 +5125,7 @@ private:
requires (std::is_same_v<T, float> || std::is_same_v<T, double>)
[[nodiscard]]
static bool IsValidFPValueForImm8(T value) {
const uint64_t bits = FEXCore::BitCast<FloatToEquivalentUInt<T>>(value);
const uint64_t bits = std::bit_cast<FloatToEquivalentUInt<T>>(value);
const uint64_t datasize_idx = FEXCore::ilog2(sizeof(T)) - 1;
static constexpr std::array mantissa_masks {
@@ -5171,7 +5171,7 @@ protected:
LOGMAN_THROW_A_FMT(IsValidFPValueForImm8(value), "Value ({}) cannot be encoded into an 8-bit immediate", value);
#endif
const auto bits = FEXCore::BitCast<uint32_t>(value);
const auto bits = std::bit_cast<uint32_t>(value);
const auto sign = (bits & 0x80000000) >> 24;
const auto expb2 = (bits & 0x20000000) >> 23;
const auto b5_to_0 = (bits >> 19) & 0x3F;
@@ -5184,7 +5184,7 @@ protected:
LOGMAN_THROW_A_FMT(IsValidFPValueForImm8(value), "Value ({}) cannot be encoded into an 8-bit immediate", value);
#endif
const auto bits = FEXCore::BitCast<uint64_t>(value);
const auto bits = std::bit_cast<uint64_t>(value);
const auto sign = (bits & 0x80000000'00000000) >> 56;
const auto expb2 = (bits & 0x20000000'00000000) >> 55;
const auto b5_to_0 = (bits >> 48) & 0x3F;
+2 -2
View File
@@ -2,8 +2,8 @@
let
toolchain = pkgs.fetchzip {
url = "https://github.com/bylaws/llvm-mingw/releases/download/20250305/llvm-mingw-20250305-ucrt-ubuntu-20.04-aarch64.tar.xz";
sha256 = "sha256-cA03/ab9O61eO9+S2JzIXD4V0HzTXK5/AYyxW2d73Po=";
url = "https://github.com/bylaws/llvm-mingw/releases/download/20250920/llvm-mingw-20250920-ucrt-ubuntu-22.04-aarch64.tar.xz";
sha256 = "sha256-LaojKjC8KzY+soW5u6eoDoXE3qtYk9Ejr7M3enTqRAE=";
};
cmakeToolchainFile = pkgs.substitute {
+1 -1
+53 -59
View File
@@ -58,10 +58,10 @@ class OpDefinition:
JITDispatch: bool
JITDispatchOverride: str
TiedSource: int
Inline: list
Arguments: list
EmitValidation: list
Desc: list
Inline: list[str]
Arguments: list[OpArgument]
EmitValidation: list[str]
Desc: list[str]
def __init__(self):
self.Name = None
@@ -92,19 +92,14 @@ class OpDefinition:
attrs = vars(self)
print(", ".join("%s: %s" % item for item in attrs.items()))
IRTypesToCXX = {}
CXXTypeToIR = {}
IROps = []
IRTypesToCXX: dict[str, IRType] = {}
CXXTypeToIR: dict[str, IRType] = {}
IROps: list[OpDefinition] = []
IROpNameMap = {}
IROpNameSet: set[str] = set()
def is_ssa_type(type):
if (type == "SSA" or
type == "GPR" or
type == "GPRPair" or
type == "FPR"):
return True
return False
def is_ssa_type(op_type: str):
return op_type in {"SSA", "GPR", "GPRPair", "FPR"}
def parse_irtypes(irtypes):
for op_key, op_val in irtypes.items():
@@ -219,11 +214,8 @@ def parse_ops(ops):
OpArg.DefaultInitializer = DefaultInit[1][:-1]
# If SSA type then we can generate validation for this op
if (OpArg.IsSSA and
(OpArg.Type == "GPR" or
OpArg.Type == "GPRPair" or
OpArg.Type == "FPR")):
OpDef.EmitValidation.append(f"GetOpRegClass({ArgName}) == InvalidClass || WalkFindRegClass({ArgName}) == {OpArg.Type}Class")
if OpArg.IsSSA and OpArg.Type in {"GPR", "GPRPair", "FPR"}:
OpDef.EmitValidation.append(f"GetOpRegClass({ArgName}) == RegClass::Invalid || WalkFindRegClass({ArgName}) == RegClass::{OpArg.Type}")
OpArg.Name = ArgName
OpArg.NameWithPrefix = NameWithPrefix
@@ -296,22 +288,29 @@ def parse_ops(ops):
#OpDef.print()
# Error on duplicate op
if OpDef.Name in IROpNameMap:
if OpDef.Name in IROpNameSet:
ExitError("Duplicate Op defined! {}".format(OpDef.Name))
IROps.append(OpDef)
IROpNameMap[OpDef.Name] = 1
IROpNameSet.add(OpDef.Name)
# Print out enum values
def print_enums():
def print_enums(enums):
output_file.write("#ifdef IROP_ENUM\n")
output_file.write("enum IROps : uint16_t {\n")
for op in IROps:
output_file.write("\tOP_{},\n" .format(op.Name.upper()))
output_file.write("};\n")
for name, members in enums.items():
output_file.write(f"enum {name} {{\n")
for member in members:
if member:
output_file.write(f"\t{member}\n")
else:
output_file.write("\n")
output_file.write("};\n\n")
output_file.write("#undef IROP_ENUM\n")
output_file.write("#endif\n\n")
@@ -408,7 +407,7 @@ def print_ir_sizes():
[[nodiscard, gnu::const]] std::string_view const& GetName(IROps Op);
[[nodiscard, gnu::const]] uint8_t GetArgs(IROps Op);
[[nodiscard, gnu::const]] uint8_t GetRAArgs(IROps Op);
[[nodiscard, gnu::const]] FEXCore::IR::RegisterClassType GetRegClass(IROps Op);
[[nodiscard, gnu::const]] FEXCore::IR::RegClass GetRegClass(IROps Op);
[[nodiscard, gnu::const]] bool HasSideEffects(IROps Op);
[[nodiscard, gnu::const]] bool ImplicitFlagClobber(IROps Op);
[[nodiscard, gnu::const]] bool GetHasDest(IROps Op);
@@ -422,30 +421,29 @@ def print_ir_sizes():
def print_ir_reg_classes():
output_file.write("#ifdef IROP_REG_CLASSES_IMPL\n")
output_file.write("constexpr std::array<FEXCore::IR::RegisterClassType, IROps::OP_LAST + 1> IRRegClasses = {\n")
output_file.write("constexpr std::array<FEXCore::IR::RegClass, IROps::OP_LAST + 1> IRRegClasses = {\n")
for op in IROps:
if op.Name == "Last":
output_file.write("\tFEXCore::IR::InvalidClass,\n")
output_file.write("\tRegClass::Invalid,\n")
else:
Class = "Invalid"
if op.HasDest and op.DestType == None:
if op.HasDest and op.DestType is None:
ExitError("IR op {} has destination with no destination class".format(op.Name))
if op.HasDest and op.DestType == "SSA": # Special case SSA type
output_file.write("\tFEXCore::IR::ComplexClass,\n")
output_file.write("\tRegClass::Complex,\n")
elif op.HasDest:
output_file.write("\tFEXCore::IR::{}Class,\n".format(op.DestType))
output_file.write("\tRegClass::{},\n".format(op.DestType))
else:
# No destination so it has an invalid destination class
output_file.write("\tFEXCore::IR::InvalidClass, // No destination\n")
output_file.write("\tRegClass::Invalid, // No destination\n")
output_file.write("};\n\n")
output_file.write("// Make sure our array maps directly to the IROps enum\n")
output_file.write("static_assert(IRRegClasses[IROps::OP_LAST] == FEXCore::IR::InvalidClass);\n\n")
output_file.write("static_assert(IRRegClasses[IROps::OP_LAST] == RegClass::Invalid);\n\n")
output_file.write("FEXCore::IR::RegisterClassType GetRegClass(IROps Op) { return IRRegClasses[Op]; }\n\n")
output_file.write("FEXCore::IR::RegClass GetRegClass(IROps Op) { return IRRegClasses[Op]; }\n\n")
output_file.write("#undef IROP_REG_CLASSES_IMPL\n")
output_file.write("#endif\n\n")
@@ -568,9 +566,7 @@ def print_ir_arg_printer():
SSAArgNum = 0
FirstArg = True
for i in range(0, len(op.Arguments)):
arg = op.Arguments[i]
for arg in op.Arguments:
# No point printing temporaries that we can't recover
if arg.Temporary:
continue
@@ -671,7 +667,7 @@ def print_ir_allocator_helpers():
output_file.write("\t\treturn HeaderOp->Op;\n")
output_file.write("\t}\n\n")
output_file.write("\tFEXCore::IR::RegisterClassType GetOpRegClass(const OrderedNode *Op) const {\n")
output_file.write("\tFEXCore::IR::RegClass GetOpRegClass(const OrderedNode *Op) const {\n")
output_file.write("\t\treturn GetRegClass(GetOpType(Op));\n")
output_file.write("\t}\n\n")
@@ -685,22 +681,21 @@ def print_ir_allocator_helpers():
output_file.write("\tIRPair<IROp_{}> _{}(" .format(op.Name, op.Name))
# Output SSA args first
for i in range(0, len(op.Arguments)):
arg = op.Arguments[i]
LastArg = len(op.Arguments) - i - 1 == 0
for i, arg in enumerate(op.Arguments):
LastArg = i == len(op.Arguments) - 1
if arg.Temporary:
CType = IRTypesToCXX[arg.Type].CXXName
output_file.write("{} {}".format(CType, arg.Name));
output_file.write("{} {}".format(CType, arg.Name))
elif arg.IsSSA:
# SSA value
output_file.write("OrderedNodeWrapper {}".format(arg.Name))
else:
# User defined op that is stored
CType = IRTypesToCXX[arg.Type].CXXName
output_file.write("{} {}".format(CType, arg.Name));
output_file.write("{} {}".format(CType, arg.Name))
if arg.DefaultInitializer != None:
if arg.DefaultInitializer:
output_file.write(" = {}".format(arg.DefaultInitializer))
if not LastArg:
@@ -758,20 +753,19 @@ def print_ir_allocator_helpers():
if op.SSAArgNum:
output_file.write("\tIRPair<IROp_{}> _{}(" .format(op.Name, op.Name))
for i in range(0, len(op.Arguments)):
arg = op.Arguments[i]
LastArg = len(op.Arguments) - i - 1 == 0
for i, arg in enumerate(op.Arguments):
LastArg = i == len(op.Arguments) - 1
if arg.Temporary:
CType = IRTypesToCXX[arg.Type].CXXName
output_file.write("{} {}".format(CType, arg.Name));
output_file.write("{} {}".format(CType, arg.Name))
elif arg.IsSSA:
output_file.write("OrderedNode *{}".format(arg.Name))
else:
CType = IRTypesToCXX[arg.Type].CXXName
output_file.write("{} {}".format(CType, arg.Name));
output_file.write("{} {}".format(CType, arg.Name))
if arg.DefaultInitializer != None:
if arg.DefaultInitializer:
output_file.write(" = {}".format(arg.DefaultInitializer))
if not LastArg:
@@ -812,16 +806,15 @@ def print_ir_allocator_helpers():
print_validation(op)
output_file.write(f"\t\treturn _{op.Name}(")
for i in range(0, len(op.Arguments)):
arg = op.Arguments[i]
LastArg = len(op.Arguments) - i - 1 == 0
for i, arg in enumerate(op.Arguments):
LastArg = i == len(op.Arguments) - 1
output_file.write(arg.Name)
if arg.IsSSA:
output_file.write("->Wrapped(ListDataBegin)")
if not LastArg:
output_file.write(", ")
output_file.write(");\n");
output_file.write("\t}\n\n");
output_file.write(");\n")
output_file.write("\t}\n\n")
output_file.write("#undef IROP_ALLOCATE_HELPERS\n")
output_file.write("#endif\n")
@@ -852,8 +845,8 @@ def print_ir_dispatcher_dispatch():
output_dispatch_file.write("#endif\n")
if (len(sys.argv) < 4):
ExitError()
if len(sys.argv) < 4:
ExitError("Insufficient parameters passed to script")
output_filename = sys.argv[2]
output_dispatcher_filename = sys.argv[3]
@@ -865,6 +858,7 @@ json_file.close()
json_object = json.loads(json_text)
json_object = {k.upper(): v for k, v in json_object.items()}
enums = json_object["ENUMS"]
ops = json_object["OPS"]
irtypes = json_object["IRTYPES"]
defines = json_object["DEFINES"]
@@ -874,7 +868,7 @@ parse_ops(ops)
output_file = open(output_filename, "w")
print_enums()
print_enums(enums)
print_ir_structs(defines)
print_ir_sizes()
print_ir_reg_classes()
-1
View File
@@ -66,7 +66,6 @@ set (SRCS
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
Interface/IR/Passes/RegisterAllocationPass.cpp
Interface/IR/Passes/x87StackOptimizationPass.cpp
Utils/LongJump.cpp
Utils/Telemetry.cpp
Utils/Threads.cpp
Utils/Profiler.cpp
+82 -8
View File
@@ -4,9 +4,9 @@
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/fextl/sstream.h>
#include <FEXCore/fextl/string.h>
#include <FEXHeaderUtils/BitUtils.h>
#include "cephes_128bit.h"
#include <bit>
#include <cmath>
#include <cstring>
#include <stdint.h>
@@ -501,12 +501,53 @@ struct FEX_PACKED X80SoftFloat {
float ToF32(softfloat_state* state) const {
const float32_t Result = extF80_to_f32(state, *this);
return FEXCore::BitCast<float>(Result);
return std::bit_cast<float>(Result);
}
bool IsSignalingNaN() const {
return (Exponent == 0x7FFF) && (Significand & 0x8000000000000000ULL) && !(Significand & 0x4000000000000000ULL) && // Bit 62 clear (signaling)
(Significand & 0x3FFFFFFFFFFFFFFFULL);
}
bool IsQuietNaN() const {
return (Exponent == 0x7FFF) && (Significand & 0x8000000000000000ULL) && (Significand & 0x4000000000000000ULL); // Bit 62 set (quiet)
}
// Helper to detect if this is any NaN
bool IsNaN() const {
return IsSignalingNaN() || IsQuietNaN();
}
// X87 value to F64 while preserving signaling nan property
double ToF64_PreserveNan(softfloat_state* state) const {
if (IsSignalingNaN()) {
// we keep it as a signaling nan in ieee754 in 64bits
uint64_t sign_bit = Sign ? 0x8000000000000000ULL : 0;
uint64_t exp_bits = 0x7FF0000000000000ULL;
uint64_t x87_frac = Significand & 0x3FFFFFFFFFFFFFFFULL;
uint64_t ieee_frac = (x87_frac >> 11) & 0x0007FFFFFFFFFFFFULL;
if (ieee_frac == 0) {
ieee_frac = 1;
}
ieee_frac &= ~0x0008000000000000ULL;
uint64_t result_bits = sign_bit | exp_bits | ieee_frac;
return std::bit_cast<double>(result_bits);
} else if (IsQuietNaN()) {
const float64_t Result = extF80_to_f64(state, *this);
uint64_t result_bits = std::bit_cast<uint64_t>(Result);
result_bits |= 0x0008000000000000ULL;
return std::bit_cast<double>(result_bits);
} else {
const float64_t Result = extF80_to_f64(state, *this);
return std::bit_cast<double>(Result);
}
}
double ToF64(softfloat_state* state) const {
const float64_t Result = extF80_to_f64(state, *this);
return FEXCore::BitCast<double>(Result);
return std::bit_cast<double>(Result);
}
FEXCore::VectorRegType ToVector() const {
@@ -518,7 +559,7 @@ struct FEX_PACKED X80SoftFloat {
BIGFLOAT ToFMax(softfloat_state* state) const {
#if BIGFLOATSIZE == 16
const float128_t Result = extF80_to_f128(state, *this);
return FEXCore::BitCast<BIGFLOAT>(Result);
return std::bit_cast<BIGFLOAT>(Result);
#else
BIGFLOAT result {};
memcpy(&result, this, sizeof(result));
@@ -577,18 +618,51 @@ struct FEX_PACKED X80SoftFloat {
}
X80SoftFloat(softfloat_state* state, const float rhs) {
*this = f32_to_extF80(state, FEXCore::BitCast<float32_t>(rhs));
*this = f32_to_extF80(state, std::bit_cast<float32_t>(rhs));
}
X80SoftFloat(softfloat_state* state, const double rhs) {
*this = f64_to_extF80(state, FEXCore::BitCast<float64_t>(rhs));
*this = f64_to_extF80(state, std::bit_cast<float64_t>(rhs));
}
// Create X80SoftFloat from double while preserving NaN signaling properties
static X80SoftFloat FromF64_PreserveNaN(softfloat_state* state, double value) {
uint64_t bits = std::bit_cast<uint64_t>(value);
// Check if it's a nan
if ((bits & 0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (bits & 0x000FFFFFFFFFFFFFULL) != 0) {
X80SoftFloat result;
result.Sign = (bits >> 63) & 1;
result.Exponent = 0x7FFF;
bool is_signaling = !(bits & 0x0008000000000000ULL);
uint64_t ieee_payload = bits & 0x0007FFFFFFFFFFFFULL;
// set bit 63 required for x87
result.Significand = 0x8000000000000000ULL;
if (is_signaling) { // clear bit 62 for signaling nan
result.Significand &= ~0x4000000000000000ULL;
} else { // clear bit 62 for quiet nan
result.Significand |= 0x4000000000000000ULL;
}
// ieee754 51-bit payload -> x87 62-bit payload
result.Significand |= (ieee_payload << 11) & 0x3FFFFFFFFFFFFFFFULL;
return result;
}
// For non-NaN values, use standard conversion
return X80SoftFloat(state, value);
}
X80SoftFloat(softfloat_state* state, BIGFLOAT rhs) {
#if BIGFLOATSIZE == 16
*this = f128_to_extF80(state, FEXCore::BitCast<float128_t>(rhs));
*this = f128_to_extF80(state, std::bit_cast<float128_t>(rhs));
#else
*this = FEXCore::BitCast<long double>(rhs);
*this = std::bit_cast<long double>(rhs);
#endif
}
+11 -47
View File
@@ -2,59 +2,23 @@
#pragma once
#include <FEXCore/fextl/string.h>
#include <cstdint>
#include <concepts>
#include <string_view>
#include <optional>
namespace FEXCore::StrConv {
inline bool Conv(std::string_view Value, bool* Result) {
*Result = std::strtoull(Value.data(), nullptr, 0);
template<std::integral T>
bool Conv(std::string_view Value, T* Result) {
if constexpr (std::is_signed_v<T>) {
*Result = static_cast<T>(std::strtoll(Value.data(), nullptr, 0));
} else {
*Result = static_cast<T>(std::strtoull(Value.data(), nullptr, 0));
}
return true;
}
inline bool Conv(std::string_view Value, uint8_t* Result) {
*Result = std::strtoul(Value.data(), nullptr, 0);
return true;
}
inline bool Conv(std::string_view Value, int8_t* Result) {
*Result = std::strtol(Value.data(), nullptr, 0);
return true;
}
inline bool Conv(std::string_view Value, uint16_t* Result) {
*Result = std::strtoul(Value.data(), nullptr, 0);
return true;
}
inline bool Conv(std::string_view Value, int16_t* Result) {
*Result = std::strtol(Value.data(), nullptr, 0);
return true;
}
inline bool Conv(std::string_view Value, uint32_t* Result) {
*Result = std::strtoul(Value.data(), nullptr, 0);
return true;
}
inline bool Conv(std::string_view Value, int32_t* Result) {
*Result = std::strtol(Value.data(), nullptr, 0);
return true;
}
inline bool Conv(std::string_view Value, uint64_t* Result) {
*Result = std::strtoull(Value.data(), nullptr, 0);
return true;
}
inline bool Conv(std::string_view Value, int64_t* Result) {
*Result = std::strtoll(Value.data(), nullptr, 0);
return true;
}
template<typename T, typename = std::enable_if<std::is_enum<T>::value, T>>
inline bool Conv(std::string_view Value, T* Result) {
*Result = static_cast<T>(std::stoull(Value.data(), nullptr, 0));
template<typename T, typename = std::enable_if_t<std::is_enum_v<T>, T>>
bool Conv(std::string_view Value, T* Result) {
*Result = static_cast<T>(std::strtoull(Value.data(), nullptr, 0));
return true;
}
-2
View File
@@ -4,8 +4,6 @@
#ifdef _M_X86_64
#include <xmmintrin.h>
#include <immintrin.h>
#else
#include <cstdint>
#endif
namespace FEXCore {
+13 -9
View File
@@ -59,7 +59,9 @@
"ENABLEWFXT": "enablewfxt",
"DISABLEWFXT": "disablewfxt",
"ENABLE3DNOW": "enable3dnow",
"DISABLE3DNOW": "disable3dnow"
"DISABLE3DNOW": "disable3dnow",
"ENABLESSE4A": "enablesse4a",
"DISABLESSE4A": "disablesse4a"
},
"Desc": [
"Allows controlling of the CPU features in the JIT.",
@@ -82,7 +84,8 @@
"\t{enable,disable}svebitperm: Will force enable or disable svebitperm even if the host doesn't support it",
"\t{enable,disable}preserveallabi: Will force enable or disable preserve_all abi even if the host doesn't support it",
"\t{enable,disable}wfxt: Will force enable or disable wfxt even if the host doesn't support it",
"\t{enable,disable}3dnow: Will force enable or disable 3DNow even if the host doesn't support it"
"\t{enable,disable}3dnow: Will force enable or disable 3DNow! even if the host doesn't support it",
"\t{enable,disable}sse4a: Will force enable or disable SSE4a even if the host doesn't support it"
]
},
"SmallTSCScale": {
@@ -327,13 +330,6 @@
"Enables FEX's low-overhead sampling profile statistics.",
"Requires a supported version of Mangohud to see the results"
]
},
"TraceProfiler": {
"Type": "bool",
"Default": "false",
"Desc": [
"Enables FEX's trace profiler. Using gpuvis or tracy"
]
}
},
"Hacks": {
@@ -403,6 +399,14 @@
"Emulates X87 floating point using 64-bit precision. This reduces emulation accuracy and may result in rendering bugs."
]
},
"X87StrictReducedPrecision": {
"Type": "bool",
"Default": "false",
"Desc": [
"Enables stricter X87 floating point behavior when X87ReducedPrecision is enabled.",
"Adds additional checks and implementations like NaN propagation for better compatibility."
]
},
"ABILocalFlags": {
"Type": "bool",
"Default": "false",
@@ -207,6 +207,7 @@ public:
FEX_CONFIG_OPT(GDBSymbols, GDBSYMBOLS);
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION);
FEX_CONFIG_OPT(x87StrictReducedPrecision, X87STRICTREDUCEDPRECISION);
FEX_CONFIG_OPT(DisableTelemetry, DISABLETELEMETRY);
FEX_CONFIG_OPT(DisableVixlIndirectCalls, DISABLE_VIXL_INDIRECT_RUNTIME_CALLS);
FEX_CONFIG_OPT(SmallTSCScale, SMALLTSCSCALE);
@@ -322,6 +323,13 @@ protected:
}
}
void UpdateX87PrecisionConfig() {
// If strict reduced precision is enabled, automatically enable reduced precision
if (Config.x87StrictReducedPrecision() && !Config.x87ReducedPrecision()) {
FEXCore::Config::Set(FEXCore::Config::CONFIG_X87REDUCEDPRECISION, "1");
}
}
private:
/**
* @brief Initializes the JIT compilers for the thread
+13 -11
View File
@@ -7,7 +7,7 @@
namespace FEXCore::IR {
Ref LoadEffectiveAddress(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSize, bool AddSegmentBase, bool AllowUpperGarbage) {
Ref LoadEffectiveAddress(IREmitter* IREmit, const AddressMode& A, IR::OpSize GPRSize, bool AddSegmentBase, bool AllowUpperGarbage) {
Ref Tmp = A.Base;
if (A.Offset) {
@@ -51,8 +51,8 @@ Ref LoadEffectiveAddress(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSize, b
return Tmp ?: IREmit->Constant(0);
}
AddressMode SelectAddressMode(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSize, bool HostSupportsTSOImm9, bool AtomicTSO, bool Vector,
IR::OpSize AccessSize) {
AddressMode SelectAddressMode(IREmitter* IREmit, const AddressMode& A, IR::OpSize GPRSize, bool HostSupportsTSOImm9, bool AtomicTSO,
bool Vector, IR::OpSize AccessSize) {
const auto Is32Bit = GPRSize == OpSize::i32Bit;
const auto GPRSizeMatchesAddrSize = A.AddrSize == GPRSize;
const auto OffsetIndexToLargeFor32Bit = Is32Bit && (A.Offset <= -16384 || A.Offset >= 16384);
@@ -103,7 +103,7 @@ AddressMode SelectAddressMode(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSi
return {
.Base = LoadEffectiveAddress(IREmit, B, GPRSize, true /* AddSegmentBase */, false),
.Index = IREmit->Constant(A.Offset),
.IndexType = MEM_OFFSET_SXTX,
.IndexType = MemOffsetType::SXTX,
.IndexScale = 1,
};
}
@@ -111,15 +111,17 @@ AddressMode SelectAddressMode(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSi
if (AtomicTSO) {
// TODO: LRCPC3 support for vector Imm9.
} else if (!Is32Bit && A.Base && (A.Index || A.Segment) && !A.Offset && (A.IndexScale == 1 || A.IndexScale == AccessSizeAsImm)) {
AddressMode B = A;
// ScaledRegisterLoadstore
if (A.Index && A.Segment) {
A.Base = IREmit->Add(GPRSize, A.Base, A.Segment);
} else if (A.Segment) {
A.Index = A.Segment;
A.IndexScale = 1;
if (B.Index && B.Segment) {
B.Base = IREmit->Add(GPRSize, B.Base, B.Segment);
} else if (B.Segment) {
B.Index = B.Segment;
B.IndexScale = 1;
}
return A;
return B;
}
if (Vector || !AtomicTSO) {
@@ -134,7 +136,7 @@ AddressMode SelectAddressMode(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSi
return {
.Base = LoadEffectiveAddress(IREmit, B, GPRSize, true /* AddSegmentBase */, false),
.Index = IREmit->Constant(A.Offset),
.IndexType = MEM_OFFSET_SXTX,
.IndexType = MemOffsetType::SXTX,
.IndexScale = 1,
};
}
+7 -6
View File
@@ -11,17 +11,18 @@ struct AddressMode {
Ref Segment {nullptr};
Ref Base {nullptr};
Ref Index {nullptr};
MemOffsetType IndexType = MEM_OFFSET_SXTX;
uint8_t IndexScale = 1;
int64_t Offset = 0;
MemOffsetType IndexType = MemOffsetType::SXTX;
uint8_t IndexScale = 1;
// Size in bytes for the address calculation. 8 for an arm64 hardware mode.
IR::OpSize AddrSize;
bool NonTSO;
};
Ref LoadEffectiveAddress(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSize, bool AddSegmentBase, bool AllowUpperGarbage = false);
AddressMode SelectAddressMode(IREmitter* IREmit, AddressMode A, IR::OpSize GPRSize, bool HostSupportsTSOImm9, bool AtomicTSO, bool Vector,
IR::OpSize AccessSize);
Ref LoadEffectiveAddress(IREmitter* IREmit, const AddressMode& A, IR::OpSize GPRSize, bool AddSegmentBase, bool AllowUpperGarbage = false);
AddressMode SelectAddressMode(IREmitter* IREmit, const AddressMode& A, IR::OpSize GPRSize, bool HostSupportsTSOImm9, bool AtomicTSO,
bool Vector, IR::OpSize AccessSize);
}; // namespace FEXCore::IR
} // namespace FEXCore::IR
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: MIT
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "FEXCore/Core/X86Enums.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
@@ -1,30 +1,31 @@
// SPDX-License-Identifier: MIT
#pragma once
#include "FEXCore/Utils/EnumUtils.h"
#include "Interface/Core/JIT/Relocations.h"
#ifdef VIXL_DISASSEMBLER
#include <aarch64/disasm-aarch64.h>
#include <FEXCore/Config/Config.h>
#include <FEXCore/fextl/memory.h>
#include <FEXCore/fextl/vector.h>
#endif
#ifdef VIXL_SIMULATOR
#include <aarch64/simulator-aarch64.h>
#include <aarch64/simulator-constants-aarch64.h>
#endif
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Config/Config.h>
#include <FEXCore/fextl/vector.h>
#include <CodeEmitter/Emitter.h>
#include <CodeEmitter/Registers.h>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <span>
namespace FEXCore::Context {
class ContextImpl;
}
namespace FEXCore::X86State {
enum X86Reg : uint32_t;
}
namespace FEXCore::CPU {
// Contains the address to the currently available CPU state
+11 -4
View File
@@ -1,14 +1,17 @@
// SPDX-License-Identifier: MIT
#include "FEXCore/IR/IR.h"
#include "FEXCore/Utils/AllocatorHooks.h"
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUBackend.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/AllocatorHooks.h>
#include <FEXCore/Utils/PrctlUtils.h>
#include <cstdint>
#include "LookupCache.h"
#ifndef _WIN32
#include <linux/prctl.h>
#include <sys/prctl.h>
#endif
@@ -357,6 +360,10 @@ namespace CPU {
LogMan::Msg::EFmt("Failed to mprotect last page of code buffer.");
}
#ifndef _WIN32
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, "FEXMemJIT");
#endif
LookupCache = fextl::make_unique<GuestToHostMap>();
}
+32 -32
View File
@@ -909,38 +909,38 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0001h(uint32_t Leaf) con
Res.eax = FAMILY_IDENTIFIER;
Res.ecx = (1 << 0) | // LAHF/SAHF
(1 << 1) | // 0 = Single core product, 1 = multi core product
(0 << 2) | // SVM
(1 << 3) | // Extended APIC register space
(0 << 4) | // LOCK MOV CR0 means MOV CR8
(1 << 5) | // ABM instructions
(0 << 6) | // SSE4a
(0 << 7) | // Misaligned SSE mode
(1 << 8) | // PREFETCHW
(0 << 9) | // OS visible workaround support
(0 << 10) | // Instruction based sampling support
(0 << 11) | // XOP
(0 << 12) | // SKINIT
(0 << 13) | // Watchdog timer support
(0 << 14) | // Reserved
(0 << 15) | // Lightweight profiling support
(0 << 16) | // FMA4
(1 << 17) | // Translation cache extension
(0 << 18) | // Reserved
(0 << 19) | // Reserved
(0 << 20) | // Reserved
(0 << 21) | // XOP-TBM
(0 << 22) | // Topology extensions support
(0 << 23) | // Core performance counter extensions
(0 << 24) | // NB performance counter extensions
(0 << 25) | // Reserved
(0 << 26) | // Data breakpoints extensions
(0 << 27) | // Performance TSC
(0 << 28) | // L2 perf counter extensions
(0 << 29) | // MONITORX
(0 << 30) | // Reserved
(0 << 31); // Reserved
Res.ecx = (1 << 0) | // LAHF/SAHF
(1 << 1) | // 0 = Single core product, 1 = multi core product
(0 << 2) | // SVM
(1 << 3) | // Extended APIC register space
(0 << 4) | // LOCK MOV CR0 means MOV CR8
(1 << 5) | // ABM instructions
(CTX->HostFeatures.SupportsSSE4a << 6) | // SSE4a
(0 << 7) | // Misaligned SSE mode
(1 << 8) | // PREFETCHW
(0 << 9) | // OS visible workaround support
(0 << 10) | // Instruction based sampling support
(0 << 11) | // XOP
(0 << 12) | // SKINIT
(0 << 13) | // Watchdog timer support
(0 << 14) | // Reserved
(0 << 15) | // Lightweight profiling support
(0 << 16) | // FMA4
(1 << 17) | // Translation cache extension
(0 << 18) | // Reserved
(0 << 19) | // Reserved
(0 << 20) | // Reserved
(0 << 21) | // XOP-TBM
(0 << 22) | // Topology extensions support
(0 << 23) | // Core performance counter extensions
(0 << 24) | // NB performance counter extensions
(0 << 25) | // Reserved
(0 << 26) | // Data breakpoints extensions
(0 << 27) | // Performance TSC
(0 << 28) | // L2 perf counter extensions
(0 << 29) | // MONITORX
(0 << 30) | // Reserved
(0 << 31); // Reserved
Res.edx = (1 << 0) | // FPU
(1 << 1) | // Virtual mode extensions
+5 -10
View File
@@ -57,20 +57,13 @@ $end_info$
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <fcntl.h>
#include <functional>
#include <mutex>
#include <queue>
#include <shared_mutex>
#include <signal.h>
#include <stdio.h>
#include <string_view>
#include <sys/stat.h>
#include <type_traits>
#include <unistd.h>
#include <unordered_map>
#include <utility>
#include <xxhash.h>
@@ -100,6 +93,8 @@ ContextImpl::ContextImpl(const FEXCore::HostFeatures& Features)
// Track atomic TSO emulation configuration.
UpdateAtomicTSOEmulationConfig();
// Ensure X87 precision constraints are respected.
UpdateX87PrecisionConfig();
}
struct GetFrameBlockInfoResult {
@@ -952,10 +947,10 @@ void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t Gu
if (GPRSize == IR::OpSize::i64Bit) {
IR::Ref R = emit->_StoreRegister(emit->Constant(Entrypoint), GPRSize);
R->Reg = IR::PhysicalRegister(IR::GPRFixedClass, X86State::REG_R11).Raw;
R->Reg = IR::PhysicalRegister(IR::RegClass::GPRFixed, X86State::REG_R11).Raw;
} else {
emit->_StoreContext(GPRSize, IR::FPRClass, emit->_VCastFromGPR(IR::OpSize::i64Bit, IR::OpSize::i64Bit, emit->Constant(Entrypoint)),
offsetof(Core::CPUState, mm[0][0]));
emit->_StoreContextFPR(GPRSize, emit->_VCastFromGPR(IR::OpSize::i64Bit, IR::OpSize::i64Bit, emit->Constant(Entrypoint)),
offsetof(Core::CPUState, mm[0][0]));
}
emit->_ExitFunction(IR::OpSize::i64Bit, emit->Constant(GuestThunkEntrypoint), IR::BranchHint::None, emit->Invalid(), emit->Invalid());
},
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
#include "Common/VectorRegType.h"
#include "Common/SoftFloat.h"
#include "Interface/Context/Context.h"
#include "Interface/Core/CPUBackend.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
@@ -26,7 +26,9 @@
#endif
#include <array>
#include <atomic>
#include <bit>
#include <condition_variable>
#include <csignal>
#include <cstring>
@@ -91,7 +93,7 @@ void Dispatcher::EmitDispatcher() {
FillStaticRegs();
ldr(RipReg, STATE_PTR(CpuStateFrame, State.rip));
(void)cbnz(ARMEmitter::Size::i32Bit, ENTRY_FILL_SRA_SINGLE_INST_REG, &CompileSingleStep);
cbnz(ARMEmitter::Size::i32Bit, ENTRY_FILL_SRA_SINGLE_INST_REG, &CompileSingleStep);
ARMEmitter::BiDirectionalLabel LoopTop {};
@@ -140,7 +142,7 @@ void Dispatcher::EmitDispatcher() {
// We want to ensure that we are 16 byte aligned at the top of this loop
Align16B();
(void)Bind(&LoopTop);
Bind(&LoopTop);
AbsoluteLoopTopAddress = GetCursorAddress<uint64_t>();
// Load in our RIP
@@ -167,11 +169,11 @@ void Dispatcher::EmitDispatcher() {
ldr(TMP2, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionEC));
br(TMP2);
(void)!Bind(&l_NotECCode);
Bind(&l_NotECCode);
#endif
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
(void)cbnz(ARMEmitter::Size::i32Bit, TMP1, &CompileSingleStep);
cbnz(ARMEmitter::Size::i32Bit, TMP1, &CompileSingleStep);
// This is the block cache lookup routine
// It matches what is going on it LookupCache.h::FindBlock
@@ -196,7 +198,7 @@ void Dispatcher::EmitDispatcher() {
ldr(TMP1, TMP1, TMP2, ARMEmitter::ExtendedType::LSL_64, 3);
// If page pointer is zero then we have no block
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &NoBlock);
cbz(ARMEmitter::Size::i64Bit, TMP1, &NoBlock);
// Steal the page offset
and_(ARMEmitter::Size::i64Bit, TMP2, TMP4, 0x0FFF);
@@ -211,11 +213,10 @@ void Dispatcher::EmitDispatcher() {
// If the guest address doesn't match, Compile the block.
sub(TMP2, TMP2, RipReg);
(void)cbnz(ARMEmitter::Size::i64Bit, TMP2, &NoBlock);
cbnz(ARMEmitter::Size::i64Bit, TMP2, &NoBlock);
// Check the host address to see if it matches, else compile the block.
(void)cbz(ARMEmitter::Size::i64Bit, TMP4, &NoBlock);
cbz(ARMEmitter::Size::i64Bit, TMP4, &NoBlock);
// If we've made it here then we have a real compiled block
{
@@ -303,7 +304,7 @@ void Dispatcher::EmitDispatcher() {
// Need to create the block
{
(void)Bind(&NoBlock);
Bind(&NoBlock);
EmitSignalGuardedRegion([&]() {
SpillStaticRegs(TMP1);
@@ -337,7 +338,7 @@ void Dispatcher::EmitDispatcher() {
}
{
(void)Bind(&CompileSingleStep);
Bind(&CompileSingleStep);
EmitSignalGuardedRegion([&]() {
SpillStaticRegs(TMP1);
@@ -499,7 +500,7 @@ void Dispatcher::EmitDispatcher() {
stp<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::zr, ARMEmitter::XReg::zr, REG_CALLRET_SP, -0x10);
// Now go back to the regular dispatcher loop
(void)b(&LoopTop);
b(&LoopTop);
}
auto EmitLongALUOpHandler = [&](auto R, auto Offset) {
@@ -568,15 +569,14 @@ void Dispatcher::EmitDispatcher() {
}
}
(void)Bind(&l_CTX);
Bind(&l_CTX);
dc64(reinterpret_cast<uintptr_t>(CTX));
(void)Bind(&l_Sleep);
Bind(&l_Sleep);
dc64(reinterpret_cast<uint64_t>(SleepThread));
(void)Bind(&l_CompileBlock);
Bind(&l_CompileBlock);
FEXCore::Utils::MemberFunctionToPointerCast PMFCompileBlock(&FEXCore::Context::ContextImpl::CompileBlock);
dc64(PMFCompileBlock.GetConvertedPointer());
(void)Bind(&l_CompileSingleStep);
Bind(&l_CompileSingleStep);
FEXCore::Utils::MemberFunctionToPointerCast PMFCompileSingleStep(&FEXCore::Context::ContextImpl::CompileSingleStep);
dc64(PMFCompileSingleStep.GetConvertedPointer());
@@ -2,11 +2,13 @@
#pragma once
#include "Common/SoftFloat.h"
#include "Interface/Context/Context.h"
#include "Interface/Core/Interpreter/Fallbacks/FallbackOpHandler.h"
#include "Interface/IR/IR.h"
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/SHMStats.h>
#include <FEXCore/Config/Config.h>
namespace FEXCore::CPU {
FEXCORE_PRESERVE_ALL_ATTR static softfloat_state SoftFloatStateFromFCW(uint16_t FCW, bool Force80BitPrecision = false) {
@@ -77,6 +79,12 @@ struct OpHandlers<IR::OP_F80CVTTO> {
FEXCORE_PRESERVE_ALL_ATTR static VectorRegType handle8(uint16_t FCW, double src, FEXCore::Core::CpuStateFrame* Frame) {
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
ScopedSoftFloatState State {FCW, Frame};
auto Context = static_cast<Context::ContextImpl*>(Frame->Thread->CTX);
auto ReducedPrecisionMode = Context->Config.x87ReducedPrecision;
auto StrictReducedPrecisionMode = Context->Config.x87StrictReducedPrecision;
if (!ReducedPrecisionMode || StrictReducedPrecisionMode) {
return X80SoftFloat::FromF64_PreserveNaN(&State.State, src);
}
return X80SoftFloat(&State.State, src);
}
};
@@ -115,6 +123,12 @@ struct OpHandlers<IR::OP_F80CVT> {
FEXCORE_PRESERVE_ALL_ATTR static double handle8(uint16_t FCW, VectorRegType src, FEXCore::Core::CpuStateFrame* Frame) {
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
ScopedSoftFloatState State {FCW, Frame};
auto Context = static_cast<Context::ContextImpl*>(Frame->Thread->CTX);
auto ReducedPrecisionMode = Context->Config.x87ReducedPrecision;
auto StrictReducedPrecisionMode = Context->Config.x87StrictReducedPrecision;
if (!ReducedPrecisionMode || StrictReducedPrecisionMode) {
return X80SoftFloat(src).ToF64_PreserveNan(&State.State);
}
return X80SoftFloat(src).ToF64(&State.State);
}
};
+46 -23
View File
@@ -372,7 +372,7 @@ DEF_OP(CondSubNZCV) {
DEF_OP(Neg) {
auto Op = IROp->C<IR::IROp_Neg>();
if (Op->Cond == FEXCore::IR::COND_AL) {
if (Op->Cond == IR::CondClass::AL) {
neg(ConvertSize48(IROp), GetReg(Node), GetReg(Op->Src));
} else {
cneg(ConvertSize48(IROp), GetReg(Node), GetReg(Op->Src), MapCC(Op->Cond));
@@ -588,7 +588,7 @@ DEF_OP(ShiftFlags) {
and_(ARMEmitter::Size::i32Bit, TMP1, Src2, OpSize == IR::OpSize::i64Bit ? 0x3f : 0x1f);
ARMEmitter::ForwardLabel Done;
(void)cbz(EmitSize, TMP1, &Done);
cbz(EmitSize, TMP1, &Done);
{
// PF/SF/ZF/OF
if (OpSize >= IR::OpSize::i32Bit) {
@@ -652,7 +652,7 @@ DEF_OP(ShiftFlags) {
msr(ARMEmitter::SystemRegister::NZCV, TMP2);
}
}
(void)Bind(&Done);
Bind(&Done);
// TODO: Make RA less dumb so this can't happen (e.g. with late-kill).
if (PFOutput != PFTemp) {
@@ -669,7 +669,7 @@ DEF_OP(RotateFlags) {
// If shift=0, flags are unaffected. Wrap the whole implementation in a cbz.
ARMEmitter::ForwardLabel Done;
(void)cbz(EmitSize, Shift, &Done);
cbz(EmitSize, Shift, &Done);
{
// Extract the last bit shifted in to CF
const auto BitSize = IR::OpSizeToSize(Op->Size) * 8;
@@ -701,7 +701,7 @@ DEF_OP(RotateFlags) {
msr(ARMEmitter::SystemRegister::NZCV, TMP3);
}
}
(void)Bind(&Done);
Bind(&Done);
}
DEF_OP(Extr) {
@@ -767,14 +767,14 @@ DEF_OP(PDep) {
// Now, they're copied, so we can start setting Dest (even if it overlaps with
// one of them). Handle early exit case
mov(EmitSize, Dest, 0);
(void)cbz(EmitSize, OrigMask, &Done);
cbz(EmitSize, OrigMask, &Done);
// Setup for first iteration
neg(EmitSize, T0, Mask);
and_(EmitSize, T0, T0, Mask);
// Main loop
(void)Bind(&NextBit);
Bind(&NextBit);
sbfx(EmitSize, T1, Input, 0, 1);
eor(EmitSize, Mask, Mask, T0);
and_(EmitSize, T0, T1, T0);
@@ -782,10 +782,10 @@ DEF_OP(PDep) {
orr(EmitSize, Dest, Dest, T0);
lsr(EmitSize, Input, Input, 1);
and_(EmitSize, T0, Mask, T1);
(void)cbnz(EmitSize, T0, &NextBit);
cbnz(EmitSize, T0, &NextBit);
// All done with nothing to do.
(void)Bind(&Done);
Bind(&Done);
}
}
@@ -821,27 +821,27 @@ DEF_OP(PExt) {
ARMEmitter::BackwardLabel NextBit;
ARMEmitter::ForwardLabel Done;
(void)cbz(EmitSize, Mask, &EarlyExit);
cbz(EmitSize, Mask, &EarlyExit);
mov(EmitSize, MaskReg, Mask);
mov(EmitSize, ValueReg, Input);
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
// Main loop
(void)Bind(&NextBit);
(void)cbz(EmitSize, MaskReg, &Done);
Bind(&NextBit);
cbz(EmitSize, MaskReg, &Done);
clz(EmitSize, BitReg, MaskReg);
lslv(EmitSize, ValueReg, ValueReg, BitReg);
lslv(EmitSize, MaskReg, MaskReg, BitReg);
extr(EmitSize, Dest, Dest, ValueReg, OpSizeBitsM1);
bfc(EmitSize, MaskReg, OpSizeBitsM1, 1);
(void)b(&NextBit);
b(&NextBit);
// Early exit
(void)Bind(&EarlyExit);
Bind(&EarlyExit);
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
// All done with nothing to do.
(void)Bind(&Done);
Bind(&Done);
}
}
@@ -909,7 +909,7 @@ DEF_OP(Div) {
eor(EmitSize, TMP1, TMP1, Upper);
// If the sign bit matches then the result is zero
(void)cbz(EmitSize, TMP1, &Only64Bit);
cbz(EmitSize, TMP1, &Only64Bit);
// Long divide
{
@@ -928,17 +928,17 @@ DEF_OP(Div) {
mov(EmitSize, Remainder, TMP2);
// Skip 64-bit path
(void)b(&LongDIVRet);
b(&LongDIVRet);
}
(void)Bind(&Only64Bit);
Bind(&Only64Bit);
// 64-Bit only
{
sdiv(EmitSize, Quotient, Lower, Divisor);
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
}
(void)Bind(&LongDIVRet);
Bind(&LongDIVRet);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown DIV Size: {}", OpSize); break;
@@ -992,7 +992,7 @@ DEF_OP(UDiv) {
// Check the upper bits for zero
// If the upper bits are zero then we can do a 64-bit divide
(void)cbz(EmitSize, Upper, &Only64Bit);
cbz(EmitSize, Upper, &Only64Bit);
// Long divide
{
@@ -1011,17 +1011,17 @@ DEF_OP(UDiv) {
mov(EmitSize, Remainder, TMP2);
// Skip 64-bit path
(void)b(&LongDIVRet);
b(&LongDIVRet);
}
(void)Bind(&Only64Bit);
Bind(&Only64Bit);
// 64-Bit only
{
udiv(EmitSize, Quotient, Lower, Divisor);
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
}
(void)Bind(&LongDIVRet);
Bind(&LongDIVRet);
break;
}
default: LOGMAN_MSG_A_FMT("Unknown LUDIV Size: {}", OpSize); break;
@@ -1190,6 +1190,19 @@ DEF_OP(Rev) {
}
}
DEF_OP(Rbit) {
auto Op = IROp->C<IR::IROp_Rbit>();
const auto OpSize = IROp->Size;
LOGMAN_THROW_A_FMT(OpSize == IR::OpSize::i32Bit || OpSize == IR::OpSize::i64Bit, "Unsupported {} size: {}", __func__, OpSize);
const auto EmitSize = ConvertSize48(IROp);
const auto Dst = GetReg(Node);
const auto Src = GetReg(Op->Src);
rbit(EmitSize, Dst, Src);
}
DEF_OP(Bfi) {
auto Op = IROp->C<IR::IROp_Bfi>();
const auto EmitSize = ConvertSize(IROp);
@@ -1273,6 +1286,16 @@ DEF_OP(Sbfe) {
sbfx(ConvertSize(IROp), Dst, Src, Op->lsb, Op->Width);
}
DEF_OP(MaskGenerateFromBitWidth) {
auto Op = IROp->C<IR::IROp_MaskGenerateFromBitWidth>();
auto BitWidth = GetReg(Op->BitWidth);
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, -1);
cmp(ARMEmitter::Size::i64Bit, BitWidth, 0);
lslv(ARMEmitter::Size::i64Bit, TMP2, TMP1, BitWidth);
csinv(ARMEmitter::Size::i64Bit, GetReg(Node), TMP1, TMP2, ARMEmitter::Condition::CC_EQ);
}
DEF_OP(Select) {
auto Op = IROp->C<IR::IROp_Select>();
const auto OpSize = IROp->Size;
@@ -63,7 +63,7 @@ void Arm64JITCore::PlaceNamedSymbolLiteral(NamedSymbolLiteralPair& Lit) {
auto CurrentCursor = GetCursorAddress<uint8_t*>();
Lit.MoveABI.NamedSymbolLiteral.Offset = CurrentCursor - CodeData.BlockBegin;
BindOrRestart(&Lit.Loc);
Bind(&Lit.Loc);
dc64(Lit.Lit);
Relocations.emplace_back(Lit.MoveABI);
}
+34 -34
View File
@@ -62,27 +62,27 @@ DEF_OP(CASPair) {
ARMEmitter::BackwardLabel LoopTop;
ARMEmitter::ForwardLabel LoopNotExpected;
ARMEmitter::ForwardLabel LoopExpected;
(void)Bind(&LoopTop);
Bind(&LoopTop);
// This instruction sequence must be synced with HandleCASPAL_Armv8.
ldaxp(EmitSize, TMP2, TMP3, MemSrc);
cmp(EmitSize, TMP2, Expected0);
ccmp(EmitSize, TMP3, Expected1, ARMEmitter::StatusFlags::None, ARMEmitter::Condition::CC_EQ);
(void)b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
stlxp(EmitSize, TMP2, Desired0, Desired1, MemSrc);
(void)cbnz(EmitSize, TMP2, &LoopTop);
cbnz(EmitSize, TMP2, &LoopTop);
mov(EmitSize, Dst0, Expected0);
mov(EmitSize, Dst1, Expected1);
(void)b(&LoopExpected);
b(&LoopExpected);
(void)Bind(&LoopNotExpected);
Bind(&LoopNotExpected);
mov(EmitSize, Dst0, TMP2.R());
mov(EmitSize, Dst1, TMP3.R());
// exclusive monitor needs to be cleared here
// Might have hit the case where ldaxr was hit but stlxr wasn't
clrex();
(void)Bind(&LoopExpected);
Bind(&LoopExpected);
// Restore
msr(ARMEmitter::SystemRegister::NZCV, TMP1);
@@ -114,7 +114,7 @@ DEF_OP(CAS) {
ARMEmitter::BackwardLabel LoopTop;
ARMEmitter::ForwardLabel LoopNotExpected;
ARMEmitter::ForwardLabel LoopExpected;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
if (IROp->Size == IR::OpSize::i8Bit) {
cmp(EmitSize, TMP2, Expected, ARMEmitter::ExtendedType::UXTB, 0);
@@ -123,18 +123,18 @@ DEF_OP(CAS) {
} else {
cmp(EmitSize, TMP2, Expected);
}
(void)b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
stlxr(SubEmitSize, TMP3, Desired, MemSrc);
(void)cbnz(EmitSize, TMP3, &LoopTop);
cbnz(EmitSize, TMP3, &LoopTop);
mov(EmitSize, Dst, Expected);
(void)b(&LoopExpected);
b(&LoopExpected);
(void)Bind(&LoopNotExpected);
Bind(&LoopNotExpected);
mov(EmitSize, Dst, TMP2.R());
// exclusive monitor needs to be cleared here
// Might have hit the case where ldaxr was hit but stlxr wasn't
clrex();
(void)Bind(&LoopExpected);
Bind(&LoopExpected);
}
}
@@ -150,11 +150,11 @@ DEF_OP(AtomicXor) {
steorl(SubEmitSize, Src, MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
eor(EmitSize, TMP2, TMP2, Src);
stlxr(SubEmitSize, TMP2, TMP2, MemSrc);
(void)cbnz(EmitSize, TMP2, &LoopTop);
cbnz(EmitSize, TMP2, &LoopTop);
}
}
@@ -179,10 +179,10 @@ DEF_OP(AtomicSwap) {
ldswpal(SubEmitSize, Src, GetReg(Node), MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
stlxr(SubEmitSize, TMP4, Src, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
ubfm(EmitSize, GetReg(Node), TMP2, 0, IR::OpSizeAsBits(OpSize) - 1);
}
}
@@ -199,11 +199,11 @@ DEF_OP(AtomicFetchAdd) {
ldaddal(SubEmitSize, Src, GetReg(Node), MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
add(EmitSize, TMP3, TMP2, Src);
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
}
}
@@ -221,11 +221,11 @@ DEF_OP(AtomicFetchSub) {
ldaddal(SubEmitSize, TMP2, GetReg(Node), MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
sub(EmitSize, TMP3, TMP2, Src);
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
}
}
@@ -243,11 +243,11 @@ DEF_OP(AtomicFetchAnd) {
ldclral(SubEmitSize, TMP2, GetReg(Node), MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
and_(EmitSize, TMP3, TMP2, Src);
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
}
}
@@ -264,11 +264,11 @@ DEF_OP(AtomicFetchCLR) {
ldclral(SubEmitSize, Src, GetReg(Node), MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
bic(EmitSize, TMP3, TMP2, Src);
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
}
}
@@ -285,11 +285,11 @@ DEF_OP(AtomicFetchOr) {
ldsetal(SubEmitSize, Src, GetReg(Node), MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
orr(EmitSize, TMP3, TMP2, Src);
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
}
}
@@ -306,11 +306,11 @@ DEF_OP(AtomicFetchXor) {
ldeoral(SubEmitSize, Src, GetReg(Node), MemSrc);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
eor(EmitSize, TMP3, TMP2, Src);
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
}
}
@@ -326,20 +326,20 @@ DEF_OP(AtomicFetchNeg) {
// Use a CAS loop to avoid needing to emulate unaligned LLSC atomics
ldr(SubEmitSize, TMP2, MemSrc);
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
mov(EmitSize, TMP4, TMP2);
neg(EmitSize, TMP3, TMP2);
casal(SubEmitSize, TMP2, TMP3, MemSrc);
sub(EmitSize, TMP3, TMP2, TMP4);
(void)cbnz(EmitSize, TMP3, &LoopTop);
cbnz(EmitSize, TMP3, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(SubEmitSize, TMP2, MemSrc);
neg(EmitSize, TMP3, TMP2);
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
(void)cbnz(EmitSize, TMP4, &LoopTop);
cbnz(EmitSize, TMP4, &LoopTop);
mov(EmitSize, GetReg(Node), TMP2.R());
}
}
@@ -359,11 +359,11 @@ DEF_OP(TelemetrySetValue) {
stsetl(ARMEmitter::SubRegSize::i64Bit, TMP1, TMP2);
} else {
ARMEmitter::BackwardLabel LoopTop;
(void)Bind(&LoopTop);
Bind(&LoopTop);
ldaxr(ARMEmitter::SubRegSize::i64Bit, TMP3, TMP2);
orr(ARMEmitter::Size::i32Bit, TMP3, TMP3, Src);
stlxr(ARMEmitter::SubRegSize::i64Bit, TMP3, TMP3, TMP2);
(void)cbnz(ARMEmitter::Size::i32Bit, TMP3, &LoopTop);
cbnz(ARMEmitter::Size::i32Bit, TMP3, &LoopTop);
}
#endif
}
+22 -22
View File
@@ -141,7 +141,7 @@ DEF_OP(ExitFunction) {
if (!Op->CallReturnBlock.IsInvalid()) {
auto CallReturnAddressReg = GetReg(Op->CallReturnAddress).X();
PendingCallReturnTargetLabel = &CallReturnTargets.try_emplace(Op->CallReturnBlock.ID()).first->second;
(void)adr(TMP1, &l_CallReturn);
adr(TMP1, &l_CallReturn);
stp<ARMEmitter::IndexType::PRE>(CallReturnAddressReg, TMP1, REG_CALLRET_SP, -0x10);
} else {
stp<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::zr, ARMEmitter::XReg::zr, REG_CALLRET_SP, -0x10);
@@ -149,16 +149,16 @@ DEF_OP(ExitFunction) {
} else if (Op->Hint == IR::BranchHint::CheckTF) {
ARMEmitter::ForwardLabel TFUnset;
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
(void)cbz(ARMEmitter::Size::i32Bit, TMP1, &TFUnset);
cbz(ARMEmitter::Size::i32Bit, TMP1, &TFUnset);
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, NewRIP);
str(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip));
ldr(TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.DispatcherLoopTop));
blr(TMP2);
(void)Bind(&TFUnset);
Bind(&TFUnset);
}
EmitLinkedBranch(NewRIP, Op->Hint == IR::BranchHint::Call);
(void)Bind(&l_CallReturn);
Bind(&l_CallReturn);
#ifdef _M_ARM_64EC
}
#endif
@@ -170,7 +170,7 @@ DEF_OP(ExitFunction) {
// First try to pop from the call-ret stack, otherwise follow the normal path (but ending in a ret)
ldp<ARMEmitter::IndexType::POST>(TMP1, TMP2, REG_CALLRET_SP, 0x10);
sub(TMP1, TMP1, RipReg.X());
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
}
// L1 Cache
@@ -187,23 +187,23 @@ DEF_OP(ExitFunction) {
// Note: sub+cbnz used over cmp+br to preserve flags.
sub(TMP1, TMP1, RipReg.X());
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
ldr(TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.DispatcherLoopTop));
str(RipReg.X(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip));
(void)Bind(&SkipFullLookup);
Bind(&SkipFullLookup);
if (Op->Hint == IR::BranchHint::Call) {
ARMEmitter::ForwardLabel l_CallReturn;
if (!Op->CallReturnBlock.IsInvalid()) {
auto CallReturnAddressReg = GetReg(Op->CallReturnAddress).X();
PendingCallReturnTargetLabel = &CallReturnTargets.try_emplace(Op->CallReturnBlock.ID()).first->second;
(void)adr(TMP1, &l_CallReturn);
adr(TMP1, &l_CallReturn);
stp<ARMEmitter::IndexType::PRE>(CallReturnAddressReg, TMP1, REG_CALLRET_SP, -0x10);
} else {
stp<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::zr, ARMEmitter::XReg::zr, REG_CALLRET_SP, -0x10);
}
blr(TMP2);
(void)Bind(&l_CallReturn);
Bind(&l_CallReturn);
} else if (Op->Hint == IR::BranchHint::Return) {
ret(TMP2);
} else {
@@ -224,7 +224,7 @@ DEF_OP(CondJump) {
auto TrueTargetLabel = JumpTarget(Op->TrueBlock);
if (Op->FromNZCV) {
b_OrRestart(MapCC(Op->Cond), TrueTargetLabel);
b(MapCC(Op->Cond), TrueTargetLabel);
} else {
uint64_t Const;
const bool isConst = IsInlineConstant(Op->Cmp2, &Const);
@@ -235,18 +235,18 @@ DEF_OP(CondJump) {
LOGMAN_THROW_A_FMT(IsGPR(Op->Cmp1), "CondJump: Expected GPR");
LOGMAN_THROW_A_FMT(isConst, "CondJump: Expected constant source");
if (Op->Cond.Val == FEXCore::IR::COND_EQ) {
if (Op->Cond == IR::CondClass::EQ) {
LOGMAN_THROW_A_FMT(Const == 0, "CondJump: Expected 0 source");
cbz_OrRestart(Size, Reg, TrueTargetLabel);
} else if (Op->Cond.Val == FEXCore::IR::COND_NEQ) {
cbz(Size, Reg, TrueTargetLabel);
} else if (Op->Cond == IR::CondClass::NEQ) {
LOGMAN_THROW_A_FMT(Const == 0, "CondJump: Expected 0 source");
cbnz_OrRestart(Size, Reg, TrueTargetLabel);
} else if (Op->Cond.Val == FEXCore::IR::COND_TSTZ) {
cbnz(Size, Reg, TrueTargetLabel);
} else if (Op->Cond == IR::CondClass::TSTZ) {
LOGMAN_THROW_A_FMT(Const < 64, "CondJump: Expected valid bit source");
tbz_OrRestart(Reg, Const, TrueTargetLabel);
} else if (Op->Cond.Val == FEXCore::IR::COND_TSTNZ) {
tbz(Reg, Const, TrueTargetLabel);
} else if (Op->Cond == IR::CondClass::TSTNZ) {
LOGMAN_THROW_A_FMT(Const < 64, "CondJump: Expected valid bit source");
tbnz_OrRestart(Reg, Const, TrueTargetLabel);
tbnz(Reg, Const, TrueTargetLabel);
} else {
LOGMAN_THROW_A_FMT(false, "CondJump expected simple condition");
}
@@ -458,7 +458,7 @@ DEF_OP(ValidateCode) {
while (len >= Size) {
LoadData();
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, TMP2);
cbnz_OrRestart(ARMEmitter::Size::i64Bit, TMP1, &Fail);
cbnz(ARMEmitter::Size::i64Bit, TMP1, &Fail);
len -= Size;
Offset += Size;
}
@@ -486,10 +486,10 @@ DEF_OP(ValidateCode) {
ARMEmitter::ForwardLabel End;
LoadConstant(ARMEmitter::Size::i32Bit, Dst, 0);
b_OrRestart(&End);
BindOrRestart(&Fail);
b(&End);
Bind(&Fail);
LoadConstant(ARMEmitter::Size::i32Bit, Dst, 1);
BindOrRestart(&End);
Bind(&End);
}
DEF_OP(ThreadRemoveCodeEntry) {
@@ -423,11 +423,11 @@ DEF_OP(Vector_FToI) {
const auto Mask = PRED_TMP_32B.Merging();
switch (Op->Round) {
case FEXCore::IR::Round_Nearest.Val: frintn(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case FEXCore::IR::Round_Negative_Infinity.Val: frintm(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case FEXCore::IR::Round_Positive_Infinity.Val: frintp(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case FEXCore::IR::Round_Towards_Zero.Val: frintz(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case FEXCore::IR::Round_Host.Val: frinti(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::Nearest: frintn(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::NegInfinity: frintm(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::PosInfinity: frintp(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::TowardsZero: frintz(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::Host: frinti(SubEmitSize, Dst.Z(), Mask, Vector.Z()); break;
}
} else {
const auto IsScalar = ElementSize == OpSize;
@@ -449,21 +449,21 @@ DEF_OP(Vector_FToI) {
}
switch (Op->Round) {
case IR::Round_Nearest.Val: ROUNDING_FN(frintn); break;
case IR::Round_Negative_Infinity.Val: ROUNDING_FN(frintm); break;
case IR::Round_Positive_Infinity.Val: ROUNDING_FN(frintp); break;
case IR::Round_Towards_Zero.Val: ROUNDING_FN(frintz); break;
case IR::Round_Host.Val: ROUNDING_FN(frinti); break;
case IR::RoundMode::Nearest: ROUNDING_FN(frintn); break;
case IR::RoundMode::NegInfinity: ROUNDING_FN(frintm); break;
case IR::RoundMode::PosInfinity: ROUNDING_FN(frintp); break;
case IR::RoundMode::TowardsZero: ROUNDING_FN(frintz); break;
case IR::RoundMode::Host: ROUNDING_FN(frinti); break;
}
#undef ROUNDING_FN
} else {
switch (Op->Round) {
case FEXCore::IR::Round_Nearest.Val: frintn(SubEmitSize, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Negative_Infinity.Val: frintm(SubEmitSize, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Positive_Infinity.Val: frintp(SubEmitSize, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Towards_Zero.Val: frintz(SubEmitSize, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Host.Val: frinti(SubEmitSize, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::Nearest: frintn(SubEmitSize, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::NegInfinity: frintm(SubEmitSize, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::PosInfinity: frintp(SubEmitSize, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::TowardsZero: frintz(SubEmitSize, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::Host: frinti(SubEmitSize, Dst.Q(), Vector.Q()); break;
}
}
}
@@ -539,11 +539,11 @@ DEF_OP(Vector_F64ToI32) {
// Then convert to integers using fcvtzs.
auto CVTReg = Dst.Z();
switch (Round) {
case IR::Round_Nearest.Val: frintn(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
case IR::Round_Negative_Infinity.Val: frintm(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
case IR::Round_Positive_Infinity.Val: frintp(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
case IR::Round_Towards_Zero.Val: CVTReg = Vector.Z(); break;
case IR::Round_Host.Val: frinti(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::Nearest: frintn(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::NegInfinity: frintm(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::PosInfinity: frintp(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
case IR::RoundMode::TowardsZero: CVTReg = Vector.Z(); break;
case IR::RoundMode::Host: frinti(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Mask, Vector.Z()); break;
}
fcvtzs(Dst.Z(), ARMEmitter::SubRegSize::i32Bit, Mask, CVTReg, ARMEmitter::SubRegSize::i64Bit);
@@ -567,11 +567,11 @@ DEF_OP(Vector_F64ToI32) {
///< Round float to integral depending on rounding mode.
switch (Round) {
case FEXCore::IR::Round_Nearest.Val: frintn(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Negative_Infinity.Val: frintm(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Positive_Infinity.Val: frintp(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Towards_Zero.Val: frintz(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case FEXCore::IR::Round_Host.Val: frinti(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::Nearest: frintn(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::NegInfinity: frintm(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::PosInfinity: frintp(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::TowardsZero: frintz(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
case IR::RoundMode::Host: frinti(ARMEmitter::SubRegSize::i64Bit, Dst.Q(), Vector.Q()); break;
}
// Now narrow from f64 to f32.
+39 -37
View File
@@ -11,6 +11,8 @@ desc: Main glue logic of the arm64 splatter backend
$end_info$
*/
#include "Common/SoftFloat.h"
#include "Interface/Context/Context.h"
#include "Interface/Core/LookupCache.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
@@ -28,7 +30,6 @@ $end_info$
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/LongJump.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/Utils/Telemetry.h>
#include <FEXCore/Utils/TypeDefines.h>
@@ -36,6 +37,7 @@ $end_info$
#include <cstdio>
#include <cstring>
#include <limits>
#include <unistd.h>
namespace {
@@ -623,10 +625,10 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::In
RAPass = Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA");
RAPass->AddRegisters(FEXCore::IR::GPRClass, GeneralRegisters.size());
RAPass->AddRegisters(FEXCore::IR::GPRFixedClass, StaticRegisters.size());
RAPass->AddRegisters(FEXCore::IR::FPRClass, GeneralFPRegisters.size());
RAPass->AddRegisters(FEXCore::IR::FPRFixedClass, StaticFPRegisters.size());
RAPass->AddRegisters(IR::RegClass::GPR, GeneralRegisters.size());
RAPass->AddRegisters(IR::RegClass::GPRFixed, StaticRegisters.size());
RAPass->AddRegisters(IR::RegClass::FPR, GeneralFPRegisters.size());
RAPass->AddRegisters(IR::RegClass::FPRFixed, StaticFPRegisters.size());
RAPass->PairRegs = PairRegisters;
{
@@ -667,6 +669,15 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::In
CurrentCodeBuffer = CodeBuffers.GetLatest();
ThreadState->LookupCache->Shared = CurrentCodeBuffer->LookupCache.get();
// Setup dynamic dispatch.
if (ParanoidTSO()) {
RT_LoadMemTSO = &Arm64JITCore::Op_ParanoidLoadMemTSO;
RT_StoreMemTSO = &Arm64JITCore::Op_ParanoidStoreMemTSO;
} else {
RT_LoadMemTSO = &Arm64JITCore::Op_LoadMemTSO;
RT_StoreMemTSO = &Arm64JITCore::Op_StoreMemTSO;
}
}
void Arm64JITCore::EmitDetectionString() {
@@ -737,11 +748,11 @@ void Arm64JITCore::EmitTFCheck() {
// Note that this needs to be before the below suspend checks, as X86 checks this flag immediately after executing an instruction.
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
(void)cbz(ARMEmitter::Size::i32Bit, TMP1, &l_TFUnset);
cbz(ARMEmitter::Size::i32Bit, TMP1, &l_TFUnset);
// X86 semantically checks TF after executing each instruction, so e.g. setting a context with TF set will execute a single instruction
// and then raise an exception. However on the FEX side this is simpler to implement by checking at the start of each instruction, handle this by having bit 1 being unset in the flag state indicate that TF is blocked for a single instruction.
(void)tbz(TMP1, 1, &l_TFBlocked);
tbz(TMP1, 1, &l_TFBlocked);
// Block TF for a single instruction when the frontend jumps to a new context by unsetting bit 1.
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
@@ -764,11 +775,11 @@ void Arm64JITCore::EmitTFCheck() {
ldr(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP));
br(TMP1);
(void)Bind(&l_TFBlocked);
Bind(&l_TFBlocked);
// If TF was blocked for this instruction, unblock it for the next.
LoadConstant(ARMEmitter::Size::i32Bit, TMP1, 0b11);
strb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
(void)Bind(&l_TFUnset);
Bind(&l_TFUnset);
}
void Arm64JITCore::EmitSuspendInterruptCheck() {
@@ -786,14 +797,14 @@ void Arm64JITCore::EmitSuspendInterruptCheck() {
ARMEmitter::ForwardLabel l_NoSuspend;
cbz(ARMEmitter::Size::i32Bit, TMP2, &l_NoSuspend);
brk(SuspendMagic);
(void)Bind(&l_NoSuspend);
Bind(&l_NoSuspend);
#endif
}
void Arm64JITCore::EmitEntryPoint(ARMEmitter::BackwardLabel& HeaderLabel, bool CheckTF) {
// Get the address of the JITCodeHeader and store in to the core state.
// Two instruction cost, each 1 cycle.
adr_OrRestart(TMP1, &HeaderLabel);
adr(TMP1, &HeaderLabel);
str(TMP1, STATE, offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader));
if (CheckTF) {
@@ -815,25 +826,16 @@ void Arm64JITCore::EmitEntryPoint(ARMEmitter::BackwardLabel& HeaderLabel, bool C
CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size, bool SingleInst, const FEXCore::IR::IRListView* IR,
FEXCore::Core::DebugData* DebugData, bool CheckTF) {
FEXCORE_PROFILE_SCOPED("Arm64::CompileCode");
this->Entry = Entry;
this->DebugData = DebugData;
this->IR = IR;
RequiresFarARM64Jumps = false;
switch (static_cast<RestartOptions::Control>(FEXCore::LongJump::SetJump(RestartControl.RestartJump))) {
case RestartOptions::Control::Incoming:
// Nothing
break;
case RestartOptions::Control::EnableFarARM64Jumps: RequiresFarARM64Jumps = true; break;
default: ERROR_AND_DIE_FMT("Unhandled Arm64 restart condition!");
}
uint32_t SSACount = IR->GetSSACount();
JumpTargets.clear();
CallReturnTargets.clear();
PendingJumpThunks.clear();
uint32_t SSACount = IR->GetSSACount();
JumpTargets.resize(IR->GetHeader()->BlockCount, {});
this->Entry = Entry;
this->DebugData = DebugData;
this->IR = IR;
CodeData.EntryPoints.clear();
// Fairly excessive buffer range to make sure we don't overflow
@@ -848,7 +850,7 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
// Put the code header at the start of the data block.
ARMEmitter::BackwardLabel JITCodeHeaderLabel {};
(void)Bind(&JITCodeHeaderLabel);
Bind(&JITCodeHeaderLabel);
JITCodeHeader* CodeHeader = GetCursorAddress<JITCodeHeader*>();
CursorIncrement(sizeof(JITCodeHeader));
@@ -896,7 +898,7 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
if (PendingTargetLabel->Backward.Location) {
EmitSuspendInterruptCheck();
}
b_OrRestart(PendingTargetLabel);
b(PendingTargetLabel);
PendingTargetLabel = nullptr;
}
@@ -906,14 +908,14 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
const auto IsReturnTarget = CallReturnTargets.try_emplace(Node).first;
if (PendingTargetLabel) {
// If there is a fallthrough branch to this block, skip over the entrypoint code.
b_OrRestart(Target);
b(Target);
} else if (PendingCallReturnTargetLabel && PendingCallReturnTargetLabel != &IsReturnTarget->second) {
// If we just emitted a call, but the block we're now emitting is not the return block so don't fallthrough.
b_OrRestart(PendingCallReturnTargetLabel);
b(PendingCallReturnTargetLabel);
}
PendingCallReturnTargetLabel = nullptr;
BindOrRestart(&IsReturnTarget->second);
Bind(&IsReturnTarget->second);
CodeData.EntryPoints.emplace(BlockStartRIP, GetCursorAddress<uint8_t*>());
DebugData->GuestOpcodes.push_back({BlockIROp->GuestEntryOffset, GetCursorAddress<uint8_t*>() - CodeData.BlockBegin});
@@ -922,12 +924,12 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
if (PendingCallReturnTargetLabel) {
// If there is still a pending call return target, then the block we're emitting is not the return block so don't fallthrough.
b_OrRestart(PendingCallReturnTargetLabel);
b(PendingCallReturnTargetLabel);
PendingCallReturnTargetLabel = nullptr;
}
PendingTargetLabel = nullptr;
BindOrRestart(Target);
Bind(Target);
}
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
@@ -954,7 +956,7 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
if (PendingTargetLabel->Backward.Location) {
EmitSuspendInterruptCheck();
}
b_OrRestart(PendingTargetLabel);
b(PendingTargetLabel);
}
PendingTargetLabel = nullptr;
@@ -965,21 +967,21 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
ARMEmitter::ForwardLabel l_DoLink;
uint64_t ThunkAddress = GetCursorAddress<uint64_t>();
BindOrRestart(&PendingJumpThunk.Label);
b_OrRestart(&l_DoLink);
Bind(&PendingJumpThunk.Label);
b(&l_DoLink);
br(TMP1);
BindOrRestart(&l_DoLink);
Bind(&l_DoLink);
ldr(TMP1, &l_ExitLink);
blr(TMP1);
// This is a ExitFunctionLinkData struct
BindOrRestart(&l_ExitLink);
Bind(&l_ExitLink);
dc64(0); // HostCode
dc64(PendingJumpThunk.GuestRIP); // GuestRIP
dc64(PendingJumpThunk.CallerAddress - ThunkAddress); // CallerOffset
}
BindOrRestart(&l_ExitLink);
Bind(&l_ExitLink);
dc64(ThreadState->CurrentFrame->Pointers.Common.ExitFunctionLinker);
// CodeSize not including the header or tail data.
+82 -257
View File
@@ -10,32 +10,38 @@ $end_info$
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
#include "Interface/Core/CPUBackend.h"
#include "Interface/Core/Dispatcher/Dispatcher.h"
#include "Interface/Core/JIT/Relocations.h"
#include "Interface/IR/IR.h"
#include "Interface/IR/IntrusiveIRList.h"
#include "Interface/IR/RegisterAllocationData.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/fextl/map.h>
#include <FEXCore/fextl/memory.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/vector.h>
#include <FEXCore/Utils/LongJump.h>
#include <CodeEmitter/Emitter.h>
#include <array>
#include <cstdint>
#include <functional>
#include <optional>
#include <utility>
#include <variant>
namespace FEXCore::Core {
struct InternalThreadState;
}
namespace FEXCore::Context {
struct ExitFunctionLinkData;
}
namespace FEXCore::IR {
class RegisterAllocationPass;
}
namespace FEXCore::CPU {
class Arm64JITCore final : public CPUBackend, public Arm64Emitter {
@@ -55,7 +61,6 @@ public:
private:
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
FEX_CONFIG_OPT(HalfBarrierTSOEnabled, HALFBARRIERTSOENABLED);
const bool HostSupportsSVE128 {};
const bool HostSupportsSVE256 {};
@@ -63,19 +68,6 @@ private:
const bool HostSupportsRPRES {};
const bool HostSupportsAFP {};
struct RestartOptions {
FEXCore::LongJump::JumpBuf RestartJump;
enum class Control : uint64_t {
Incoming = 0,
EnableFarARM64Jumps = 1,
};
};
// FEXCore makes assumptions in the JIT about certain conditions being true.
// In the rare case when those assumptions are broken, FEX needs to safely restart the JIT.
RestartOptions RestartControl {};
bool RequiresFarARM64Jumps {};
ARMEmitter::BiDirectionalLabel* PendingTargetLabel {};
ARMEmitter::BiDirectionalLabel* PendingCallReturnTargetLabel {};
FEXCore::Context::ContextImpl* CTX {};
@@ -105,11 +97,13 @@ private:
[[nodiscard]]
ARMEmitter::Register GetReg(IR::PhysicalRegister Reg) const {
LOGMAN_THROW_A_FMT(Reg.Class == IR::GPRFixedClass.Val || Reg.Class == IR::GPRClass.Val, "Unexpected Class: {}", Reg.Class);
const auto RegClass = Reg.AsRegClass();
if (Reg.Class == IR::GPRFixedClass.Val) {
LOGMAN_THROW_A_FMT(RegClass == IR::RegClass::GPRFixed || RegClass == IR::RegClass::GPR, "Unexpected Class: {}", Reg.Class);
if (RegClass == IR::RegClass::GPRFixed) {
return StaticRegisters[Reg.Reg];
} else if (Reg.Class == IR::GPRClass.Val) {
} else if (RegClass == IR::RegClass::GPR) {
return GeneralRegisters[Reg.Reg];
}
@@ -128,11 +122,13 @@ private:
[[nodiscard]]
ARMEmitter::VRegister GetVReg(IR::PhysicalRegister Reg) const {
LOGMAN_THROW_A_FMT(Reg.Class == IR::FPRFixedClass.Val || Reg.Class == IR::FPRClass.Val, "Unexpected Class: {}", Reg.Class);
const auto RegClass = Reg.AsRegClass();
if (Reg.Class == IR::FPRFixedClass.Val) {
LOGMAN_THROW_A_FMT(RegClass == IR::RegClass::FPRFixed || RegClass == IR::RegClass::FPR, "Unexpected Class: {}", Reg.Class);
if (RegClass == IR::RegClass::FPRFixed) {
return StaticFPRegisters[Reg.Reg];
} else if (Reg.Class == IR::FPRClass.Val) {
} else if (RegClass == IR::RegClass::FPR) {
return GeneralFPRegisters[Reg.Reg];
}
@@ -150,8 +146,8 @@ private:
}
[[nodiscard]]
FEXCore::IR::RegisterClassType GetRegClass(IR::Ref Node) const {
return FEXCore::IR::RegisterClassType {IR::PhysicalRegister(Node).Class};
static IR::RegClass GetRegClass(IR::Ref Node) {
return IR::PhysicalRegister(Node).AsRegClass();
}
[[nodiscard]]
@@ -168,7 +164,7 @@ private:
// Converts IR-base shift type to ARMEmitter shift type.
// Will be a no-op, only a type conversion since the two definitions match.
[[nodiscard]]
ARMEmitter::ShiftType ConvertIRShiftType(IR::ShiftType Shift) const {
static ARMEmitter::ShiftType ConvertIRShiftType(IR::ShiftType Shift) {
return Shift == IR::ShiftType::LSL ? ARMEmitter::ShiftType::LSL :
Shift == IR::ShiftType::LSR ? ARMEmitter::ShiftType::LSR :
Shift == IR::ShiftType::ASR ? ARMEmitter::ShiftType::ASR :
@@ -176,18 +172,23 @@ private:
}
[[nodiscard]]
ARMEmitter::Size ConvertSize(const IR::IROp_Header* Op) {
static ARMEmitter::Size ConvertSize(const IR::IROp_Header* Op) {
return Op->Size == IR::OpSize::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
}
[[nodiscard]]
ARMEmitter::Size ConvertSize48(const IR::IROp_Header* Op) {
static ARMEmitter::Size ConvertSize48(const IR::IROp_Header* Op) {
LOGMAN_THROW_A_FMT(Op->Size == IR::OpSize::i32Bit || Op->Size == IR::OpSize::i64Bit, "Invalid size");
return ConvertSize(Op);
}
[[nodiscard]]
ARMEmitter::SubRegSize ConvertSubRegSize16(IR::OpSize ElementSize) {
static ARMEmitter::Size ConvertSize(IR::OpSize Size) {
return Size == IR::OpSize::i64Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
}
[[nodiscard]]
static ARMEmitter::SubRegSize ConvertSubRegSize16(IR::OpSize ElementSize) {
LOGMAN_THROW_A_FMT(ElementSize == IR::OpSize::i8Bit || ElementSize == IR::OpSize::i16Bit || ElementSize == IR::OpSize::i32Bit ||
ElementSize == IR::OpSize::i64Bit || ElementSize == IR::OpSize::i128Bit,
"Invalid size");
@@ -199,105 +200,105 @@ private:
}
[[nodiscard]]
ARMEmitter::SubRegSize ConvertSubRegSize16(const IR::IROp_Header* Op) {
static ARMEmitter::SubRegSize ConvertSubRegSize16(const IR::IROp_Header* Op) {
return ConvertSubRegSize16(Op->ElementSize);
}
[[nodiscard]]
ARMEmitter::SubRegSize ConvertSubRegSize8(IR::OpSize ElementSize) {
static ARMEmitter::SubRegSize ConvertSubRegSize8(IR::OpSize ElementSize) {
LOGMAN_THROW_A_FMT(ElementSize != IR::OpSize::i128Bit, "Invalid size");
return ConvertSubRegSize16(ElementSize);
}
[[nodiscard]]
ARMEmitter::SubRegSize ConvertSubRegSize8(const IR::IROp_Header* Op) {
static ARMEmitter::SubRegSize ConvertSubRegSize8(const IR::IROp_Header* Op) {
return ConvertSubRegSize8(Op->ElementSize);
}
[[nodiscard]]
ARMEmitter::SubRegSize ConvertSubRegSize4(const IR::IROp_Header* Op) {
static ARMEmitter::SubRegSize ConvertSubRegSize4(const IR::IROp_Header* Op) {
LOGMAN_THROW_A_FMT(Op->ElementSize != IR::OpSize::i64Bit, "Invalid size");
return ConvertSubRegSize8(Op);
}
[[nodiscard]]
ARMEmitter::SubRegSize ConvertSubRegSize248(const IR::IROp_Header* Op) {
static ARMEmitter::SubRegSize ConvertSubRegSize248(const IR::IROp_Header* Op) {
LOGMAN_THROW_A_FMT(Op->ElementSize != IR::OpSize::i8Bit, "Invalid size");
return ConvertSubRegSize8(Op);
}
[[nodiscard]]
ARMEmitter::VectorRegSizePair ConvertSubRegSizePair16(const IR::IROp_Header* Op) {
static ARMEmitter::VectorRegSizePair ConvertSubRegSizePair16(const IR::IROp_Header* Op) {
return ARMEmitter::ToVectorSizePair(ConvertSubRegSize16(Op));
}
[[nodiscard]]
ARMEmitter::VectorRegSizePair ConvertSubRegSizePair8(const IR::IROp_Header* Op) {
static ARMEmitter::VectorRegSizePair ConvertSubRegSizePair8(const IR::IROp_Header* Op) {
LOGMAN_THROW_A_FMT(Op->ElementSize != IR::OpSize::i128Bit, "Invalid size");
return ConvertSubRegSizePair16(Op);
}
[[nodiscard]]
ARMEmitter::VectorRegSizePair ConvertSubRegSizePair248(const IR::IROp_Header* Op) {
static ARMEmitter::VectorRegSizePair ConvertSubRegSizePair248(const IR::IROp_Header* Op) {
LOGMAN_THROW_A_FMT(Op->ElementSize != IR::OpSize::i8Bit, "Invalid size");
return ConvertSubRegSizePair8(Op);
}
[[nodiscard]]
ARMEmitter::Condition MapCC(IR::CondClassType Cond) {
switch (Cond.Val) {
case FEXCore::IR::COND_EQ: return ARMEmitter::Condition::CC_EQ;
case FEXCore::IR::COND_NEQ: return ARMEmitter::Condition::CC_NE;
case FEXCore::IR::COND_SGE: return ARMEmitter::Condition::CC_GE;
case FEXCore::IR::COND_SLT: return ARMEmitter::Condition::CC_LT;
case FEXCore::IR::COND_SGT: return ARMEmitter::Condition::CC_GT;
case FEXCore::IR::COND_SLE: return ARMEmitter::Condition::CC_LE;
case FEXCore::IR::COND_UGE: return ARMEmitter::Condition::CC_CS;
case FEXCore::IR::COND_ULT: return ARMEmitter::Condition::CC_CC;
case FEXCore::IR::COND_UGT: return ARMEmitter::Condition::CC_HI;
case FEXCore::IR::COND_ULE: return ARMEmitter::Condition::CC_LS;
case FEXCore::IR::COND_FLU: return ARMEmitter::Condition::CC_LT;
case FEXCore::IR::COND_FGE: return ARMEmitter::Condition::CC_GE;
case FEXCore::IR::COND_FLEU: return ARMEmitter::Condition::CC_LE;
case FEXCore::IR::COND_FGT: return ARMEmitter::Condition::CC_GT;
case FEXCore::IR::COND_FU:
case FEXCore::IR::COND_VS: return ARMEmitter::Condition::CC_VS;
case FEXCore::IR::COND_FNU:
case FEXCore::IR::COND_VC: return ARMEmitter::Condition::CC_VC;
case FEXCore::IR::COND_MI: return ARMEmitter::Condition::CC_MI;
case FEXCore::IR::COND_PL: return ARMEmitter::Condition::CC_PL;
static ARMEmitter::Condition MapCC(IR::CondClass Cond) {
switch (Cond) {
case IR::CondClass::EQ: return ARMEmitter::Condition::CC_EQ;
case IR::CondClass::NEQ: return ARMEmitter::Condition::CC_NE;
case IR::CondClass::SGE: return ARMEmitter::Condition::CC_GE;
case IR::CondClass::SLT: return ARMEmitter::Condition::CC_LT;
case IR::CondClass::SGT: return ARMEmitter::Condition::CC_GT;
case IR::CondClass::SLE: return ARMEmitter::Condition::CC_LE;
case IR::CondClass::UGE: return ARMEmitter::Condition::CC_CS;
case IR::CondClass::ULT: return ARMEmitter::Condition::CC_CC;
case IR::CondClass::UGT: return ARMEmitter::Condition::CC_HI;
case IR::CondClass::ULE: return ARMEmitter::Condition::CC_LS;
case IR::CondClass::FLU: return ARMEmitter::Condition::CC_LT;
case IR::CondClass::FGE: return ARMEmitter::Condition::CC_GE;
case IR::CondClass::FLEU: return ARMEmitter::Condition::CC_LE;
case IR::CondClass::FGT: return ARMEmitter::Condition::CC_GT;
case IR::CondClass::FU:
case IR::CondClass::VS: return ARMEmitter::Condition::CC_VS;
case IR::CondClass::FNU:
case IR::CondClass::VC: return ARMEmitter::Condition::CC_VC;
case IR::CondClass::MI: return ARMEmitter::Condition::CC_MI;
case IR::CondClass::PL: return ARMEmitter::Condition::CC_PL;
default: LOGMAN_MSG_A_FMT("Unsupported compare type"); return ARMEmitter::Condition::CC_NV;
}
}
[[nodiscard]]
bool IsFPR(IR::RegisterClassType Class) const {
return Class == IR::FPRClass || Class == IR::FPRFixedClass;
static bool IsFPR(IR::RegClass Class) {
return Class == IR::RegClass::FPR || Class == IR::RegClass::FPRFixed;
}
[[nodiscard]]
bool IsGPR(IR::RegisterClassType Class) const {
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
static bool IsGPR(IR::RegClass Class) {
return Class == IR::RegClass::GPR || Class == IR::RegClass::GPRFixed;
}
[[nodiscard]]
bool IsGPR(IR::Ref Node) {
static bool IsGPR(IR::Ref Node) {
return IsGPR(GetRegClass(Node));
}
[[nodiscard]]
bool IsFPR(IR::Ref Node) {
static bool IsFPR(IR::Ref Node) {
return IsFPR(GetRegClass(Node));
}
[[nodiscard]]
bool IsGPR(IR::OrderedNodeWrapper Wrap) {
return IsGPR(IR::RegisterClassType {IR::PhysicalRegister(Wrap).Class});
static bool IsGPR(IR::OrderedNodeWrapper Wrap) {
return IsGPR(IR::PhysicalRegister(Wrap).AsRegClass());
}
[[nodiscard]]
bool IsFPR(IR::OrderedNodeWrapper Wrap) {
return IsFPR(IR::RegisterClassType {IR::PhysicalRegister(Wrap).Class});
static bool IsFPR(IR::OrderedNodeWrapper Wrap) {
return IsFPR(IR::PhysicalRegister(Wrap).AsRegClass());
}
[[nodiscard]]
@@ -330,199 +331,14 @@ private:
void EmitLinkedBranch(uint64_t GuestRIP, bool Call) {
PendingJumpThunks.push_back({GetCursorAddress<uint64_t>(), GuestRIP, {}});
auto& Thunk = PendingJumpThunks.back();
BindOrRestart(&Thunk.Label);
Bind(&Thunk.Label);
if (Call) {
bl_OrRestart(&Thunk.Label);
bl(&Thunk.Label);
} else {
b_OrRestart(&Thunk.Label);
b(&Thunk.Label);
}
}
// Restart helpers
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void bl_OrRestart(T* Label) {
if (bl(Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
// We can support this but currently unnecessary.
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void b_OrRestart(T* Label) {
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
// We can support this but currently unnecessary.
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void b_OrRestart(ARMEmitter::Condition Cond, T* Label) {
if (RequiresFarARM64Jumps) {
ARMEmitter::ForwardLabel Skip {};
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
(void)b(InvertCondition(Cond), &Skip);
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
}
(void)Bind(&Skip);
return;
}
if (b(Cond, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void cbz_OrRestart(ARMEmitter::Size s, ARMEmitter::Register rt, T* Label) {
if (RequiresFarARM64Jumps) {
ARMEmitter::ForwardLabel Skip {};
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
(void)cbnz(s, rt, &Skip);
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
}
(void)Bind(&Skip);
return;
}
if (cbz(s, rt, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void cbnz_OrRestart(ARMEmitter::Size s, ARMEmitter::Register rt, T* Label) {
if (RequiresFarARM64Jumps) {
ARMEmitter::ForwardLabel Skip {};
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
(void)cbz(s, rt, &Skip);
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
}
(void)Bind(&Skip);
return;
}
if (cbnz(s, rt, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void tbz_OrRestart(ARMEmitter::Register rt, uint32_t Bit, T* Label) {
if (RequiresFarARM64Jumps) {
ARMEmitter::ForwardLabel Skip {};
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
(void)tbnz(rt, Bit, &Skip);
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
}
(void)Bind(&Skip);
return;
}
if (tbz(rt, Bit, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void tbnz_OrRestart(ARMEmitter::Register rt, uint32_t Bit, T* Label) {
if (RequiresFarARM64Jumps) {
ARMEmitter::ForwardLabel Skip {};
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
(void)tbz(rt, Bit, &Skip);
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
}
(void)Bind(&Skip);
return;
}
if (tbnz(rt, Bit, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void adr_OrRestart(ARMEmitter::Register rd, T* Label) {
if (adr(rd, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
// We can support this but currently unnecessary.
ERROR_AND_DIE_FMT("Long ADR currently unsupported!");
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void adrp_OrRestart(ARMEmitter::Register rd, T* Label) {
if (adrp(rd, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
return;
}
// We can support this but currently unnecessary.
ERROR_AND_DIE_FMT("Long ADRP currently unsupported!");
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
template<typename T>
requires (std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>)
void BindOrRestart(T* Label) {
if (Bind(Label)) {
return;
}
if (RequiresFarARM64Jumps) {
// This should have been caught before this point.
ERROR_AND_DIE_FMT("Oops. Unhandled long bind.");
return;
}
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
}
// This is purely a debugging aid for developers to see if they are in JIT code space when inspecting raw memory
void EmitDetectionString();
IR::RegisterAllocationPass* RAPass {};
@@ -606,7 +422,7 @@ private:
void Emulate128BitGather(IR::OpSize Size, IR::OpSize ElementSize, ARMEmitter::VRegister Dst, ARMEmitter::VRegister IncomingDst,
std::optional<ARMEmitter::Register> BaseAddr, ARMEmitter::VRegister VectorIndexLow,
std::optional<ARMEmitter::VRegister> VectorIndexHigh, ARMEmitter::VRegister MaskReg, IR::OpSize VectorIndexSize,
size_t DataElementOffsetStart, size_t IndexElementOffsetStart, uint8_t OffsetScale);
size_t DataElementOffsetStart, size_t IndexElementOffsetStart, uint8_t OffsetScale, IR::OpSize AddrSize);
void EmitTFCheck();
@@ -614,8 +430,17 @@ private:
void EmitEntryPoint(ARMEmitter::BackwardLabel& HeaderLabel, bool CheckTF);
// Runtime selection;
// Load and store TSO memory style
OpType RT_LoadMemTSO;
OpType RT_StoreMemTSO;
#define DEF_OP(x) void Op_##x(IR::IROp_Header const* IROp, IR::Ref Node)
// Dynamic Dispatcher supporting operations
DEF_OP(ParanoidLoadMemTSO);
DEF_OP(ParanoidStoreMemTSO);
///< Unhandled handler
DEF_OP(Unhandled);
+290 -131
View File
@@ -21,7 +21,7 @@ DEF_OP(LoadContext) {
const auto Op = IROp->C<IR::IROp_LoadContext>();
const auto OpSize = IROp->Size;
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
auto Dst = GetReg(Node);
switch (OpSize) {
@@ -52,7 +52,7 @@ DEF_OP(LoadContext) {
DEF_OP(LoadContextPair) {
const auto Op = IROp->C<IR::IROp_LoadContextPair>();
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Dst1 = GetReg(Op->OutValue1);
const auto Dst2 = GetReg(Op->OutValue2);
@@ -78,7 +78,7 @@ DEF_OP(StoreContext) {
const auto Op = IROp->C<IR::IROp_StoreContext>();
const auto OpSize = IROp->Size;
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
auto Src = GetZeroableReg(Op->Value);
switch (OpSize) {
@@ -110,7 +110,7 @@ DEF_OP(StoreContextPair) {
const auto Op = IROp->C<IR::IROp_StoreContextPair>();
const auto OpSize = IROp->Size;
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
auto Src1 = GetZeroableReg(Op->Value1);
auto Src2 = GetZeroableReg(Op->Value2);
@@ -135,11 +135,11 @@ DEF_OP(StoreContextPair) {
DEF_OP(LoadRegister) {
const auto Op = IROp->C<IR::IROp_LoadRegister>();
if (Op->Class == IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
LOGMAN_THROW_A_FMT(Op->Reg < StaticRegisters.size(), "out of range reg");
mov(GetReg(Node).X(), StaticRegisters[Op->Reg].X());
} else if (Op->Class == IR::FPRClass) {
} else if (Op->Class == IR::RegClass::FPR) {
const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit;
LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "out of range reg");
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
@@ -175,12 +175,13 @@ DEF_OP(LoadAF) {
DEF_OP(StoreRegister) {
const auto Op = IROp->C<IR::IROp_StoreRegister>();
auto Reg = IR::PhysicalRegister(Node);
const auto Reg = IR::PhysicalRegister(Node);
const auto RegClass = Reg.AsRegClass();
if (Reg.Class == IR::GPRFixedClass) {
if (RegClass == IR::RegClass::GPRFixed) {
// Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode.
mov(ARMEmitter::Size::i64Bit, GetReg(Reg), GetReg(Op->Value));
} else if (Reg.Class == IR::FPRFixedClass) {
} else if (RegClass == IR::RegClass::FPRFixed) {
const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit;
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
@@ -193,7 +194,7 @@ DEF_OP(StoreRegister) {
mov(guest.Q(), host.Q());
}
} else {
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Reg.Class);
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", RegClass);
}
}
@@ -225,7 +226,7 @@ DEF_OP(LoadContextIndexed) {
const auto Index = GetReg(Op->Index);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
switch (Op->Stride) {
case 1:
case 2:
@@ -288,7 +289,7 @@ DEF_OP(StoreContextIndexed) {
const auto Index = GetReg(Op->Index);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Value = GetReg(Op->Value);
switch (Op->Stride) {
@@ -372,7 +373,7 @@ DEF_OP(SpillRegister) {
const auto OpSize = IROp->Size;
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Src = GetReg(Op->Value);
switch (OpSize) {
case IR::OpSize::i8Bit: {
@@ -413,7 +414,7 @@ DEF_OP(SpillRegister) {
}
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize); break;
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
} else if (Op->Class == FEXCore::IR::RegClass::FPR) {
const auto Src = GetVReg(Op->Value);
switch (OpSize) {
@@ -452,7 +453,7 @@ DEF_OP(SpillRegister) {
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize); break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class);
}
}
@@ -461,7 +462,7 @@ DEF_OP(FillRegister) {
const auto OpSize = IROp->Size;
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
switch (OpSize) {
case IR::OpSize::i8Bit: {
@@ -502,7 +503,7 @@ DEF_OP(FillRegister) {
}
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize); break;
}
} else if (Op->Class == FEXCore::IR::FPRClass) {
} else if (Op->Class == FEXCore::IR::RegClass::FPR) {
const auto Dst = GetVReg(Node);
switch (OpSize) {
@@ -541,7 +542,7 @@ DEF_OP(FillRegister) {
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize); break;
}
} else {
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class);
}
}
@@ -578,14 +579,14 @@ ARMEmitter::ExtendedMemOperand Arm64JITCore::GenerateMemOperand(
return ARMEmitter::ExtendedMemOperand(Base.X(), ARMEmitter::IndexType::OFFSET, Const);
} else {
auto RegOffset = GetReg(Offset);
switch (OffsetType.Val) {
case IR::MEM_OFFSET_SXTX.Val:
switch (OffsetType) {
case IR::MemOffsetType::SXTX:
return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::SXTX, FEXCore::ilog2(OffsetScale));
case IR::MEM_OFFSET_UXTW.Val:
case IR::MemOffsetType::UXTW:
return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::UXTW, FEXCore::ilog2(OffsetScale));
case IR::MEM_OFFSET_SXTW.Val:
case IR::MemOffsetType::SXTW:
return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::SXTW, FEXCore::ilog2(OffsetScale));
default: LOGMAN_MSG_A_FMT("Unhandled GenerateMemOperand OffsetType: {}", OffsetType.Val); break;
default: LOGMAN_MSG_A_FMT("Unhandled GenerateMemOperand OffsetType: {}", OffsetType); break;
}
}
}
@@ -612,20 +613,20 @@ ARMEmitter::Register Arm64JITCore::ApplyMemOperand(IR::OpSize AccessSize, ARMEmi
add(ARMEmitter::Size::i64Bit, Tmp, Base, Tmp, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(OffsetScale));
} else {
auto RegOffset = GetReg(Offset);
switch (OffsetType.Val) {
case IR::MEM_OFFSET_SXTX.Val:
switch (OffsetType) {
case IR::MemOffsetType::SXTX:
add(ARMEmitter::Size::i64Bit, Tmp, Base, RegOffset, ARMEmitter::ExtendedType::SXTX, FEXCore::ilog2(OffsetScale));
break;
case IR::MEM_OFFSET_UXTW.Val:
case IR::MemOffsetType::UXTW:
add(ARMEmitter::Size::i64Bit, Tmp, Base, RegOffset, ARMEmitter::ExtendedType::UXTW, FEXCore::ilog2(OffsetScale));
break;
case IR::MEM_OFFSET_SXTW.Val:
case IR::MemOffsetType::SXTW:
add(ARMEmitter::Size::i64Bit, Tmp, Base, RegOffset, ARMEmitter::ExtendedType::SXTW, FEXCore::ilog2(OffsetScale));
break;
default: LOGMAN_MSG_A_FMT("Unhandled OffsetType: {}", OffsetType.Val); break;
default: LOGMAN_MSG_A_FMT("Unhandled OffsetType: {}", OffsetType); break;
}
}
return Tmp;
@@ -676,7 +677,7 @@ ARMEmitter::SVEMemOperand Arm64JITCore::GenerateSVEMemOperand(IR::OpSize AccessS
// Note that we do nothing with the offset type and offset scale,
// since SVE loads and stores don't have the ability to perform an
// optional extension or shift as part of their behavior.
LOGMAN_THROW_A_FMT(OffsetType.Val == IR::MEM_OFFSET_SXTX.Val, "Currently only the default offset type (SXTX) is supported.");
LOGMAN_THROW_A_FMT(OffsetType == IR::MemOffsetType::SXTX, "Currently only the default offset type (SXTX) is supported.");
const auto RegOffset = GetReg(Offset);
return ARMEmitter::SVEMemOperand(Base.X(), RegOffset.X());
@@ -689,7 +690,7 @@ DEF_OP(LoadMem) {
const auto MemReg = GetReg(Op->Addr);
const auto MemSrc = GenerateMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
switch (OpSize) {
@@ -723,7 +724,7 @@ DEF_OP(LoadMemPair) {
const auto Op = IROp->C<IR::IROp_LoadMemPair>();
const auto Addr = GetReg(Op->Addr);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Dst1 = GetReg(Op->OutValue1);
const auto Dst2 = GetReg(Op->OutValue2);
@@ -751,13 +752,13 @@ DEF_OP(LoadMemTSO) {
const auto MemReg = GetReg(Op->Addr);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
LOGMAN_THROW_A_FMT(Op->Offset.IsInvalid() || CTX->HostFeatures.SupportsTSOImm9, "unexpected offset");
LOGMAN_THROW_A_FMT(Op->OffsetScale == 1, "unexpected offset scale");
LOGMAN_THROW_A_FMT(Op->OffsetType == IR::MEM_OFFSET_SXTX, "unexpected offset type");
LOGMAN_THROW_A_FMT(Op->OffsetType == IR::MemOffsetType::SXTX, "unexpected offset type");
}
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == FEXCore::IR::GPRClass) {
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
uint64_t Offset = 0;
if (!Op->Offset.IsInvalid()) {
@@ -776,12 +777,10 @@ DEF_OP(LoadMemTSO) {
case IR::OpSize::i64Bit: ldapur(Dst.X(), MemReg, Offset); break;
default: LOGMAN_MSG_A_FMT("Unhandled LoadMemTSO size: {}", OpSize); break;
}
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
// Half-barrier once back-patched.
nop();
}
// Half-barrier once back-patched.
nop();
}
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == FEXCore::IR::GPRClass) {
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
if (OpSize == IR::OpSize::i8Bit) {
// 8bit load is always aligned to natural alignment
@@ -793,12 +792,10 @@ DEF_OP(LoadMemTSO) {
case IR::OpSize::i64Bit: ldapr(Dst.X(), MemReg); break;
default: LOGMAN_MSG_A_FMT("Unhandled LoadMemTSO size: {}", OpSize); break;
}
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
// Half-barrier once back-patched.
nop();
}
// Half-barrier once back-patched.
nop();
}
} else if (Op->Class == FEXCore::IR::GPRClass) {
} else if (Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
if (OpSize == IR::OpSize::i8Bit) {
// 8bit load is always aligned to natural alignment
@@ -810,10 +807,8 @@ DEF_OP(LoadMemTSO) {
case IR::OpSize::i64Bit: ldar(Dst.X(), MemReg); break;
default: LOGMAN_MSG_A_FMT("Unhandled LoadMemTSO size: {}", OpSize); break;
}
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
// Half-barrier once back-patched.
nop();
}
// Half-barrier once back-patched.
nop();
}
} else {
const auto Dst = GetVReg(Node);
@@ -917,7 +912,7 @@ DEF_OP(VLoadVectorMasked) {
// If the sign bit is zero then skip the load
ARMEmitter::ForwardLabel Skip {};
(void)tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
// Do the gather load for this element into the destination
switch (IROp->ElementSize) {
case IR::OpSize::i8Bit: ld1<ARMEmitter::SubRegSize::i8Bit>(TempDst.Q(), i, TempMemReg); break;
@@ -928,7 +923,7 @@ DEF_OP(VLoadVectorMasked) {
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, IROp->ElementSize); return;
}
(void)Bind(&Skip);
Bind(&Skip);
if ((i + 1) != NumElements) {
// Handle register rename to save a move.
@@ -1018,7 +1013,7 @@ DEF_OP(VStoreVectorMasked) {
// If the sign bit is zero then skip the load
ARMEmitter::ForwardLabel Skip {};
(void)tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
// Do the gather load for this element into the destination
switch (IROp->ElementSize) {
case IR::OpSize::i8Bit: st1<ARMEmitter::SubRegSize::i8Bit>(RegData.Q(), i, TempMemReg); break;
@@ -1029,7 +1024,7 @@ DEF_OP(VStoreVectorMasked) {
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, IROp->ElementSize); return;
}
(void)Bind(&Skip);
Bind(&Skip);
if ((i + 1) != NumElements) {
// Handle register rename to save a move.
@@ -1045,7 +1040,7 @@ void Arm64JITCore::Emulate128BitGather(IR::OpSize Size, IR::OpSize ElementSize,
ARMEmitter::VRegister IncomingDst, std::optional<ARMEmitter::Register> BaseAddr,
ARMEmitter::VRegister VectorIndexLow, std::optional<ARMEmitter::VRegister> VectorIndexHigh,
ARMEmitter::VRegister MaskReg, IR::OpSize VectorIndexSize, size_t DataElementOffsetStart,
size_t IndexElementOffsetStart, uint8_t OffsetScale) {
size_t IndexElementOffsetStart, uint8_t OffsetScale, IR::OpSize AddrSize) {
LOGMAN_THROW_A_FMT(ElementSize >= IR::OpSize::i8Bit && ElementSize <= IR::OpSize::i64Bit, "Invalid element size");
const auto PerformSMove = [this](IR::OpSize ElementSize, const ARMEmitter::Register Dst, const ARMEmitter::VRegister Vector, int index) {
@@ -1107,7 +1102,7 @@ void Arm64JITCore::Emulate128BitGather(IR::OpSize Size, IR::OpSize ElementSize,
PerformMove(ElementSize, WorkingReg, MaskReg, i);
// Skip if the mask's sign bit isn't set
(void)tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
// Extract Index Element
if ((IndexElement * IR::OpSizeToSize(VectorIndexSize)) >= 16) {
@@ -1121,17 +1116,17 @@ void Arm64JITCore::Emulate128BitGather(IR::OpSize Size, IR::OpSize ElementSize,
// Calculate memory position for this gather load
if (BaseAddr.has_value()) {
if (VectorIndexSize == IR::OpSize::i32Bit) {
add(ARMEmitter::Size::i64Bit, TempMemReg, *BaseAddr, WorkingReg, ARMEmitter::ExtendedType::SXTW, FEXCore::ilog2(OffsetScale));
add(ConvertSize(AddrSize), TempMemReg, *BaseAddr, WorkingReg, ARMEmitter::ExtendedType::SXTW, FEXCore::ilog2(OffsetScale));
} else {
add(ARMEmitter::Size::i64Bit, TempMemReg, *BaseAddr, WorkingReg, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(OffsetScale));
add(ConvertSize(AddrSize), TempMemReg, *BaseAddr, WorkingReg, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(OffsetScale));
}
} else {
///< In this case we have no base address, All addresses come from the vector register itself
if (VectorIndexSize == IR::OpSize::i32Bit) {
// Sign extend and shift in to the 64-bit register
sbfiz(ARMEmitter::Size::i64Bit, TempMemReg, WorkingReg, FEXCore::ilog2(OffsetScale), 32);
sbfiz(ConvertSize(AddrSize), TempMemReg, WorkingReg, FEXCore::ilog2(OffsetScale), 32);
} else {
lsl(ARMEmitter::Size::i64Bit, TempMemReg, WorkingReg, FEXCore::ilog2(OffsetScale));
lsl(ConvertSize(AddrSize), TempMemReg, WorkingReg, FEXCore::ilog2(OffsetScale));
}
}
@@ -1145,7 +1140,7 @@ void Arm64JITCore::Emulate128BitGather(IR::OpSize Size, IR::OpSize ElementSize,
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, ElementSize); FEX_UNREACHABLE;
}
(void)Bind(&Skip);
Bind(&Skip);
}
if (NeedsDestTmp) {
@@ -1189,7 +1184,8 @@ DEF_OP(VLoadVectorGatherMasked) {
///< If the host supports SVE and the offset scale matches SVE limitations then it can do an SVE style load.
const bool SupportsSVELoad = (HostSupportsSVE128 || HostSupportsSVE256) &&
(OffsetScale == 1 || OffsetScale == IR::OpSizeToSize(VectorIndexSize)) && VectorIndexSize == IROp->ElementSize;
(OffsetScale == 1 || OffsetScale == IR::OpSizeToSize(VectorIndexSize)) &&
VectorIndexSize == IROp->ElementSize && Op->AddrSize == IR::OpSize::i64Bit;
if (SupportsSVELoad) {
uint8_t SVEScale = FEXCore::ilog2(OffsetScale);
@@ -1247,7 +1243,7 @@ DEF_OP(VLoadVectorGatherMasked) {
} else {
LOGMAN_THROW_A_FMT(!Is256Bit, "Can't emulate this gather load in the backend! Programming error!");
Emulate128BitGather(IROp->Size, IROp->ElementSize, Dst, IncomingDst, BaseAddr, VectorIndexLow, VectorIndexHigh, MaskReg,
VectorIndexSize, DataElementOffsetStart, IndexElementOffsetStart, OffsetScale);
VectorIndexSize, DataElementOffsetStart, IndexElementOffsetStart, OffsetScale, Op->AddrSize);
}
}
@@ -1272,7 +1268,9 @@ DEF_OP(VLoadVectorGatherMaskedQPS) {
!Op->VectorIndexHigh.IsInvalid() ? std::make_optional(GetVReg(Op->VectorIndexHigh)) : std::nullopt;
///< If the host supports SVE and the offset scale matches SVE limitations then it can do an SVE style load.
if (HostSupportsSVE128 && (OffsetScale == 1 || OffsetScale == 4)) {
const bool SupportsSVELoad = HostSupportsSVE128 && (OffsetScale == 1 || OffsetScale == 4) && Op->AddrSize == IR::OpSize::i64Bit;
if (SupportsSVELoad) {
ARMEmitter::SVEModType ModType = ARMEmitter::SVEModType::MOD_NONE;
if (OffsetScale != 1) {
ModType = ARMEmitter::SVEModType::MOD_LSL;
@@ -1326,7 +1324,7 @@ DEF_OP(VLoadVectorGatherMaskedQPS) {
}
} else {
Emulate128BitGather(IR::OpSize::i128Bit, IR::OpSize::i32Bit, Dst, IncomingDst, BaseAddr, VectorIndexLow, VectorIndexHigh, MaskReg,
IR::OpSize::i64Bit, 0, 0, OffsetScale);
IR::OpSize::i64Bit, 0, 0, OffsetScale, Op->AddrSize);
}
}
@@ -1627,7 +1625,7 @@ DEF_OP(StoreMem) {
const auto MemReg = GetReg(Op->Addr);
const auto MemSrc = GenerateMemOperand(OpSize, MemReg, Op->Offset, Op->OffsetType, Op->OffsetScale);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Src = GetZeroableReg(Op->Value);
switch (OpSize) {
case IR::OpSize::i8Bit: strb(Src, MemSrc); break;
@@ -1738,7 +1736,7 @@ DEF_OP(StoreMemPair) {
const auto OpSize = IROp->Size;
const auto Addr = GetReg(Op->Addr);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
const auto Src1 = GetZeroableReg(Op->Value1);
const auto Src2 = GetZeroableReg(Op->Value2);
switch (OpSize) {
@@ -1765,13 +1763,13 @@ DEF_OP(StoreMemTSO) {
const auto MemReg = GetReg(Op->Addr);
if (Op->Class == FEXCore::IR::GPRClass) {
if (Op->Class == IR::RegClass::GPR) {
LOGMAN_THROW_A_FMT(Op->Offset.IsInvalid() || CTX->HostFeatures.SupportsTSOImm9, "unexpected offset");
LOGMAN_THROW_A_FMT(Op->OffsetScale == 1, "unexpected offset scale");
LOGMAN_THROW_A_FMT(Op->OffsetType == IR::MEM_OFFSET_SXTX, "unexpected offset type");
LOGMAN_THROW_A_FMT(Op->OffsetType == IR::MemOffsetType::SXTX, "unexpected offset type");
}
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == FEXCore::IR::GPRClass) {
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
const auto Src = GetZeroableReg(Op->Value);
uint64_t Offset = 0;
if (!Op->Offset.IsInvalid()) {
@@ -1783,10 +1781,8 @@ DEF_OP(StoreMemTSO) {
// 8bit load is always aligned to natural alignment
stlurb(Src, MemReg, Offset);
} else {
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
// Half-barrier once back-patched.
nop();
}
// Half-barrier once back-patched.
nop();
switch (OpSize) {
case IR::OpSize::i16Bit: stlurh(Src, MemReg, Offset); break;
case IR::OpSize::i32Bit: stlur(Src.W(), MemReg, Offset); break;
@@ -1794,17 +1790,15 @@ DEF_OP(StoreMemTSO) {
default: LOGMAN_MSG_A_FMT("Unhandled StoreMemTSO size: {}", OpSize); break;
}
}
} else if (Op->Class == FEXCore::IR::GPRClass) {
} else if (Op->Class == IR::RegClass::GPR) {
const auto Src = GetZeroableReg(Op->Value);
if (OpSize == IR::OpSize::i8Bit) {
// 8bit load is always aligned to natural alignment
stlrb(Src, MemReg);
} else {
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
// Half-barrier once back-patched.
nop();
}
// Half-barrier once back-patched.
nop();
switch (OpSize) {
case IR::OpSize::i16Bit: stlrh(Src, MemReg); break;
case IR::OpSize::i32Bit: stlr(Src.W(), MemReg); break;
@@ -1880,7 +1874,7 @@ DEF_OP(MemSet) {
if (!DirectionIsInline) {
// Backward or forwards implementation depends on flag
(void)tbnz(DirectionReg, 1, &BackwardImpl);
tbnz(DirectionReg, 1, &BackwardImpl);
}
auto MemStore = [this](auto Value, uint32_t OpSize, int32_t Size) {
@@ -1898,9 +1892,7 @@ DEF_OP(MemSet) {
// 8bit load is always aligned to natural alignment
stlrb(Value.W(), TMP2);
} else {
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
nop();
}
nop();
switch (OpSize) {
case 2: stlrh(Value.W(), TMP2); break;
case 4: stlr(Value.W(), TMP2); break;
@@ -1930,7 +1922,7 @@ DEF_OP(MemSet) {
ARMEmitter::ForwardLabel DoneInternal {};
// Early exit if zero count.
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
if (!IsAtomic) {
ARMEmitter::ForwardLabel AgainInternal256Exit {};
@@ -1947,50 +1939,50 @@ DEF_OP(MemSet) {
// Do this in two parts, to fallback to the byte by byte loop if size < 32, and to the
// single copy loop if size < 64.
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
tbnz(TMP1, 63, &AgainInternal128Exit);
// Fill VTMP2 with the set pattern
dup(SubRegSize, VTMP2.Q(), Value);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbnz(TMP1, 63, &AgainInternal256Exit);
tbnz(TMP1, 63, &AgainInternal256Exit);
(void)Bind(&AgainInternal256);
Bind(&AgainInternal256);
stp<ARMEmitter::IndexType::POST>(VTMP2.Q(), VTMP2.Q(), TMP2, 32 * Direction);
stp<ARMEmitter::IndexType::POST>(VTMP2.Q(), VTMP2.Q(), TMP2, 32 * Direction);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
(void)tbz(TMP1, 63, &AgainInternal256);
tbz(TMP1, 63, &AgainInternal256);
(void)Bind(&AgainInternal256Exit);
Bind(&AgainInternal256Exit);
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
(void)Bind(&AgainInternal128);
tbnz(TMP1, 63, &AgainInternal128Exit);
Bind(&AgainInternal128);
stp<ARMEmitter::IndexType::POST>(VTMP2.Q(), VTMP2.Q(), TMP2, 32 * Direction);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbz(TMP1, 63, &AgainInternal128);
tbz(TMP1, 63, &AgainInternal128);
(void)Bind(&AgainInternal128Exit);
Bind(&AgainInternal128Exit);
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
if (Direction == -1) {
add(ARMEmitter::Size::i64Bit, TMP2, TMP2, 32 - Size);
}
}
(void)Bind(&AgainInternal);
Bind(&AgainInternal);
if (IsAtomic) {
MemStoreTSO(Value, OpSize, SizeDirection);
} else {
MemStore(Value, OpSize, SizeDirection);
}
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 1);
(void)cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
(void)Bind(&DoneInternal);
Bind(&DoneInternal);
if (SizeDirection >= 0) {
switch (OpSize) {
@@ -2020,12 +2012,12 @@ DEF_OP(MemSet) {
EmitMemset(Direction);
if (Direction == 1) {
(void)b(&Done);
(void)Bind(&BackwardImpl);
b(&Done);
Bind(&BackwardImpl);
}
}
(void)Bind(&Done);
Bind(&Done);
// Destination already set to the final pointer.
}
}
@@ -2075,7 +2067,7 @@ DEF_OP(MemCpy) {
if (!DirectionIsInline) {
// Backward or forwards implementation depends on flag
(void)tbnz(DirectionReg, 1, &BackwardImpl);
tbnz(DirectionReg, 1, &BackwardImpl);
}
auto MemCpy = [this](uint32_t OpSize, int32_t Size) {
@@ -2118,11 +2110,9 @@ DEF_OP(MemCpy) {
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size); break;
}
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
// Placeholders for backpatching barriers (one per load/store)
nop();
nop();
}
// Placeholders for backpatching barriers (one per load/store)
nop();
nop();
switch (OpSize) {
case 2: stlrh(TMP4.W(), TMP2); break;
@@ -2144,11 +2134,9 @@ DEF_OP(MemCpy) {
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size); break;
}
if (HalfBarrierTSOEnabled() && !ParanoidTSO()) {
// Placeholders for backpatching barriers (one per load/store)
nop();
nop();
}
// Placeholders for backpatching barriers (one per load/store)
nop();
nop();
switch (OpSize) {
case 2: stlrh(TMP4.W(), TMP2); break;
@@ -2176,7 +2164,7 @@ DEF_OP(MemCpy) {
ARMEmitter::ForwardLabel DoneInternal {};
// Early exit if zero count.
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
if (!IsAtomic) {
ARMEmitter::ForwardLabel AbsPos {};
@@ -2186,11 +2174,11 @@ DEF_OP(MemCpy) {
ARMEmitter::BackwardLabel AgainInternal256 {};
sub(ARMEmitter::Size::i64Bit, TMP4, TMP2, TMP3);
(void)tbz(TMP4, 63, &AbsPos);
tbz(TMP4, 63, &AbsPos);
neg(ARMEmitter::Size::i64Bit, TMP4, TMP4);
(void)Bind(&AbsPos);
Bind(&AbsPos);
sub(ARMEmitter::Size::i64Bit, TMP4, TMP4, 32);
(void)tbnz(TMP4, 63, &AgainInternal);
tbnz(TMP4, 63, &AgainInternal);
if (Direction == -1) {
sub(ARMEmitter::Size::i64Bit, TMP2, TMP2, 32 - Size);
@@ -2202,30 +2190,30 @@ DEF_OP(MemCpy) {
// Do this in two parts, to fallback to the byte by byte loop if size < 32, and to the
// single copy loop if size < 64.
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
tbnz(TMP1, 63, &AgainInternal128Exit);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbnz(TMP1, 63, &AgainInternal256Exit);
tbnz(TMP1, 63, &AgainInternal256Exit);
(void)Bind(&AgainInternal256);
Bind(&AgainInternal256);
MemCpy(32, 32 * Direction);
MemCpy(32, 32 * Direction);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
(void)tbz(TMP1, 63, &AgainInternal256);
tbz(TMP1, 63, &AgainInternal256);
(void)Bind(&AgainInternal256Exit);
Bind(&AgainInternal256Exit);
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
(void)Bind(&AgainInternal128);
tbnz(TMP1, 63, &AgainInternal128Exit);
Bind(&AgainInternal128);
MemCpy(32, 32 * Direction);
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)tbz(TMP1, 63, &AgainInternal128);
tbz(TMP1, 63, &AgainInternal128);
(void)Bind(&AgainInternal128Exit);
Bind(&AgainInternal128Exit);
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
if (Direction == -1) {
add(ARMEmitter::Size::i64Bit, TMP2, TMP2, 32 - Size);
@@ -2233,16 +2221,16 @@ DEF_OP(MemCpy) {
}
}
(void)Bind(&AgainInternal);
Bind(&AgainInternal);
if (IsAtomic) {
MemCpyTSO(OpSize, SizeDirection);
} else {
MemCpy(OpSize, SizeDirection);
}
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 1);
(void)cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
(void)Bind(&DoneInternal);
Bind(&DoneInternal);
// Needs to use temporaries just in case of overwrite
mov(TMP1, MemRegDest.X());
@@ -2300,15 +2288,186 @@ DEF_OP(MemCpy) {
for (int32_t Direction : {1, -1}) {
EmitMemcpy(Direction);
if (Direction == 1) {
(void)b(&Done);
(void)Bind(&BackwardImpl);
b(&Done);
Bind(&BackwardImpl);
}
}
(void)Bind(&Done);
Bind(&Done);
// Destination already set to the final pointer.
}
}
DEF_OP(ParanoidLoadMemTSO) {
const auto Op = IROp->C<IR::IROp_LoadMemTSO>();
const auto OpSize = IROp->Size;
auto MemReg = GetReg(Op->Addr);
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
uint64_t Offset = 0;
if (!Op->Offset.IsInvalid()) {
if (!IsInlineConstant(Op->Offset, &Offset)) {
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
}
}
if (OpSize == IR::OpSize::i8Bit) {
// 8bit load is always aligned to natural alignment
const auto Dst = GetReg(Node);
ldapurb(Dst, MemReg, Offset);
} else {
switch (OpSize) {
case IR::OpSize::i16Bit: ldapurh(Dst, MemReg, Offset); break;
case IR::OpSize::i32Bit: ldapur(Dst.W(), MemReg, Offset); break;
case IR::OpSize::i64Bit: ldapur(Dst.X(), MemReg, Offset); break;
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
}
}
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
if (OpSize == IR::OpSize::i8Bit) {
// 8bit load is always aligned to natural alignment
ldaprb(Dst.W(), MemReg);
} else {
switch (OpSize) {
case IR::OpSize::i16Bit: ldaprh(Dst.W(), MemReg); break;
case IR::OpSize::i32Bit: ldapr(Dst.W(), MemReg); break;
case IR::OpSize::i64Bit: ldapr(Dst.X(), MemReg); break;
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
}
}
} else if (Op->Class == IR::RegClass::GPR) {
const auto Dst = GetReg(Node);
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
switch (OpSize) {
case IR::OpSize::i8Bit: ldarb(Dst, MemReg); break;
case IR::OpSize::i16Bit: ldarh(Dst, MemReg); break;
case IR::OpSize::i32Bit: ldar(Dst.W(), MemReg); break;
case IR::OpSize::i64Bit: ldar(Dst.X(), MemReg); break;
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
}
} else {
const auto Dst = GetVReg(Node);
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
switch (OpSize) {
case IR::OpSize::i8Bit:
ldarb(TMP1, MemReg);
fmov(ARMEmitter::Size::i32Bit, Dst.S(), TMP1.W());
break;
case IR::OpSize::i16Bit:
ldarh(TMP1, MemReg);
fmov(ARMEmitter::Size::i32Bit, Dst.S(), TMP1.W());
break;
case IR::OpSize::i32Bit:
ldar(TMP1.W(), MemReg);
fmov(ARMEmitter::Size::i32Bit, Dst.S(), TMP1.W());
break;
case IR::OpSize::i64Bit:
ldar(TMP1, MemReg);
fmov(ARMEmitter::Size::i64Bit, Dst.D(), TMP1);
break;
case IR::OpSize::i128Bit:
ldaxp(ARMEmitter::Size::i64Bit, TMP1, TMP2, MemReg);
clrex();
ins(ARMEmitter::SubRegSize::i64Bit, Dst, 0, TMP1);
ins(ARMEmitter::SubRegSize::i64Bit, Dst, 1, TMP2);
break;
case IR::OpSize::i256Bit:
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use {} with 256-bit operation", __func__);
dmb(ARMEmitter::BarrierScope::ISH);
ld1b<ARMEmitter::SubRegSize::i8Bit>(Dst.Z(), PRED_TMP_32B.Zeroing(), MemReg);
dmb(ARMEmitter::BarrierScope::ISH);
break;
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
}
}
}
DEF_OP(ParanoidStoreMemTSO) {
const auto Op = IROp->C<IR::IROp_StoreMemTSO>();
const auto OpSize = IROp->Size;
auto MemReg = GetReg(Op->Addr);
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
const auto Src = GetZeroableReg(Op->Value);
uint64_t Offset = 0;
if (!Op->Offset.IsInvalid()) {
if (!IsInlineConstant(Op->Offset, &Offset)) {
MemReg = ApplyMemOperand(OpSize, MemReg, TMP1, Op->Offset, Op->OffsetType, Op->OffsetScale);
}
}
if (OpSize == IR::OpSize::i8Bit) {
// 8bit load is always aligned to natural alignment
stlurb(Src, MemReg, Offset);
} else {
switch (OpSize) {
case IR::OpSize::i16Bit: stlurh(Src, MemReg, Offset); break;
case IR::OpSize::i32Bit: stlur(Src.W(), MemReg, Offset); break;
case IR::OpSize::i64Bit: stlur(Src.X(), MemReg, Offset); break;
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidStoreMemTSO size: {}", OpSize); break;
}
}
} else if (Op->Class == IR::RegClass::GPR) {
const auto Src = GetZeroableReg(Op->Value);
MemReg = ApplyMemOperand(OpSize, MemReg, TMP1, Op->Offset, Op->OffsetType, Op->OffsetScale);
switch (OpSize) {
case IR::OpSize::i8Bit: stlrb(Src, MemReg); break;
case IR::OpSize::i16Bit: stlrh(Src, MemReg); break;
case IR::OpSize::i32Bit: stlr(Src.W(), MemReg); break;
case IR::OpSize::i64Bit: stlr(Src.X(), MemReg); break;
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidStoreMemTSO size: {}", OpSize); break;
}
} else {
const auto Src = GetVReg(Op->Value);
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
switch (OpSize) {
case IR::OpSize::i8Bit:
umov<ARMEmitter::SubRegSize::i8Bit>(TMP1, Src, 0);
stlrb(TMP1, MemReg);
break;
case IR::OpSize::i16Bit:
umov<ARMEmitter::SubRegSize::i16Bit>(TMP1, Src, 0);
stlrh(TMP1, MemReg);
break;
case IR::OpSize::i32Bit:
umov<ARMEmitter::SubRegSize::i32Bit>(TMP1, Src, 0);
stlr(TMP1.W(), MemReg);
break;
case IR::OpSize::i64Bit:
umov<ARMEmitter::SubRegSize::i64Bit>(TMP1, Src, 0);
stlr(TMP1, MemReg);
break;
case IR::OpSize::i128Bit: {
// Move vector to GPRs
umov<ARMEmitter::SubRegSize::i64Bit>(TMP1, Src, 0);
umov<ARMEmitter::SubRegSize::i64Bit>(TMP2, Src, 1);
ARMEmitter::BackwardLabel B;
Bind(&B);
// ldaxp must not have both the destination registers be the same
ldaxp(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::zr, TMP3, MemReg); // <- Can hit SIGBUS. Overwritten with DMB
stlxp(ARMEmitter::Size::i64Bit, TMP3, TMP1, TMP2, MemReg); // <- Can also hit SIGBUS
cbnz(ARMEmitter::Size::i64Bit, TMP3, &B); // < Overwritten with DMB
break;
}
case IR::OpSize::i256Bit: {
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use {} with 256-bit operation", __func__);
dmb(ARMEmitter::BarrierScope::ISH);
st1b<ARMEmitter::SubRegSize::i8Bit>(Src.Z(), PRED_TMP_32B, MemReg, 0);
dmb(ARMEmitter::BarrierScope::ISH);
break;
}
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidStoreMemTSO size: {}", OpSize); break;
}
}
}
DEF_OP(CacheLineClear) {
if (!CTX->HostFeatures.SupportsCacheMaintenanceOps) {
dmb(ARMEmitter::BarrierScope::SY);
@@ -15,6 +15,7 @@ $end_info$
#include <FEXCore/Core/SignalDelegator.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/EnumUtils.h>
namespace FEXCore::CPU {
@@ -47,10 +48,10 @@ DEF_OP(GuestOpcode) {
DEF_OP(Fence) {
auto Op = IROp->C<IR::IROp_Fence>();
switch (Op->Fence) {
case IR::Fence_Load.Val: dmb(ARMEmitter::BarrierScope::LD); break;
case IR::Fence_LoadStore.Val: dmb(ARMEmitter::BarrierScope::SY); break;
case IR::Fence_Store.Val: dmb(ARMEmitter::BarrierScope::ST); break;
case IR::Fence_Inst.Val: isb(); break;
case IR::FenceType::Load: dmb(ARMEmitter::BarrierScope::LD); break;
case IR::FenceType::LoadStore: dmb(ARMEmitter::BarrierScope::SY); break;
case IR::FenceType::Store: dmb(ARMEmitter::BarrierScope::ST); break;
case IR::FenceType::Inst: isb(); break;
default: LOGMAN_MSG_A_FMT("Unknown Fence: {}", Op->Fence); break;
}
}
@@ -107,10 +108,10 @@ DEF_OP(GetRoundingMode) {
// zero. Just swapping 01 and 10. That's a bitfield reverse. Round mode is in
// bottom two bits. After reversing as a 32-bit operation, it'll be in [31:30]
// and ripe for reinsertion back at 0.
static_assert(IR::ROUND_MODE_NEAREST == 0);
static_assert(IR::ROUND_MODE_NEGATIVE_INFINITY == 1);
static_assert(IR::ROUND_MODE_POSITIVE_INFINITY == 2);
static_assert(IR::ROUND_MODE_TOWARDS_ZERO == 3);
static_assert(FEXCore::ToUnderlying(IR::RoundMode::Nearest) == 0);
static_assert(FEXCore::ToUnderlying(IR::RoundMode::NegInfinity) == 1);
static_assert(FEXCore::ToUnderlying(IR::RoundMode::PosInfinity) == 2);
static_assert(FEXCore::ToUnderlying(IR::RoundMode::TowardsZero) == 3);
rbit(ARMEmitter::Size::i32Bit, TMP1, Dst);
bfi(ARMEmitter::Size::i64Bit, Dst, TMP1, 30, 2);
@@ -18,18 +18,4 @@ DEF_OP(RMWHandle) {
mov(ARMEmitter::Size::i64Bit, GetReg(Node), GetReg(IROp->Args[0]));
}
DEF_OP(Swap1) {
auto Op = IROp->C<IR::IROp_Swap1>();
auto A = GetReg(Op->A), B = GetReg(Op->B);
LOGMAN_THROW_A_FMT(B == GetReg(Node), "Invariant");
mov(ARMEmitter::Size::i64Bit, TMP1, A);
mov(ARMEmitter::Size::i64Bit, A, B);
mov(ARMEmitter::Size::i64Bit, B, TMP1);
}
DEF_OP(Swap2) {
// Implemented above
}
} // namespace FEXCore::CPU
@@ -41,6 +41,7 @@ namespace FEXCore::CPU {
const auto Op = IROp->C<IR::IROp_##FEXOp>(); \
const auto OpSize = IROp->Size; \
const auto Is256Bit = OpSize == IR::OpSize::i256Bit; \
const auto Is128Bit = OpSize == IR::OpSize::i128Bit; \
LOGMAN_THROW_A_FMT(!Is256Bit || HostSupportsSVE256, "Need SVE256 support in order to use {} with 256-bit operation", __func__); \
\
const auto Dst = GetVReg(Node); \
@@ -49,8 +50,10 @@ namespace FEXCore::CPU {
\
if (HostSupportsSVE256 && Is256Bit) { \
ARMOp(Dst.Z(), Vector1.Z(), Vector2.Z()); \
} else { \
} else if (Is128Bit) { \
ARMOp(Dst.Q(), Vector1.Q(), Vector2.Q()); \
} else { \
ARMOp(Dst.D(), Vector1.D(), Vector2.D()); \
} \
}
@@ -744,11 +747,11 @@ DEF_OP(VFToIScalarInsert) {
auto Src = *std::get_if<ARMEmitter::VRegister>(&SrcVar);
switch (RoundMode) {
case IR::Round_Nearest: frintn(SubRegSize.Scalar, Dst, Src); break;
case IR::Round_Negative_Infinity: frintm(SubRegSize.Scalar, Dst, Src); break;
case IR::Round_Positive_Infinity: frintp(SubRegSize.Scalar, Dst, Src); break;
case IR::Round_Towards_Zero: frintz(SubRegSize.Scalar, Dst, Src); break;
case IR::Round_Host: frinti(SubRegSize.Scalar, Dst, Src); break;
case IR::RoundMode::Nearest: frintn(SubRegSize.Scalar, Dst, Src); break;
case IR::RoundMode::NegInfinity: frintm(SubRegSize.Scalar, Dst, Src); break;
case IR::RoundMode::PosInfinity: frintp(SubRegSize.Scalar, Dst, Src); break;
case IR::RoundMode::TowardsZero: frintz(SubRegSize.Scalar, Dst, Src); break;
case IR::RoundMode::Host: frinti(SubRegSize.Scalar, Dst, Src); break;
}
};
File diff suppressed because it is too large. Load diff
+189 -115
View File
@@ -139,27 +139,28 @@ public:
FlushRegisterCache();
return _Jump(_TargetBlock);
}
IRPair<IROp_CondJump> CondJump(Ref _Cmp1, Ref _Cmp2, Ref _TrueBlock, Ref _FalseBlock, CondClassType _Cond = {COND_NEQ},
IRPair<IROp_CondJump> CondJump(Ref _Cmp1, Ref _Cmp2, Ref _TrueBlock, Ref _FalseBlock, CondClass _Cond = CondClass::NEQ,
IR::OpSize _CompareSize = OpSize::iInvalid) {
FlushRegisterCache();
return _CondJump(_Cmp1, _Cmp2, _TrueBlock, _FalseBlock, _Cond, _CompareSize);
}
IRPair<IROp_CondJump> CondJump(Ref ssa0, CondClassType cond = {COND_NEQ}) {
IRPair<IROp_CondJump> CondJump(Ref ssa0, CondClass cond = CondClass::NEQ) {
FlushRegisterCache();
return _CondJump(ssa0, cond);
}
IRPair<IROp_CondJump> CondJump(Ref ssa0, Ref ssa1, Ref ssa2, CondClassType cond = {COND_NEQ}) {
IRPair<IROp_CondJump> CondJump(Ref ssa0, Ref ssa1, Ref ssa2, CondClass cond = CondClass::NEQ) {
FlushRegisterCache();
return _CondJump(ssa0, ssa1, ssa2, cond);
}
IRPair<IROp_CondJump> CondJumpNZCV(CondClassType Cond) {
IRPair<IROp_CondJump> CondJumpNZCV(CondClass Cond) {
FlushRegisterCache();
return _CondJump(InvalidNode, InvalidNode, InvalidNode, InvalidNode, Cond, OpSize::iInvalid, true);
}
IRPair<IROp_CondJump> CondJumpBit(Ref Src, unsigned Bit, bool Set) {
FlushRegisterCache();
auto InlineConst = _InlineConstant(Bit);
return _CondJump(Src, InlineConst, InvalidNode, InvalidNode, {Set ? COND_TSTNZ : COND_TSTZ}, OpSize::iInvalid, false);
auto Cond = Set ? CondClass::TSTNZ : CondClass::TSTZ;
return _CondJump(Src, InlineConst, InvalidNode, InvalidNode, Cond, OpSize::iInvalid, false);
}
IRPair<IROp_ExitFunction> ExitFunction(Ref NewRIP, BranchHint Hint = BranchHint::None) {
FlushRegisterCache();
@@ -251,7 +252,7 @@ public:
auto ExitBlock = CreateNewCodeBlockAfter(BackwardBlock);
auto DF = GetRFLAG(X86State::RFLAG_DF_RAW_LOC);
CondJump(DF, Zero, ForwardBlock, BackwardBlock, {COND_EQ});
CondJump(DF, Zero, ForwardBlock, BackwardBlock, CondClass::EQ);
for (auto D = 0; D < 2; ++D) {
SetCurrentCodeBlock(D ? BackwardBlock : ForwardBlock);
@@ -564,7 +565,7 @@ public:
template<IR::OpSize DstElementSize, IR::OpSize SrcElementSize>
void AVXInsertScalar_CVT_Float_To_Float(OpcodeArgs);
RoundType TranslateRoundType(uint8_t Mode);
RoundMode TranslateRoundType(uint8_t Mode);
template<IR::OpSize ElementSize>
void InsertScalarRound(OpcodeArgs);
@@ -758,7 +759,6 @@ public:
void X87FXTRACT(OpcodeArgs);
void X87FYL2X(OpcodeArgs, bool IsFYL2XP1);
void X87LDENV(OpcodeArgs);
void X87LDSW(OpcodeArgs);
void X87ModifySTP(OpcodeArgs, bool Inc);
void X87OpHelper(OpcodeArgs, FEXCore::IR::IROps IROp, bool ZeroC2);
@@ -907,6 +907,10 @@ public:
void VPCLMULQDQOp(OpcodeArgs);
void CRC32(OpcodeArgs);
void Extrq_imm(OpcodeArgs);
void Insertq_imm(OpcodeArgs);
void Extrq(OpcodeArgs);
void Insertq(OpcodeArgs);
void BreakOp(OpcodeArgs, FEXCore::IR::BreakDefinition BreakDefinition);
void UnimplementedOp(OpcodeArgs);
@@ -985,7 +989,6 @@ public:
void AVX128_VPSIGN(OpcodeArgs, IR::OpSize ElementSize);
void AVX128_UCOMISx(OpcodeArgs, IR::OpSize ElementSize);
void AVX128_VectorScalarInsertALU(OpcodeArgs, FEXCore::IR::IROps IROp, IR::OpSize ElementSize);
Ref AVX128_VFCMPImpl(IR::OpSize ElementSize, Ref Src1, Ref Src2, uint8_t CompType);
void AVX128_VFCMP(OpcodeArgs, IR::OpSize ElementSize);
void AVX128_InsertScalarFCMP(OpcodeArgs, IR::OpSize ElementSize);
void AVX128_MOVBetweenGPR_FPR(OpcodeArgs);
@@ -1004,9 +1007,7 @@ public:
void AVX128_VINSERT(OpcodeArgs);
void AVX128_VINSERTPS(OpcodeArgs);
Ref AVX128_PHSUBImpl(Ref Src1, Ref Src2, size_t ElementSize);
void AVX128_VPHSUB(OpcodeArgs, IR::OpSize ElementSize);
void AVX128_VPHSUBSW(OpcodeArgs);
void AVX128_VADDSUBP(OpcodeArgs, IR::OpSize ElementSize);
@@ -1098,8 +1099,8 @@ public:
void AVX128_VFMAScalarImpl(OpcodeArgs, IROps IROp, uint8_t Src1Idx, uint8_t Src2Idx, uint8_t AddendIdx);
void AVX128_VFMAddSubImpl(OpcodeArgs, bool AddSub, uint8_t Src1Idx, uint8_t Src2Idx, uint8_t AddendIdx);
RefPair AVX128_VPGatherQPSImpl(Ref Dest, Ref Mask, RefVSIB VSIB);
RefPair AVX128_VPGatherImpl(OpSize Size, OpSize ElementLoadSize, OpSize AddrElementSize, RefPair Dest, RefPair Mask, RefVSIB VSIB);
RefPair AVX128_VPGatherQPSImpl(OpcodeArgs, Ref Dest, Ref Mask, RefVSIB VSIB);
RefPair AVX128_VPGatherImpl(OpcodeArgs, OpSize Size, OpSize ElementLoadSize, OpSize AddrElementSize, RefPair Dest, RefPair Mask, RefVSIB VSIB);
void AVX128_VPGATHER(OpcodeArgs, OpSize AddrElementSize);
@@ -1109,8 +1110,8 @@ public:
// End of AVX 128-bit implementation
// AVX 256-bit operations
void StoreResult_WithAVXInsert(VectorOpType Type, FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, Ref Value,
IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
void StoreResult_WithAVXInsert(VectorOpType Type, RegClass Class, FEXCore::X86Tables::DecodedOp Op, Ref Value,
IR::OpSize Align = IR::OpSize::iInvalid, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
if (Op->Dest.IsGPR() && Op->Dest.Data.GPR.GPR >= X86State::REG_XMM_0 && Op->Dest.Data.GPR.GPR <= X86State::REG_XMM_15 &&
GetGuestVectorLength() == OpSize::i256Bit && Type == VectorOpType::SSE) {
const auto gpr = Op->Dest.Data.GPR.GPR;
@@ -1161,7 +1162,7 @@ public:
}
}
void StoreContextHelper(IR::OpSize Size, RegisterClassType Class, Ref Value, uint32_t Offset) {
void StoreContextHelper(IR::OpSize Size, RegClass Class, Ref Value, uint32_t Offset) {
// For i128Bit, we won't see a normal Constant to inline, but as a special
// case we can replace with a 2x64-bit store which can use inline zeroes.
if (Size == OpSize::i128Bit) {
@@ -1173,7 +1174,7 @@ public:
if (Const->Constant == IR::NamedVectorConstant::NAMED_VECTOR_ZERO) {
Ref Zero = _Constant(0);
Ref STP = _StoreContextPair(IR::OpSize::i64Bit, GPRClass, Zero, Zero, Offset);
Ref STP = _StoreContextPair(IR::OpSize::i64Bit, RegClass::GPR, Zero, Zero, Offset);
// XXX: This works around InlineConstant not having an associated
// register class, else we'd just do InlineConstant above.
@@ -1229,16 +1230,16 @@ public:
if (Index >= GPR0Index && Index <= GPR15Index) {
Ref R = _StoreRegister(Value, GPRSize);
R->Reg = PhysicalRegister(GPRFixedClass, Index - GPR0Index).Raw;
R->Reg = PhysicalRegister(RegClass::GPRFixed, Index - GPR0Index).Raw;
} else if (Index == PFIndex) {
_StorePF(Value, GPRSize);
} else if (Index == AFIndex) {
_StoreAF(Value, GPRSize);
} else if (Index >= FPR0Index && Index <= FPR15Index) {
Ref R = _StoreRegister(Value, VectorSize);
R->Reg = PhysicalRegister(FPRFixedClass, Index - FPR0Index).Raw;
R->Reg = PhysicalRegister(RegClass::FPRFixed, Index - FPR0Index).Raw;
} else if (Index == DFIndex) {
_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(Core::CPUState, flags[X86State::RFLAG_DF_RAW_LOC]));
_StoreContextGPR(OpSize::i8Bit, Value, offsetof(Core::CPUState, flags[X86State::RFLAG_DF_RAW_LOC]));
} else {
bool Partial = RegCache.Partial & (1ull << Index);
auto Size = Partial ? OpSize::i64Bit : CacheIndexToOpSize(Index);
@@ -1263,7 +1264,7 @@ public:
StoreContextHelper(Size, Class, Value, Offset);
// If Partial and MMX register, then we need to store all 1s in bits 64-80
if (Partial && Index >= MM0Index && Index <= MM7Index) {
_StoreContext(OpSize::i16Bit, IR::GPRClass, Constant(0xFFFF), Offset + 8);
_StoreContextGPR(OpSize::i16Bit, Constant(0xFFFF), Offset + 8);
}
}
}
@@ -1329,6 +1330,7 @@ protected:
private:
FEX_CONFIG_OPT(ReducedPrecisionMode, X87REDUCEDPRECISION);
FEX_CONFIG_OPT(StrictReducedPrecisionMode, X87STRICTREDUCEDPRECISION);
struct JumpTargetInfo {
Ref BlockEntry;
@@ -1553,23 +1555,63 @@ private:
AddressMode DecodeAddress(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, MemoryAccessType AccessType, bool IsLoad);
Ref LoadSource(RegisterClassType Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
Ref LoadSource(RegClass Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
const LoadSourceOptions& Options = {});
Ref LoadSource_WithOpSize(RegisterClassType Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand,
IR::OpSize OpSize, uint32_t Flags, const LoadSourceOptions& Options = {});
void StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op,
const FEXCore::X86Tables::DecodedOperand& Operand, const Ref Src, IR::OpSize OpSize, IR::OpSize Align,
MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
void StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, const FEXCore::X86Tables::DecodedOperand& Operand,
const Ref Src, IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
void StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, const Ref Src, IR::OpSize Align,
Ref LoadSourceGPR(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
const LoadSourceOptions& Options = {}) {
return LoadSource(RegClass::GPR, Op, Operand, Flags, Options);
}
Ref LoadSourceFPR(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, uint32_t Flags,
const LoadSourceOptions& Options = {}) {
return LoadSource(RegClass::FPR, Op, Operand, Flags, Options);
}
Ref LoadSource_WithOpSize(RegClass Class, const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize,
uint32_t Flags, const LoadSourceOptions& Options = {});
Ref LoadSourceGPR_WithOpSize(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize, uint32_t Flags,
const LoadSourceOptions& Options = {}) {
return LoadSource_WithOpSize(RegClass::GPR, Op, Operand, OpSize, Flags, Options);
}
Ref LoadSourceFPR_WithOpSize(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize, uint32_t Flags,
const LoadSourceOptions& Options = {}) {
return LoadSource_WithOpSize(RegClass::FPR, Op, Operand, OpSize, Flags, Options);
}
void StoreResult_WithOpSize(RegClass Class, X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, IR::OpSize OpSize,
IR::OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
void StoreResultGPR_WithOpSize(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, IR::OpSize OpSize,
IR::OpSize Align = IR::OpSize::iInvalid, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
StoreResult_WithOpSize(RegClass::GPR, Op, Operand, Src, OpSize, Align, AccessType);
}
void StoreResultFPR_WithOpSize(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, IR::OpSize OpSize,
IR::OpSize Align = IR::OpSize::iInvalid, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
StoreResult_WithOpSize(RegClass::FPR, Op, Operand, Src, OpSize, Align, AccessType);
}
void StoreResult(RegClass Class, X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, OpSize Align,
MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
void StoreResultGPR(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, OpSize Align = OpSize::iInvalid,
MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
StoreResult(RegClass::GPR, Op, Operand, Src, Align, AccessType);
}
void StoreResultFPR(X86Tables::DecodedOp Op, const X86Tables::DecodedOperand& Operand, Ref Src, OpSize Align = OpSize::iInvalid,
MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
StoreResult(RegClass::FPR, Op, Operand, Src, Align, AccessType);
}
void StoreResult(RegClass Class, X86Tables::DecodedOp Op, Ref Src, OpSize Align, MemoryAccessType AccessType = MemoryAccessType::DEFAULT);
void StoreResultGPR(X86Tables::DecodedOp Op, Ref Src, OpSize Align = OpSize::iInvalid, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
StoreResult(RegClass::GPR, Op, Src, Align, AccessType);
}
void StoreResultFPR(X86Tables::DecodedOp Op, Ref Src, OpSize Align = OpSize::iInvalid, MemoryAccessType AccessType = MemoryAccessType::DEFAULT) {
StoreResult(RegClass::FPR, Op, Src, Align, AccessType);
}
// In several instances, it's desirable to get a base address with the segment offset
// applied to it. This pulls all the common-case appending into a single set of functions.
[[nodiscard]]
Ref MakeSegmentAddress(const X86Tables::DecodedOp& Op, const X86Tables::DecodedOperand& Operand, IR::OpSize OpSize) {
Ref Mem = LoadSource_WithOpSize(GPRClass, Op, Operand, OpSize, Op->Flags, {.LoadData = false});
Ref Mem = LoadSourceGPR_WithOpSize(Op, Operand, OpSize, Op->Flags, {.LoadData = false});
return AppendSegmentOffset(Mem, Op->Flags);
}
[[nodiscard]]
@@ -1805,14 +1847,15 @@ private:
// For DF, we need to transform 0/1 into 1/-1
StoreDF(_SubShift(OpSize::i64Bit, Constant(1), Value, ShiftType::LSL, 1));
} else if (BitOffset == FEXCore::X86State::RFLAG_TF_RAW_LOC) {
auto PackedTF = _LoadContext(OpSize::i8Bit, GPRClass, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
auto PackedTF = _LoadContextGPR(OpSize::i8Bit, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
// An exception should still be raised after an instruction that unsets TF, leave the unblocked bit set but unset
// the TF bit to cause such behaviour. The handling code at the start of the next block will then unset the
// unblocked bit before raising the exception.
auto NewPackedTF = _Select(FEXCore::IR::COND_EQ, Value, Constant(0), _And(OpSize::i32Bit, PackedTF, Constant(~1)), Constant(1));
_StoreContext(OpSize::i8Bit, GPRClass, NewPackedTF, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
auto NewPackedTF =
_Select(OpSize::i64Bit, OpSize::i64Bit, CondClass::EQ, Value, Constant(0), _And(OpSize::i32Bit, PackedTF, Constant(~1)), Constant(1));
_StoreContextGPR(OpSize::i8Bit, NewPackedTF, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
} else {
_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
_StoreContextGPR(OpSize::i8Bit, Value, offsetof(FEXCore::Core::CPUState, flags[BitOffset]));
}
}
@@ -1838,12 +1881,12 @@ private:
}
[[nodiscard]]
static CondClassType CondForNZCVBit(unsigned BitOffset, bool Invert) {
static CondClass CondForNZCVBit(unsigned BitOffset, bool Invert) {
switch (BitOffset) {
case X86State::RFLAG_SF_RAW_LOC: return {Invert ? COND_PL : COND_MI};
case X86State::RFLAG_ZF_RAW_LOC: return {Invert ? COND_NEQ : COND_EQ};
case X86State::RFLAG_CF_RAW_LOC: return {Invert ? COND_ULT : COND_UGE};
case X86State::RFLAG_OF_RAW_LOC: return {Invert ? COND_FNU : COND_FU};
case X86State::RFLAG_SF_RAW_LOC: return Invert ? CondClass::PL : CondClass::MI;
case X86State::RFLAG_ZF_RAW_LOC: return Invert ? CondClass::NEQ : CondClass::EQ;
case X86State::RFLAG_CF_RAW_LOC: return Invert ? CondClass::ULT : CondClass::UGE;
case X86State::RFLAG_OF_RAW_LOC: return Invert ? CondClass::FNU : CondClass::FU;
default: FEX_UNREACHABLE;
}
}
@@ -1874,11 +1917,11 @@ private:
}
[[nodiscard]]
static RegisterClassType CacheIndexClass(int Index) {
static RegClass CacheIndexClass(int Index) {
if ((Index >= MM0Index && Index <= MM7Index) || Index >= FPR0Index) {
return FPRClass;
return RegClass::FPR;
} else {
return GPRClass;
return RegClass::GPR;
}
}
@@ -1910,14 +1953,14 @@ private:
RegCache.Written &= ~Bit;
}
Ref LoadRegCache(uint64_t Offset, uint8_t Index, RegisterClassType RegClass, IR::OpSize Size) {
Ref LoadRegCache(uint64_t Offset, uint8_t Index, RegClass Class, IR::OpSize Size) {
LOGMAN_THROW_A_FMT(Index < 64, "valid index");
uint64_t Bit = (1ull << (uint64_t)Index);
if (Size == OpSize::i128Bit && (RegCache.Partial & Bit)) {
// We need to load the full register extend if we previously did a partial access.
Ref Value = RegCache.Value[Index];
Ref Full = _LoadContext(Size, RegClass, Offset);
Ref Full = _LoadContext(Size, Class, Offset);
// If we did a partial store, we're inserting into the full register
if (RegCache.Written & Bit) {
@@ -1931,7 +1974,7 @@ private:
if (Index == DFIndex) {
RegCache.Value[Index] = _LoadDF();
} else if ((Index >= MM0Index && Index <= MM7Index) || Index >= AVXHigh0Index) {
RegCache.Value[Index] = _LoadContext(Size, RegClass, Offset);
RegCache.Value[Index] = _LoadContext(Size, Class, Offset);
// We may have done a partial load, this requires special handling.
if (Size == OpSize::i64Bit) {
@@ -1942,7 +1985,7 @@ private:
} else if (Index == AFIndex) {
RegCache.Value[Index] = _LoadAF(Size);
} else {
RegCache.Value[Index] = _LoadRegister(Offset, RegClass, Size);
RegCache.Value[Index] = _LoadRegister(Offset, Class, Size);
}
RegCache.Cached |= Bit;
@@ -1951,21 +1994,21 @@ private:
return RegCache.Value[Index];
}
RefPair AllocatePair(FEXCore::IR::RegisterClassType Class, IR::OpSize Size) {
if (Class == FPRClass) {
RefPair AllocatePair(RegClass Class, IR::OpSize Size) {
if (Class == RegClass::FPR) {
return {_AllocateFPR(Size, Size), _AllocateFPR(Size, Size)};
} else {
return {_AllocateGPR(false), _AllocateGPR(false)};
}
}
RefPair LoadContextPair_Uncached(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, unsigned Offset) {
RefPair LoadContextPair_Uncached(RegClass Class, IR::OpSize Size, unsigned Offset) {
RefPair Values = AllocatePair(Class, Size);
_LoadContextPair(Size, Class, Offset, Values.Low, Values.High);
return Values;
}
RefPair LoadRegCachePair(uint64_t Offset, uint8_t Index, RegisterClassType RegClass, IR::OpSize Size) {
RefPair LoadRegCachePair(uint64_t Offset, uint8_t Index, RegClass Class, IR::OpSize Size) {
LOGMAN_THROW_A_FMT(Index != DFIndex, "must be pairable");
LOGMAN_THROW_A_FMT(Size != IR::OpSize::iUnsized, "Invalid size!");
@@ -1973,7 +2016,7 @@ private:
uint64_t Bits = (3ull << (uint64_t)Index);
const auto SizeInt = IR::OpSizeToSize(Size);
if (((RegCache.Partial | RegCache.Cached) & Bits) == 0 && ((Offset / SizeInt) < 64)) {
auto Values = LoadContextPair_Uncached(RegClass, Size, Offset);
auto Values = LoadContextPair_Uncached(Class, Size, Offset);
RegCache.Value[Index] = Values.Low;
RegCache.Value[Index + 1] = Values.High;
RegCache.Cached |= Bits;
@@ -1985,13 +2028,13 @@ private:
// Fallback on a pair of loads
return {
.Low = LoadRegCache(Offset, Index, RegClass, Size),
.High = LoadRegCache(Offset + SizeInt, Index + 1, RegClass, Size),
.Low = LoadRegCache(Offset, Index, Class, Size),
.High = LoadRegCache(Offset + SizeInt, Index + 1, Class, Size),
};
}
Ref LoadGPR(uint8_t Reg) {
return LoadRegCache(Reg, GPR0Index + Reg, GPRClass, GetGPROpSize());
return LoadRegCache(Reg, GPR0Index + Reg, RegClass::GPR, GetGPROpSize());
}
Ref LoadContext(IR::OpSize Size, uint8_t Index) {
@@ -2007,7 +2050,7 @@ private:
}
Ref LoadXMMRegister(uint8_t Reg) {
return LoadRegCache(Reg, FPR0Index + Reg, FPRClass, GetGuestVectorLength());
return LoadRegCache(Reg, FPR0Index + Reg, RegClass::FPR, GetGuestVectorLength());
}
Ref LoadDF() {
@@ -2062,7 +2105,7 @@ private:
// Recover the sign bit, it is the logical DF value
return _Lshr(OpSize::i64Bit, LoadDF(), Constant(63));
} else {
return _LoadContext(OpSize::i8Bit, GPRClass, offsetof(Core::CPUState, flags[BitOffset]));
return _LoadContextGPR(OpSize::i8Bit, offsetof(Core::CPUState, flags[BitOffset]));
}
}
@@ -2079,18 +2122,18 @@ private:
}
// Safe version of NZCVSelect that handles inverted carries automatically.
Ref NZCVSelect(OpSize OpSize, CondClassType Cond, Ref TrueV, Ref FalseV, bool CarryIsInverted = false) {
Ref NZCVSelect(OpSize OpSize, CondClass Cond, Ref TrueV, Ref FalseV, bool CarryIsInverted = false) {
switch (Cond) {
case IR::COND_UGE: /* cs */
case IR::COND_ULT: /* cc */
case CondClass::UGE: /* cs */
case CondClass::ULT: /* cc */
// Invert the condition to match our expectations.
if (CarryIsInverted != CFInverted) {
Cond = {Cond == COND_UGE ? COND_ULT : COND_UGE};
Cond = (Cond == CondClass::UGE) ? CondClass::ULT : CondClass::UGE;
}
break;
case IR::COND_UGT: /* hi */
case IR::COND_ULE: /* ls */
case CondClass::UGT: /* hi */
case CondClass::ULE: /* ls */
// No clever optimization we can do here, rectify carry itself.
RectifyCarryInvert(CarryIsInverted);
break;
@@ -2179,7 +2222,7 @@ private:
HandleNZCV_RMW();
CalculatePF(_ShiftFlags(OpSizeFromSrc(Op), Result, Dest, Shift, Src, OldPF, CFInverted));
StoreResult(GPRClass, Op, Result, OpSize::iInvalid);
StoreResultGPR(Op, Result);
}
// Helper to derive Dest by a given builder-using Expression with the opcode
@@ -2252,8 +2295,7 @@ private:
CachedIndexedNamedVectorConstants.clear();
}
std::optional<CondClassType> DecodeNZCVCondition(uint8_t OP);
Ref SelectBit(Ref Cmp, IR::OpSize ResultSize, Ref TrueValue, Ref FalseValue);
std::optional<CondClass> DecodeNZCVCondition(uint8_t OP);
Ref SelectCC0All1(uint8_t OP);
/**
@@ -2267,8 +2309,8 @@ private:
if (Size != OpSize::i32Bit) {
return;
}
auto Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
StoreResult(GPRClass, Op, Dest, OpSize::iInvalid);
auto Dest = LoadSourceGPR(Op, Op->Dest, Op->Flags);
StoreResultGPR(Op, Dest);
}
using ZeroShiftFunctionPtr = void (OpDispatchBuilder::*)(FEXCore::X86Tables::DecodedOp Op);
@@ -2303,7 +2345,7 @@ private:
///< Jump to zeroshift block or end block depending on if it was provided.
IRPair<IROp_CodeBlock> TailHandling = ZeroShiftResult ? ZeroShiftBlock : EndBlock;
CondJump(Shift, Zero, TailHandling, SetBlock, {COND_EQ});
CondJump(Shift, Zero, TailHandling, SetBlock, CondClass::EQ);
SetCurrentCodeBlock(SetBlock);
StartNewBlock();
@@ -2346,9 +2388,7 @@ private:
void CalculateFlags_MUL(IR::OpSize SrcSize, Ref Res, Ref High);
void CalculateFlags_UMUL(Ref High);
void CalculateFlags_Logical(IR::OpSize SrcSize, Ref Res);
void CalculateFlags_ShiftLeft(IR::OpSize SrcSize, Ref Res, Ref Src1, Ref Src2);
void CalculateFlags_ShiftLeftImmediate(IR::OpSize SrcSize, Ref Res, Ref Src1, uint64_t Shift);
void CalculateFlags_ShiftRight(IR::OpSize SrcSize, Ref Res, Ref Src1, Ref Src2);
void CalculateFlags_ShiftRightImmediate(IR::OpSize SrcSize, Ref Res, Ref Src1, uint64_t Shift);
void CalculateFlags_ShiftRightDoubleImmediate(IR::OpSize SrcSize, Ref Res, Ref Src1, uint64_t Shift);
void CalculateFlags_ShiftRightImmediateCommon(IR::OpSize SrcSize, Ref Res, Ref Src1, uint64_t Shift);
@@ -2365,7 +2405,7 @@ private:
LOGMAN_THROW_A_FMT(MMXState == MMXState_X87, "Expected state to be x87");
_StackForceSlow();
SetX87Top(Constant(0)); // top reset to zero
_StoreContext(OpSize::i8Bit, GPRClass, Constant(0xFFFFUL), offsetof(FEXCore::Core::CPUState, AbridgedFTW));
_StoreContextGPR(OpSize::i8Bit, Constant(0xFFFFUL), offsetof(FEXCore::Core::CPUState, AbridgedFTW));
MMXState = MMXState_MMX;
}
@@ -2402,44 +2442,62 @@ private:
IROp_IRHeader* CurrentHeader {};
[[nodiscard]]
bool IsTSOEnabled(FEXCore::IR::RegisterClassType Class) const {
bool IsTSOEnabled(RegClass Class) const {
if (ForceTSO == ForceTSOMode::ForceEnabled) {
return true;
} else if (ForceTSO == ForceTSOMode::ForceDisabled) {
return false;
} else if (Class == FPRClass) {
} else if (Class == RegClass::FPR) {
return CTX->IsVectorAtomicTSOEnabled();
} else {
return CTX->IsAtomicTSOEnabled();
}
}
Ref _StoreMemAutoTSO(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, Ref Addr, Ref Value, IR::OpSize Align = IR::OpSize::i8Bit) {
Ref _StoreMemAutoTSO(RegClass Class, OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
if (IsTSOEnabled(Class)) {
return _StoreMemTSO(Class, Size, Value, Addr, Invalid(), Align, MEM_OFFSET_SXTX, 1);
return _StoreMemTSO(Class, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
} else {
return _StoreMem(Class, Size, Value, Addr, Invalid(), Align, MEM_OFFSET_SXTX, 1);
return _StoreMem(Class, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
}
}
Ref _LoadMemAutoTSO(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, Ref ssa0, IR::OpSize Align = IR::OpSize::i8Bit) {
if (IsTSOEnabled(Class)) {
return _LoadMemTSO(Class, Size, ssa0, Invalid(), Align, MEM_OFFSET_SXTX, 1);
} else {
return _LoadMem(Class, Size, ssa0, Invalid(), Align, MEM_OFFSET_SXTX, 1);
}
Ref _StoreMemGPRAutoTSO(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
return _StoreMemAutoTSO(RegClass::GPR, Size, Addr, Value, Align);
}
Ref _StoreMemFPRAutoTSO(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
return _StoreMemAutoTSO(RegClass::FPR, Size, Addr, Value, Align);
}
Ref _LoadMemAutoTSO(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, AddressMode A, IR::OpSize Align = IR::OpSize::i8Bit) {
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
A = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != GPRClass, Size);
Ref _LoadMemAutoTSO(RegClass Class, OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
if (IsTSOEnabled(Class)) {
return _LoadMemTSO(Class, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
} else {
return _LoadMem(Class, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
}
}
Ref _LoadMemGPRAutoTSO(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
return _LoadMemAutoTSO(RegClass::GPR, Size, ssa0, Align);
}
Ref _LoadMemFPRAutoTSO(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
return _LoadMemAutoTSO(RegClass::FPR, Size, ssa0, Align);
}
Ref _LoadMemAutoTSO(RegClass Class, OpSize Size, const AddressMode& A, OpSize Align = OpSize::i8Bit) {
const bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
const auto B = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != RegClass::GPR, Size);
if (AtomicTSO) {
return _LoadMemTSO(Class, Size, A.Base, A.Index, Align, A.IndexType, A.IndexScale);
return _LoadMemTSO(Class, Size, B.Base, B.Index, Align, B.IndexType, B.IndexScale);
} else {
return _LoadMem(Class, Size, A.Base, A.Index, Align, A.IndexType, A.IndexScale);
return _LoadMem(Class, Size, B.Base, B.Index, Align, B.IndexType, B.IndexScale);
}
}
Ref _LoadMemGPRAutoTSO(OpSize Size, const AddressMode& A, OpSize Align = OpSize::i8Bit) {
return _LoadMemAutoTSO(RegClass::GPR, Size, A, Align);
}
Ref _LoadMemFPRAutoTSO(OpSize Size, const AddressMode& A, OpSize Align = OpSize::i8Bit) {
return _LoadMemAutoTSO(RegClass::FPR, Size, A, Align);
}
AddressMode SelectPairAddressMode(AddressMode A, IR::OpSize Size) {
LOGMAN_THROW_A_FMT(Size != IR::OpSize::iUnsized, "Invalid size!");
@@ -2457,56 +2515,72 @@ private:
}
RefPair LoadMemPair(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, Ref Base, unsigned Offset) {
RefPair LoadMemPair(RegClass Class, OpSize Size, Ref Base, uint32_t Offset) {
RefPair Values = AllocatePair(Class, Size);
_LoadMemPair(Class, Size, Base, Offset, Values.Low, Values.High);
return Values;
}
RefPair LoadMemPairFPR(OpSize Size, Ref Base, uint32_t Offset) {
return LoadMemPair(RegClass::FPR, Size, Base, Offset);
}
RefPair _LoadMemPairAutoTSO(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, AddressMode A, IR::OpSize Align = IR::OpSize::i8Bit) {
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
RefPair _LoadMemPairAutoTSO(RegClass Class, OpSize Size, const AddressMode& A, OpSize Align = OpSize::i8Bit) {
const bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
// Use ldp if possible, otherwise fallback on two loads.
if (!AtomicTSO && !A.Segment && Size >= OpSize::i32Bit & Size <= OpSize::i128Bit) {
A = SelectPairAddressMode(A, Size);
return LoadMemPair(Class, Size, A.Base, A.Offset);
} else {
AddressMode HighA = A;
HighA.Offset += 16;
return {
.Low = _LoadMemAutoTSO(Class, Size, A, Align),
.High = _LoadMemAutoTSO(Class, Size, HighA, Align),
};
const auto B = SelectPairAddressMode(A, Size);
return LoadMemPair(Class, Size, B.Base, B.Offset);
}
AddressMode HighA = A;
HighA.Offset += 16;
return {
.Low = _LoadMemAutoTSO(Class, Size, A, Align),
.High = _LoadMemAutoTSO(Class, Size, HighA, Align),
};
}
RefPair _LoadMemPairFPRAutoTSO(OpSize Size, const AddressMode& A, OpSize Align = OpSize::i8Bit) {
return _LoadMemPairAutoTSO(RegClass::FPR, Size, A, Align);
}
Ref _StoreMemAutoTSO(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, AddressMode A, Ref Value, IR::OpSize Align = IR::OpSize::i8Bit) {
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
A = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != GPRClass, Size);
Ref _StoreMemAutoTSO(RegClass Class, OpSize Size, const AddressMode& A, Ref Value, OpSize Align = OpSize::i8Bit) {
const bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
const auto B = SelectAddressMode(this, A, GetGPROpSize(), CTX->HostFeatures.SupportsTSOImm9, AtomicTSO, Class != RegClass::GPR, Size);
if (AtomicTSO) {
return _StoreMemTSO(Class, Size, Value, A.Base, A.Index, Align, A.IndexType, A.IndexScale);
return _StoreMemTSO(Class, Size, Value, B.Base, B.Index, Align, B.IndexType, B.IndexScale);
} else {
return _StoreMem(Class, Size, Value, A.Base, A.Index, Align, A.IndexType, A.IndexScale);
return _StoreMem(Class, Size, Value, B.Base, B.Index, Align, B.IndexType, B.IndexScale);
}
}
Ref _StoreMemGPRAutoTSO(OpSize Size, const AddressMode& A, Ref Value, OpSize Align = OpSize::i8Bit) {
return _StoreMemAutoTSO(RegClass::GPR, Size, A, Value, Align);
}
Ref _StoreMemFPRAutoTSO(OpSize Size, const AddressMode& A, Ref Value, OpSize Align = OpSize::i8Bit) {
return _StoreMemAutoTSO(RegClass::FPR, Size, A, Value, Align);
}
void _StoreMemPairAutoTSO(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, AddressMode A, Ref Value1, Ref Value2,
IR::OpSize Align = IR::OpSize::i8Bit) {
void _StoreMemPairAutoTSO(RegClass Class, OpSize Size, const AddressMode& A, Ref Value1, Ref Value2, OpSize Align = OpSize::i8Bit) {
const auto SizeInt = IR::OpSizeToSize(Size);
bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
const bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
// Use stp if possible, otherwise fallback on two stores.
if (!AtomicTSO && !A.Segment && Size >= OpSize::i32Bit & Size <= OpSize::i128Bit) {
A = SelectPairAddressMode(A, Size);
_StoreMemPair(Class, Size, Value1, Value2, A.Base, A.Offset);
const auto B = SelectPairAddressMode(A, Size);
_StoreMemPair(Class, Size, Value1, Value2, B.Base, B.Offset);
} else {
_StoreMemAutoTSO(Class, Size, A, Value1, OpSize::i8Bit);
A.Offset += SizeInt;
_StoreMemAutoTSO(Class, Size, A, Value2, OpSize::i8Bit);
auto B = A;
_StoreMemAutoTSO(Class, Size, B, Value1, OpSize::i8Bit);
B.Offset += SizeInt;
_StoreMemAutoTSO(Class, Size, B, Value2, OpSize::i8Bit);
}
}
void _StoreMemPairFPRAutoTSO(OpSize Size, const AddressMode& A, Ref Value1, Ref Value2, OpSize Align = OpSize::i8Bit) {
return _StoreMemPairAutoTSO(RegClass::FPR, Size, A, Value1, Value2, Align);
}
Ref Pop(IR::OpSize Size, Ref SP_RMW) {
Ref Value = _AllocateGPR(false);
@@ -35,20 +35,16 @@ OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_LoadSource_WithOpSize(
} else {
LOGMAN_THROW_A_FMT(IsOperandMem(Operand, true), "only memory sources");
AddressMode A = DecodeAddress(Op, Operand, AccessType, true /* IsLoad */);
AddressMode HighA = A;
HighA.Offset += 16;
if (Operand.IsSIB()) {
const bool IsVSIB = (Op->Flags & X86Tables::DecodeFlags::FLAG_VSIB_BYTE) != 0;
LOGMAN_THROW_A_FMT(!IsVSIB, "VSIB uses LoadVSIB instead");
}
const AddressMode A = DecodeAddress(Op, Operand, AccessType, true /* IsLoad */);
if (NeedsHigh) {
return _LoadMemPairAutoTSO(FPRClass, OpSize::i128Bit, A, OpSize::i8Bit);
return _LoadMemPairFPRAutoTSO(OpSize::i128Bit, A, OpSize::i8Bit);
} else {
return {.Low = _LoadMemAutoTSO(FPRClass, OpSize::i128Bit, A, OpSize::i8Bit)};
return {.Low = _LoadMemFPRAutoTSO(OpSize::i128Bit, A, OpSize::i8Bit)};
}
}
}
@@ -95,9 +91,9 @@ void OpDispatchBuilder::AVX128_StoreResult_WithOpSize(FEXCore::X86Tables::Decode
AddressMode A = DecodeAddress(Op, Operand, AccessType, false /* IsLoad */);
if (Src.High) {
_StoreMemPairAutoTSO(FPRClass, OpSize::i128Bit, A, Src.Low, Src.High, OpSize::i8Bit);
_StoreMemPairFPRAutoTSO(OpSize::i128Bit, A, Src.Low, Src.High, OpSize::i8Bit);
} else {
_StoreMemAutoTSO(FPRClass, OpSize::i128Bit, A, Src.Low, OpSize::i8Bit);
_StoreMemFPRAutoTSO(OpSize::i128Bit, A, Src.Low, OpSize::i8Bit);
}
}
}
@@ -151,13 +147,13 @@ void OpDispatchBuilder::AVX128_VMOVScalarImpl(OpcodeArgs, IR::OpSize ElementSize
AVX128_StoreResult_WithOpSize(Op, Op->Dest, RefPair {.Low = Result, .High = High});
} else if (Op->Dest.IsGPR()) {
// VMOVSS/SD xmm1, mem32/mem64
Ref Src = LoadSource_WithOpSize(FPRClass, Op, Op->Src[1], ElementSize, Op->Flags);
Ref Src = LoadSourceFPR_WithOpSize(Op, Op->Src[1], ElementSize, Op->Flags);
auto High = LoadZeroVector(OpSize::i128Bit);
AVX128_StoreResult_WithOpSize(Op, Op->Dest, RefPair {.Low = Src, .High = High});
} else {
// VMOVSS/SD mem32/mem64, xmm1
auto Src = AVX128_LoadSource_WithOpSize(Op, Op->Src[1], Op->Flags, false);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Src.Low, ElementSize, OpSize::iInvalid);
StoreResultFPR_WithOpSize(Op, Op->Dest, Src.Low, ElementSize);
}
}
@@ -351,7 +347,7 @@ void OpDispatchBuilder::AVX128_MOVVectorNT(OpcodeArgs) {
if (Op->Dest.IsGPR()) {
///< MOVNTDQA load non-temporal comes from SSE4.1 and is extended by AVX/AVX2.
RefPair Src {};
Ref SrcAddr = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, {.LoadData = false});
Ref SrcAddr = LoadSourceGPR(Op, Op->Src[0], Op->Flags, {.LoadData = false});
Src.Low = _VLoadNonTemporal(OpSize::i128Bit, SrcAddr, 0);
if (Is128Bit) {
@@ -362,7 +358,7 @@ void OpDispatchBuilder::AVX128_MOVVectorNT(OpcodeArgs) {
AVX128_StoreResult_WithOpSize(Op, Op->Dest, Src);
} else {
auto Src = AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, !Is128Bit, MemoryAccessType::STREAM);
Ref Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
Ref Dest = LoadSourceGPR(Op, Op->Dest, Op->Flags, {.LoadData = false});
if (Is128Bit) {
// Single store non-temporal for 128-bit operations.
@@ -379,7 +375,7 @@ void OpDispatchBuilder::AVX128_MOVQ(OpcodeArgs) {
if (Op->Src[0].IsGPR()) {
Src = AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, false);
} else {
Src.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], OpSize::i64Bit, Op->Flags);
Src.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[0], OpSize::i64Bit, Op->Flags);
}
// This instruction is a bit special that if the destination is a register then it'll ZEXT the 64bit source to 256bit
@@ -390,7 +386,7 @@ void OpDispatchBuilder::AVX128_MOVQ(OpcodeArgs) {
Src.High = ZeroVector;
AVX128_StoreResult_WithOpSize(Op, Op->Dest, Src);
} else {
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Src.Low, OpSize::i64Bit, OpSize::i64Bit);
StoreResultFPR_WithOpSize(Op, Op->Dest, Src.Low, OpSize::i64Bit, OpSize::i64Bit);
}
}
@@ -399,7 +395,7 @@ void OpDispatchBuilder::AVX128_VMOVLP(OpcodeArgs) {
if (!Op->Dest.IsGPR()) {
///< VMOVLPS/PD mem64, xmm1
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Src1.Low, OpSize::i64Bit, OpSize::i64Bit);
StoreResultFPR_WithOpSize(Op, Op->Dest, Src1.Low, OpSize::i64Bit, OpSize::i64Bit);
} else if (!Op->Src[1].IsGPR()) {
///< VMOVLPS/PD xmm1, xmm2, mem64
// Bits[63:0] come from Src2[63:0]
@@ -463,7 +459,7 @@ void OpDispatchBuilder::AVX128_VMOVDDUP(OpcodeArgs) {
// 128-bit operation only loads 8-bytes.
// 256-bit operation loads a full 32-bytes.
if (Is128Bit) {
Src.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], OpSize::i64Bit, Op->Flags);
Src.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[0], OpSize::i64Bit, Op->Flags);
} else {
Src = AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, true);
}
@@ -558,18 +554,18 @@ void OpDispatchBuilder::AVX128_InsertCVTGPR_To_FPR(OpcodeArgs, IR::OpSize DstEle
if (Op->Src[1].IsGPR()) {
// If the source is a GPR then convert directly from the GPR.
auto Src2 = LoadSource_WithOpSize(GPRClass, Op, Op->Src[1], GetGPROpSize(), Op->Flags);
auto Src2 = LoadSourceGPR_WithOpSize(Op, Op->Src[1], GetGPROpSize(), Op->Flags);
Result.Low = _VSToFGPRInsert(OpSize::i128Bit, DstElementSize, SrcSize, Src1.Low, Src2, false);
} else if (SrcSize != DstElementSize) {
// If the source is from memory but the Source size and destination size aren't the same,
// then it is more optimal to load in to a GPR and convert between GPR->FPR.
// ARM GPR->FPR conversion supports different size source and destinations while FPR->FPR doesn't.
auto Src2 = LoadSource(GPRClass, Op, Op->Src[1], Op->Flags);
auto Src2 = LoadSourceGPR(Op, Op->Src[1], Op->Flags);
Result.Low = _VSToFGPRInsert(DstSize, DstElementSize, SrcSize, Src1.Low, Src2, false);
} else {
// In the case of cvtsi2s{s,d} where the source and destination are the same size,
// then it is more optimal to load in to the FPR register directly and convert there.
auto Src2 = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
auto Src2 = LoadSourceFPR(Op, Op->Src[1], Op->Flags);
// Always signed
Result.Low = _VSToFVectorInsert(DstSize, DstElementSize, DstElementSize, Src1.Low, Src2, false, false);
}
@@ -589,11 +585,11 @@ void OpDispatchBuilder::AVX128_CVTFPR_To_GPR(OpcodeArgs, IR::OpSize SrcElementSi
if (Op->Src[0].IsGPR()) {
Src = AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, false);
} else {
Src.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], SrcElementSize, Op->Flags);
Src.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[0], SrcElementSize, Op->Flags);
}
Ref Result = CVTFPR_To_GPRImpl(Op, Src.Low, SrcElementSize, HostRoundingMode);
StoreResult(GPRClass, Op, Result, OpSize::iInvalid);
StoreResultGPR(Op, Result);
}
void OpDispatchBuilder::AVX128_VANDN(OpcodeArgs) {
@@ -636,7 +632,7 @@ void OpDispatchBuilder::AVX128_UCOMISx(OpcodeArgs, IR::OpSize ElementSize) {
if (Op->Src[0].IsGPR()) {
Src2 = AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, false);
} else {
Src2.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], SrcSize, Op->Flags);
Src2.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[0], SrcSize, Op->Flags);
}
Comiss(ElementSize, Src1.Low, Src2.Low);
@@ -653,7 +649,7 @@ void OpDispatchBuilder::AVX128_VectorScalarInsertALU(OpcodeArgs, FEXCore::IR::IR
if (Op->Src[1].IsGPR()) {
Src2 = AVX128_LoadSource_WithOpSize(Op, Op->Src[1], Op->Flags, false);
} else {
Src2.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[1], SrcSize, Op->Flags);
Src2.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[1], SrcSize, Op->Flags);
}
// If OpSize == ElementSize then it only does the lower scalar op
@@ -690,7 +686,7 @@ void OpDispatchBuilder::AVX128_InsertScalarFCMP(OpcodeArgs, IR::OpSize ElementSi
if (Op->Src[1].IsGPR()) {
Src2 = AVX128_LoadSource_WithOpSize(Op, Op->Src[1], Op->Flags, false);
} else {
Src2.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[1], SrcSize, Op->Flags);
Src2.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[1], SrcSize, Op->Flags);
}
const uint8_t CompType = Op->Src[2].Literal();
@@ -708,12 +704,12 @@ void OpDispatchBuilder::AVX128_MOVBetweenGPR_FPR(OpcodeArgs) {
RefPair Result {};
if (Op->Src[0].IsGPR()) {
// Loading from GPR and moving to Vector.
Ref Src = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], GetGPROpSize(), Op->Flags);
Ref Src = LoadSourceFPR_WithOpSize(Op, Op->Src[0], GetGPROpSize(), Op->Flags);
// zext to 128bit
Result.Low = _VCastFromGPR(OpSize::i128Bit, OpSizeFromSrc(Op), Src);
} else {
// Loading from Memory as a scalar. Zero extend
Result.Low = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Result.Low = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
}
Result.High = LoadZeroVector(OpSize::i128Bit);
@@ -726,11 +722,11 @@ void OpDispatchBuilder::AVX128_MOVBetweenGPR_FPR(OpcodeArgs) {
auto ElementSize = OpSizeFromDst(Op);
// Extract element from GPR. Zero extending in the process.
Src.Low = _VExtractToGPR(OpSizeFromSrc(Op), ElementSize, Src.Low, 0);
StoreResult(GPRClass, Op, Op->Dest, Src.Low, OpSize::iInvalid);
StoreResultGPR(Op, Op->Dest, Src.Low);
} else {
// Storing first element to memory.
Ref Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
_StoreMem(FPRClass, OpSizeFromDst(Op), Dest, Src.Low, OpSize::i8Bit);
Ref Dest = LoadSourceGPR(Op, Op->Dest, Op->Flags, {.LoadData = false});
_StoreMemFPR(OpSizeFromDst(Op), Dest, Src.Low, OpSize::i8Bit);
}
}
}
@@ -758,7 +754,7 @@ void OpDispatchBuilder::AVX128_PExtr(OpcodeArgs, IR::OpSize ElementSize) {
const auto GPRSize = GetGPROpSize();
// Extract already zero extends the result.
Ref Result = _VExtractToGPR(OpSize::i128Bit, OverridenElementSize, Src.Low, Index);
StoreResult_WithOpSize(GPRClass, Op, Op->Dest, Result, GPRSize, OpSize::iInvalid);
StoreResultGPR_WithOpSize(Op, Op->Dest, Result, GPRSize);
return;
}
@@ -779,7 +775,7 @@ void OpDispatchBuilder::AVX128_ExtendVectorElements(OpcodeArgs, IR::OpSize Eleme
const auto SrcSize = OpSizeFromSrc(Op);
const auto LoadSize = Is256Bit ? IR::SizeToOpSize(IR::OpSizeToSize(SrcSize) * 2) : SrcSize;
return LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], LoadSize, Op->Flags);
return LoadSourceFPR_WithOpSize(Op, Op->Src[0], LoadSize, Op->Flags);
}
};
@@ -868,7 +864,7 @@ void OpDispatchBuilder::AVX128_MOVMSK(OpcodeArgs, IR::OpSize ElementSize) {
auto GPRHigh = Mask8Byte(Src.High);
GPR = _Orlshl(OpSize::i64Bit, GPRLow, GPRHigh, 2);
}
StoreResult_WithOpSize(GPRClass, Op, Op->Dest, GPR, GetGPROpSize(), OpSize::iInvalid);
StoreResultGPR_WithOpSize(Op, Op->Dest, GPR, GetGPROpSize());
}
void OpDispatchBuilder::AVX128_MOVMSKB(OpcodeArgs) {
@@ -897,7 +893,7 @@ void OpDispatchBuilder::AVX128_MOVMSKB(OpcodeArgs) {
Result = _Orlshl(OpSize::i64Bit, Result, ResultHigh, 16);
}
StoreResult(GPRClass, Op, Result, OpSize::iInvalid);
StoreResultGPR(Op, Result);
}
void OpDispatchBuilder::AVX128_PINSRImpl(OpcodeArgs, IR::OpSize ElementSize, const X86Tables::DecodedOperand& Src1Op,
@@ -910,7 +906,7 @@ void OpDispatchBuilder::AVX128_PINSRImpl(OpcodeArgs, IR::OpSize ElementSize, con
if (Src2Op.IsGPR()) {
// If the source is a GPR then convert directly from the GPR.
auto Src2 = LoadSource_WithOpSize(GPRClass, Op, Src2Op, GetGPROpSize(), Op->Flags);
auto Src2 = LoadSourceGPR_WithOpSize(Op, Src2Op, GetGPROpSize(), Op->Flags);
Result.Low = _VInsGPR(OpSize::i128Bit, ElementSize, Index, Src1.Low, Src2);
} else {
// If loading from memory then we only load the element size
@@ -1047,7 +1043,7 @@ void OpDispatchBuilder::AVX128_InsertScalar_CVT_Float_To_Float(OpcodeArgs, IR::O
// Then zero extends the top 128-bit.
const auto SrcSize = Op->Src[1].IsGPR() ? OpSize::i128Bit : SrcElementSize;
auto Src1 = AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, false);
Ref Src2 = LoadSource_WithOpSize(FPRClass, Op, Op->Src[1], SrcSize, Op->Flags, {.AllowUpperGarbage = true});
Ref Src2 = LoadSourceFPR_WithOpSize(Op, Op->Src[1], SrcSize, Op->Flags, {.AllowUpperGarbage = true});
Ref Result = _VFToFScalarInsert(OpSize::i128Bit, DstElementSize, SrcElementSize, Src1.Low, Src2, false);
AVX128_StoreResult_WithOpSize(Op, Op->Dest, AVX128_Zext(Result));
@@ -1076,7 +1072,7 @@ void OpDispatchBuilder::AVX128_Vector_CVT_Float_To_Float(OpcodeArgs, IR::OpSize
} else {
// Handle 64-bit memory source.
// In the case of cvtps2pd xmm, m64.
Src.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], LoadSize, Op->Flags);
Src.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[0], LoadSize, Op->Flags);
}
RefPair Result {};
@@ -1154,7 +1150,7 @@ void OpDispatchBuilder::AVX128_Vector_CVT_Int_To_Float(OpcodeArgs, IR::OpSize Sr
// unnecessarily zero extend the vector. Otherwise, if
// memory, then we want to load the element size exactly.
const auto LoadSize = IR::SizeToOpSize(8 * (IR::OpSizeToSize(Size) / 16));
return RefPair {.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], LoadSize, Op->Flags)};
return RefPair {.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[0], LoadSize, Op->Flags)};
} else {
return AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, !Is128Bit);
}
@@ -1304,7 +1300,7 @@ void OpDispatchBuilder::AVX128_InsertScalarRound(OpcodeArgs, IR::OpSize ElementS
if (Op->Src[1].IsGPR()) {
Src2 = AVX128_LoadSource_WithOpSize(Op, Op->Src[1], Op->Flags, false);
} else {
Src2.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[1], SrcSize, Op->Flags);
Src2.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[1], SrcSize, Op->Flags);
}
// If OpSize == ElementSize then it only does the lower scalar op
@@ -1573,20 +1569,20 @@ void OpDispatchBuilder::AVX128_VMASKMOVImpl(OpcodeArgs, IR::OpSize ElementSize,
auto Address = MakeAddress(Op->Dest);
auto Data = AVX128_LoadSource_WithOpSize(Op, DataOp, Op->Flags, !Is128Bit);
_VStoreVectorMasked(OpSize::i128Bit, ElementSize, Mask.Low, Data.Low, Address, Invalid(), MEM_OFFSET_SXTX, 1);
_VStoreVectorMasked(OpSize::i128Bit, ElementSize, Mask.Low, Data.Low, Address, Invalid(), MemOffsetType::SXTX, 1);
if (!Is128Bit) {
_VStoreVectorMasked(OpSize::i128Bit, ElementSize, Mask.High, Data.High, Address, _InlineConstant(16), MEM_OFFSET_SXTX, 1);
_VStoreVectorMasked(OpSize::i128Bit, ElementSize, Mask.High, Data.High, Address, _InlineConstant(16), MemOffsetType::SXTX, 1);
}
} else {
auto Address = MakeAddress(DataOp);
RefPair Result {};
Result.Low = _VLoadVectorMasked(OpSize::i128Bit, ElementSize, Mask.Low, Address, Invalid(), MEM_OFFSET_SXTX, 1);
Result.Low = _VLoadVectorMasked(OpSize::i128Bit, ElementSize, Mask.Low, Address, Invalid(), MemOffsetType::SXTX, 1);
if (Is128Bit) {
Result.High = LoadZeroVector(OpSize::i128Bit);
} else {
Result.High = _VLoadVectorMasked(OpSize::i128Bit, ElementSize, Mask.High, Address, _InlineConstant(16), MEM_OFFSET_SXTX, 1);
Result.High = _VLoadVectorMasked(OpSize::i128Bit, ElementSize, Mask.High, Address, _InlineConstant(16), MemOffsetType::SXTX, 1);
}
AVX128_StoreResult_WithOpSize(Op, Op->Dest, Result);
}
@@ -1616,11 +1612,11 @@ void OpDispatchBuilder::AVX128_MASKMOV(OpcodeArgs) {
// RDI source (DS prefix by default)
auto MemDest = MakeSegmentAddress(X86State::REG_RDI, Op->Flags, X86Tables::DecodeFlags::FLAG_DS_PREFIX);
Ref XMMReg = _LoadMem(FPRClass, Size, MemDest, OpSize::i8Bit);
Ref XMMReg = _LoadMemFPR(Size, MemDest, OpSize::i8Bit);
// If the Mask element high bit is set then overwrite the element with the source, else keep the memory variant
XMMReg = _VBSL(Size, MaskSrc.Low, VectorSrc.Low, XMMReg);
_StoreMem(FPRClass, Size, MemDest, XMMReg, OpSize::i8Bit);
_StoreMemFPR(Size, MemDest, XMMReg, OpSize::i8Bit);
}
void OpDispatchBuilder::AVX128_VectorVariableBlend(OpcodeArgs, IR::OpSize ElementSize) {
@@ -1660,7 +1656,7 @@ void OpDispatchBuilder::AVX128_SaveAVXState(Ref MemBase) {
for (uint32_t i = 0; i < NumRegs; i += 2) {
RefPair Pair = LoadContextPair(OpSize::i128Bit, AVXHigh0Index + i);
_StoreMemPair(FPRClass, OpSize::i128Bit, Pair.Low, Pair.High, MemBase, i * 16 + 576);
_StoreMemPairFPR(OpSize::i128Bit, Pair.Low, Pair.High, MemBase, i * 16 + 576);
}
}
@@ -1668,7 +1664,7 @@ void OpDispatchBuilder::AVX128_RestoreAVXState(Ref MemBase) {
const auto NumRegs = Is64BitMode ? 16U : 8U;
for (uint32_t i = 0; i < NumRegs; i += 2) {
auto YMMHRegs = LoadMemPair(FPRClass, OpSize::i128Bit, MemBase, i * 16 + 576);
auto YMMHRegs = LoadMemPairFPR(OpSize::i128Bit, MemBase, i * 16 + 576);
AVX128_StoreXMMRegister(i, YMMHRegs.Low, true);
AVX128_StoreXMMRegister(i + 1, YMMHRegs.High, true);
@@ -1968,7 +1964,7 @@ void OpDispatchBuilder::AVX128_VFMAScalarImpl(OpcodeArgs, IROps IROp, uint8_t Sr
if (Op->Src[1].IsGPR()) {
Src2 = AVX128_LoadSource_WithOpSize(Op, Op->Src[1], Op->Flags, false).Low;
} else {
Src2 = LoadSource_WithOpSize(FPRClass, Op, Op->Src[1], SrcSize, Op->Flags);
Src2 = LoadSourceFPR_WithOpSize(Op, Op->Src[1], SrcSize, Op->Flags);
}
Ref Sources[3] = {Dest, Src1, Src2};
@@ -2016,8 +2012,8 @@ void OpDispatchBuilder::AVX128_VFMAddSubImpl(OpcodeArgs, bool AddSub, uint8_t Sr
AVX128_StoreResult_WithOpSize(Op, Op->Dest, Result);
}
OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherImpl(OpSize Size, OpSize ElementLoadSize, OpSize AddrElementSize, RefPair Dest,
RefPair Mask, RefVSIB VSIB) {
OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherImpl(OpcodeArgs, OpSize Size, OpSize ElementLoadSize, OpSize AddrElementSize,
RefPair Dest, RefPair Mask, RefVSIB VSIB) {
LOGMAN_THROW_A_FMT(AddrElementSize == OpSize::i32Bit || AddrElementSize == OpSize::i64Bit, "Unknown address element size");
const auto Is128Bit = Size == OpSize::i128Bit;
@@ -2061,10 +2057,13 @@ OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherImpl(OpSize Size, O
}
}
const auto GPRSize = GetGPROpSize();
auto AddrSize = (Op->Flags & X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) != 0 ? (GPRSize >> 1) : GPRSize;
RefPair Result {};
///< Calculate the low-half.
Result.Low = _VLoadVectorGatherMasked(OpSize::i128Bit, ElementLoadSize, Dest.Low, Mask.Low, BaseAddr, VSIB.Low, VSIB.High,
AddrElementSize, VSIB.Scale, 0, 0);
AddrElementSize, VSIB.Scale, 0, 0, AddrSize);
if (Is128Bit) {
Result.High = LoadZeroVector(OpSize::i128Bit);
@@ -2101,7 +2100,7 @@ OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherImpl(OpSize Size, O
///< Calculate the high-half.
auto ResultHigh = _VLoadVectorGatherMasked(OpSize::i128Bit, ElementLoadSize, DestReg, MaskReg, BaseAddr, AddrAddressing.Low,
AddrAddressing.High, AddrElementSize, VSIB.Scale, DataElementOffset, IndexElementOffset);
AddrAddressing.High, AddrElementSize, VSIB.Scale, DataElementOffset, IndexElementOffset, AddrSize);
if (AddrElementSize == OpSize::i64Bit && ElementLoadSize == OpSize::i32Bit) {
// If we only fetched 128-bits worth of data then the upper-result is all zero.
@@ -2114,7 +2113,7 @@ OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherImpl(OpSize Size, O
return Result;
}
OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherQPSImpl(Ref Dest, Ref Mask, RefVSIB VSIB) {
OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherQPSImpl(OpcodeArgs, Ref Dest, Ref Mask, RefVSIB VSIB) {
///< BaseAddr doesn't need to exist, calculate that here.
Ref BaseAddr = VSIB.BaseAddr;
@@ -2142,8 +2141,11 @@ OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherQPSImpl(Ref Dest, R
RefPair Result {};
const auto GPRSize = GetGPROpSize();
auto AddrSize = (Op->Flags & X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) != 0 ? (GPRSize >> 1) : GPRSize;
///< Calculate the low-half.
Result.Low = _VLoadVectorGatherMaskedQPS(OpSize::i128Bit, OpSize::i32Bit, Dest, Mask, BaseAddr, VSIB.Low, VSIB.High, VSIB.Scale);
Result.Low = _VLoadVectorGatherMaskedQPS(OpSize::i128Bit, OpSize::i32Bit, Dest, Mask, BaseAddr, VSIB.Low, VSIB.High, VSIB.Scale, AddrSize);
Result.High = LoadZeroVector(OpSize::i128Bit);
if (VSIB.High == Invalid()) {
// Special case for only loading two floats.
@@ -2202,15 +2204,15 @@ void OpDispatchBuilder::AVX128_VPGATHER(OpcodeArgs, OpSize AddrElementSize) {
}
///< AddressElementSize is now OpSize::i64Bit
Result = AVX128_VPGatherQPSImpl(Dest.Low, Mask.Low, VSIBLow);
Result = AVX128_VPGatherQPSImpl(Op, Dest.Low, Mask.Low, VSIBLow);
if (NeedsHighAddrBytes) {
auto Res = AVX128_VPGatherQPSImpl(Dest.High, Mask.High, VSIBHigh);
auto Res = AVX128_VPGatherQPSImpl(Op, Dest.High, Mask.High, VSIBHigh);
Result.High = Res.Low;
}
} else if (AddrElementSize == OpSize::i64Bit && ElementLoadSize == OpSize::i32Bit) {
Result = AVX128_VPGatherQPSImpl(Dest.Low, Mask.Low, VSIB);
Result = AVX128_VPGatherQPSImpl(Op, Dest.Low, Mask.Low, VSIB);
} else {
Result = AVX128_VPGatherImpl(Size, ElementLoadSize, AddrElementSize, Dest, Mask, VSIB);
Result = AVX128_VPGatherImpl(Op, Size, ElementLoadSize, AddrElementSize, Dest, Mask, VSIB);
}
AVX128_StoreResult_WithOpSize(Op, Op->Dest, Result);
@@ -2234,7 +2236,7 @@ void OpDispatchBuilder::AVX128_VCVTPH2PS(OpcodeArgs) {
// In the event that a memory operand is used as the source operand,
// the access width will always be half the size of the destination vector width
// (i.e. 128-bit vector -> 64-bit mem, 256-bit vector -> 128-bit mem)
Src.Low = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], SrcSize, Op->Flags);
Src.Low = LoadSourceFPR_WithOpSize(Op, Op->Src[0], SrcSize, Op->Flags);
}
RefPair Result {};
@@ -2289,7 +2291,7 @@ void OpDispatchBuilder::AVX128_VCVTPS2PH(OpcodeArgs) {
}
if (!Op->Dest.IsGPR()) {
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result.Low, StoreSize, OpSize::iInvalid);
StoreResultFPR_WithOpSize(Op, Op->Dest, Result.Low, StoreSize);
} else {
AVX128_StoreResult_WithOpSize(Op, Op->Dest, Result);
}
@@ -23,8 +23,8 @@ void OpDispatchBuilder::SHA1NEXTEOp(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
// ARMv8 SHA1 extension provides a `SHA1H` instruction which does a fixed rotate by 30.
// This only operates on element 0 rather than element 3. We don't have the luxury of rewriting the x86 SHA algorithm to take advantage of this.
@@ -36,7 +36,7 @@ void OpDispatchBuilder::SHA1NEXTEOp(OpcodeArgs) {
auto Tmp = _VAdd(OpSize::i128Bit, OpSize::i32Bit, Src, RotatedNode);
auto Result = _VInsElement(OpSize::i128Bit, OpSize::i32Bit, 3, 3, Src, Tmp);
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::SHA1MSG1Op(OpcodeArgs) {
@@ -44,15 +44,15 @@ void OpDispatchBuilder::SHA1MSG1Op(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref NewVec = _VExtr(OpSize::i128Bit, OpSize::i64Bit, Dest, Src, 1);
// [W0, W1, W2, W3] ^ [W2, W3, W4, W5]
Ref Result = _VXor(OpSize::i128Bit, OpSize::i8Bit, Dest, NewVec);
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::SHA1MSG2Op(OpcodeArgs) {
@@ -60,8 +60,8 @@ void OpDispatchBuilder::SHA1MSG2Op(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
// ARM SHA1 mostly matches x86 semantics, except the input and outputs are both flipped from elements 0,1,2,3 to 3,2,1,0.
auto Src1 = SHADataShuffle(Dest);
@@ -70,7 +70,7 @@ void OpDispatchBuilder::SHA1MSG2Op(OpcodeArgs) {
// The result is swizzled differently than expected
auto Result = SHADataShuffle(_VSha1SU1(Src1, Src2));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::SHA1RNDS4Op(OpcodeArgs) {
@@ -79,8 +79,8 @@ void OpDispatchBuilder::SHA1RNDS4Op(OpcodeArgs) {
return;
}
const uint64_t Imm8 = Op->Src[1].Literal() & 0b11;
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Result {};
Ref ConstantVector {};
@@ -112,7 +112,7 @@ void OpDispatchBuilder::SHA1RNDS4Op(OpcodeArgs) {
case 3: Result = SHADataShuffle(_VSha1P(Src1, ZeroRegister, Src2)); break;
}
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::SHA256MSG1Op(OpcodeArgs) {
@@ -120,12 +120,12 @@ void OpDispatchBuilder::SHA256MSG1Op(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
auto Result = _VSha256U0(Dest, Src);
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::SHA256MSG2Op(OpcodeArgs) {
@@ -133,8 +133,8 @@ void OpDispatchBuilder::SHA256MSG2Op(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
auto Src1 = _VExtr(OpSize::i128Bit, OpSize::i32Bit, Dest, Dest, 3);
auto DupDst = _VDupElement(OpSize::i128Bit, OpSize::i32Bit, Dest, 3);
@@ -142,7 +142,7 @@ void OpDispatchBuilder::SHA256MSG2Op(OpcodeArgs) {
auto Result = _VSha256U1(Src1, Src2);
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::SHA256RNDS2Op(OpcodeArgs) {
@@ -150,8 +150,8 @@ void OpDispatchBuilder::SHA256RNDS2Op(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
// Hardcoded to XMM0
auto XMM0 = LoadXMMRegister(0);
@@ -177,7 +177,7 @@ void OpDispatchBuilder::SHA256RNDS2Op(OpcodeArgs) {
auto B = _VSha256H2(EFGH, ABCD, Key);
auto Result = shuffle_abcd(A, B);
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::AESImcOp(OpcodeArgs) {
@@ -185,9 +185,9 @@ void OpDispatchBuilder::AESImcOp(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Result = _VAESImc(Src);
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::AESEncOp(OpcodeArgs) {
@@ -195,10 +195,10 @@ void OpDispatchBuilder::AESEncOp(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Result = _VAESEnc(OpSize::i128Bit, Dest, Src, LoadZeroVector(OpSize::i128Bit));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
@@ -208,11 +208,11 @@ void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
// TODO: Handle 256-bit VAESENC.
LOGMAN_THROW_A_FMT(Is128Bit, "256-bit VAESENC unimplemented");
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
Ref State = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Key = LoadSourceFPR(Op, Op->Src[1], Op->Flags);
Ref Result = _VAESEnc(DstSize, State, Key, LoadZeroVector(DstSize));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::AESEncLastOp(OpcodeArgs) {
@@ -220,10 +220,10 @@ void OpDispatchBuilder::AESEncLastOp(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Result = _VAESEncLast(OpSize::i128Bit, Dest, Src, LoadZeroVector(OpSize::i128Bit));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
@@ -233,11 +233,11 @@ void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
// TODO: Handle 256-bit VAESENCLAST.
LOGMAN_THROW_A_FMT(Is128Bit, "256-bit VAESENCLAST unimplemented");
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
Ref State = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Key = LoadSourceFPR(Op, Op->Src[1], Op->Flags);
Ref Result = _VAESEncLast(DstSize, State, Key, LoadZeroVector(DstSize));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::AESDecOp(OpcodeArgs) {
@@ -245,10 +245,10 @@ void OpDispatchBuilder::AESDecOp(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Result = _VAESDec(OpSize::i128Bit, Dest, Src, LoadZeroVector(OpSize::i128Bit));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
@@ -258,11 +258,11 @@ void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
// TODO: Handle 256-bit VAESDEC.
LOGMAN_THROW_A_FMT(Is128Bit, "256-bit VAESDEC unimplemented");
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
Ref State = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Key = LoadSourceFPR(Op, Op->Src[1], Op->Flags);
Ref Result = _VAESDec(DstSize, State, Key, LoadZeroVector(DstSize));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::AESDecLastOp(OpcodeArgs) {
@@ -270,10 +270,10 @@ void OpDispatchBuilder::AESDecLastOp(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Result = _VAESDecLast(OpSize::i128Bit, Dest, Src, LoadZeroVector(OpSize::i128Bit));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::VAESDecLastOp(OpcodeArgs) {
@@ -283,15 +283,15 @@ void OpDispatchBuilder::VAESDecLastOp(OpcodeArgs) {
// TODO: Handle 256-bit VAESDECLAST.
LOGMAN_THROW_A_FMT(Is128Bit, "256-bit VAESDECLAST unimplemented");
Ref State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
Ref State = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Key = LoadSourceFPR(Op, Op->Src[1], Op->Flags);
Ref Result = _VAESDecLast(DstSize, State, Key, LoadZeroVector(DstSize));
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
Ref OpDispatchBuilder::AESKeyGenAssistImpl(OpcodeArgs) {
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
const uint64_t RCON = Op->Src[1].Literal();
auto KeyGenSwizzle = LoadAndCacheNamedVectorConstant(OpSize::i128Bit, NAMED_VECTOR_AESKEYGENASSIST_SWIZZLE);
@@ -305,7 +305,7 @@ void OpDispatchBuilder::AESKeyGenAssist(OpcodeArgs) {
}
Ref Result = AESKeyGenAssistImpl(Op);
StoreResult(FPRClass, Op, Result, OpSize::iInvalid);
StoreResultFPR(Op, Result);
}
void OpDispatchBuilder::PCLMULQDQOp(OpcodeArgs) {
@@ -313,12 +313,12 @@ void OpDispatchBuilder::PCLMULQDQOp(OpcodeArgs) {
UnimplementedOp(Op);
return;
}
Ref Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags);
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Dest = LoadSourceFPR(Op, Op->Dest, Op->Flags);
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
const auto Selector = static_cast<uint8_t>(Op->Src[1].Literal());
auto Res = _PCLMUL(OpSize::i128Bit, Dest, Src, Selector & 0b1'0001);
StoreResult(FPRClass, Op, Res, OpSize::iInvalid);
StoreResultFPR(Op, Res);
}
void OpDispatchBuilder::VPCLMULQDQOp(OpcodeArgs) {
@@ -328,12 +328,12 @@ void OpDispatchBuilder::VPCLMULQDQOp(OpcodeArgs) {
}
const auto DstSize = OpSizeFromDst(Op);
Ref Src1 = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Ref Src2 = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags);
Ref Src1 = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Src2 = LoadSourceFPR(Op, Op->Src[1], Op->Flags);
const auto Selector = static_cast<uint8_t>(Op->Src[2].Literal());
Ref Res = _PCLMUL(DstSize, Src1, Src2, Selector & 0b1'0001);
StoreResult(FPRClass, Op, Res, OpSize::iInvalid);
StoreResultFPR(Op, Res);
}
} // namespace FEXCore::IR
@@ -263,7 +263,7 @@ void OpDispatchBuilder::CalculateDeferredFlags() {
Ref OpDispatchBuilder::IncrementByCarry(OpSize OpSize, Ref Src) {
// If CF not inverted, we use .cc since the increment happens when the
// condition is false. If CF inverted, invert to use .cs. A bit mindbendy.
return _NZCVSelectIncrement(OpSize, {CFInverted ? COND_UGE : COND_ULT}, Src, Src);
return _NZCVSelectIncrement(OpSize, CFInverted ? CondClass::UGE : CondClass::ULT, Src, Src);
}
Ref OpDispatchBuilder::CalculateFlags_ADC(IR::OpSize SrcSize, Ref Src1, Ref Src2) {
@@ -290,7 +290,7 @@ Ref OpDispatchBuilder::CalculateFlags_ADC(IR::OpSize SrcSize, Ref Src1, Ref Src2
Res = _Bfe(OpSize, IR::OpSizeAsBits(SrcSize), 0, Res);
// TODO: We can fold that second Bfe in (cmp uxth).
auto SelectCFInv = Select01(OpSize, CondClassType {COND_UGE}, Res, Src2PlusCF);
auto SelectCFInv = Select01(OpSize, CondClass::UGE, Res, Src2PlusCF);
SetNZ_ZeroCV(SrcSize, Res);
SetCFInverted(SelectCFInv);
@@ -324,7 +324,7 @@ Ref OpDispatchBuilder::CalculateFlags_SBB(IR::OpSize SrcSize, Ref Src1, Ref Src2
Res = Sub(OpSize, Src1, Src2PlusCF);
Res = _Bfe(OpSize, IR::OpSizeAsBits(SrcSize), 0, Res);
auto SelectCFInv = Select01(OpSize, CondClassType {COND_UGE}, Src1, Src2PlusCF);
auto SelectCFInv = Select01(OpSize, CondClass::UGE, Src1, Src2PlusCF);
SetNZ_ZeroCV(SrcSize, Res);
SetCFInverted(SelectCFInv);
@@ -406,7 +406,7 @@ void OpDispatchBuilder::CalculateFlags_MUL(IR::OpSize SrcSize, Ref Res, Ref High
// If High = SignBit, then sets to nZCv. Else sets to nzcV. Since SF/ZF
// undefined, this does what we need after inverting carry.
auto Zero = _InlineConstant(0);
_CondSubNZCV(OpSize::i64Bit, Zero, Zero, CondClassType {COND_EQ}, 0x1 /* nzcV */);
_CondSubNZCV(OpSize::i64Bit, Zero, Zero, CondClass::EQ, 0x1 /* nzcV */);
CFInverted = true;
}
@@ -423,7 +423,7 @@ void OpDispatchBuilder::CalculateFlags_UMUL(Ref High) {
// If High = 0, then sets to nZCv. Else sets to nzcV. Since SF/ZF undefined,
// this does what we need.
_CondSubNZCV(Size, Zero, Zero, CondClassType {COND_EQ}, 0x1 /* nzcV */);
_CondSubNZCV(Size, Zero, Zero, CondClass::EQ, 0x1 /* nzcV */);
CFInverted = true;
}
@@ -151,6 +151,9 @@ constexpr DispatchTableEntry OpDispatch_SecondaryGroupTables[] = {
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F2, 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 3>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F2, 4), 4, &OpDispatchBuilder::NOPOp},
// GROUP 17
{OPD(FEXCore::X86Tables::TYPE_GROUP_17, PF_66, 0), 1, &OpDispatchBuilder::Extrq_imm},
// GROUP P
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_NONE, 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 1>},
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_NONE, 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, true, false, 1>},
@@ -198,6 +198,8 @@ constexpr DispatchTableEntry OpDispatch_SecondaryRepNEModTables[] = {
{0x5E, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFDIVSCALARINSERT, OpSize::i64Bit>},
{0x5F, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFMAXSCALARINSERT, OpSize::i64Bit>},
{0x70, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSHUFWOp, true>},
{0x78, 1, &OpDispatchBuilder::Insertq_imm},
{0x79, 1, &OpDispatchBuilder::Insertq},
{0x7C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADDP, OpSize::i32Bit>},
{0x7D, 1, &OpDispatchBuilder::HSUBP<OpSize::i32Bit>},
{0xD0, 1, &OpDispatchBuilder::ADDSUBPOp<OpSize::i32Bit>},
@@ -256,6 +258,7 @@ constexpr DispatchTableEntry OpDispatch_SecondaryOpSizeModTables[] = {
{0x75, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, OpSize::i16Bit>},
{0x76, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, OpSize::i32Bit>},
{0x78, 1, nullptr}, // GROUP 17
{0x79, 1, &OpDispatchBuilder::Extrq},
{0x7C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADDP, OpSize::i64Bit>},
{0x7D, 1, &OpDispatchBuilder::HSUBP<OpSize::i64Bit>},
{0x7E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVBetweenGPR_FPR, OpDispatchBuilder::VectorOpType::SSE>},
File diff suppressed because it is too large. Load diff
@@ -28,7 +28,7 @@ class OrderedNode;
Ref OpDispatchBuilder::GetX87Top() {
// Yes, we are storing 3 bits in a single flag register.
// Deal with it
return _LoadContext(OpSize::i8Bit, GPRClass, offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
return _LoadContextGPR(OpSize::i8Bit, offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
}
void OpDispatchBuilder::SetX87FTW(Ref FTW) {
@@ -52,18 +52,18 @@ void OpDispatchBuilder::SetX87FTW(Ref FTW) {
FTW = _Orlshr(OpSize::i32Bit, FTW, FTW, 4);
// ...and that's it. StoreContext implicitly does the final masking.
_StoreContext(OpSize::i8Bit, GPRClass, FTW, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
_StoreContextGPR(OpSize::i8Bit, FTW, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
}
void OpDispatchBuilder::SetX87Top(Ref Value) {
_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
_StoreContextGPR(OpSize::i8Bit, Value, offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
}
// Float LoaD operation with memory operand
void OpDispatchBuilder::FLD(OpcodeArgs, IR::OpSize Width) {
const auto ReadWidth = (Width == OpSize::f80Bit) ? OpSize::i128Bit : Width;
Ref Data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], Width, Op->Flags);
Ref Data = LoadSourceFPR_WithOpSize(Op, Op->Src[0], Width, Op->Flags);
Ref ConvertedData = Data;
// Convert to 80bit float
if (Width == OpSize::i32Bit || Width == OpSize::i64Bit) {
@@ -79,14 +79,14 @@ void OpDispatchBuilder::FLDFromStack(OpcodeArgs) {
void OpDispatchBuilder::FBLD(OpcodeArgs) {
// Read from memory
Ref Data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], OpSize::f80Bit, Op->Flags);
Ref Data = LoadSourceFPR_WithOpSize(Op, Op->Src[0], OpSize::f80Bit, Op->Flags);
Ref ConvertedData = _F80BCDLoad(Data);
_PushStack(ConvertedData, Data, OpSize::i128Bit, true);
}
void OpDispatchBuilder::FBSTP(OpcodeArgs) {
Ref converted = _F80BCDStore(_ReadStackValue(0));
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, converted, OpSize::f80Bit, OpSize::i8Bit);
StoreResultFPR_WithOpSize(Op, Op->Dest, converted, OpSize::f80Bit, OpSize::i8Bit);
_PopStackDestroy();
}
@@ -99,7 +99,7 @@ void OpDispatchBuilder::FLD_Const(OpcodeArgs, NamedVectorConstant K) {
void OpDispatchBuilder::FILD(OpcodeArgs) {
const auto ReadWidth = OpSizeFromSrc(Op);
// Read from memory
Ref Data = LoadSource_WithOpSize(GPRClass, Op, Op->Src[0], ReadWidth, Op->Flags);
Ref Data = LoadSourceGPR_WithOpSize(Op, Op->Src[0], ReadWidth, Op->Flags);
// Sign extend to 64bits
if (ReadWidth != OpSize::i64Bit) {
@@ -112,15 +112,15 @@ void OpDispatchBuilder::FILD(OpcodeArgs) {
// Extract sign and make integer absolute
auto zero = Constant(0);
_SubNZCV(OpSize::i64Bit, Data, zero);
auto sign = _NZCVSelect(OpSize::i64Bit, CondClassType {COND_SLT}, Constant(0x8000), zero);
auto absolute = _Neg(OpSize::i64Bit, Data, CondClassType {COND_MI});
auto sign = _NZCVSelect(OpSize::i64Bit, CondClass::SLT, Constant(0x8000), zero);
auto absolute = _Neg(OpSize::i64Bit, Data, CondClass::MI);
// left justify the absolute integer
auto shift = Sub(OpSize::i64Bit, Constant(63), _FindMSB(IR::OpSize::i64Bit, absolute));
auto shifted = _Lshl(OpSize::i64Bit, absolute, shift);
auto adjusted_exponent = Sub(OpSize::i64Bit, Constant(0x3fff + 63), shift);
auto zeroed_exponent = _Select(COND_EQ, absolute, zero, zero, adjusted_exponent);
auto zeroed_exponent = _Select(OpSize::i64Bit, OpSize::i64Bit, CondClass::EQ, absolute, zero, zero, adjusted_exponent);
auto upper = _Or(OpSize::i64Bit, sign, zeroed_exponent);
Ref ConvertedData = _VLoadTwoGPRs(shifted, upper);
@@ -166,12 +166,12 @@ void OpDispatchBuilder::FIST(OpcodeArgs, bool Truncate) {
// Check for NaN/Infinity: exponent = 0x7fff
SaveNZCV();
_TestNZ(OpSize::i64Bit, Exponent, Constant(0x7fff));
Ref IsSpecial = _NZCVSelect01({COND_EQ});
Ref IsSpecial = _NZCVSelect01(CondClass::EQ);
// For overflow detection, check if exponent indicates a value >= 2^15
// Biased exponent for 2^15 is 0x3fff + 15 = 0x400e
SubWithFlags(OpSize::i64Bit, Exponent, 0x400e);
Ref IsOverflow = _NZCVSelect01({COND_UGE});
Ref IsOverflow = _NZCVSelect01(CondClass::UGE);
// Set Invalid Operation flag if overflow or special value
Ref InvalidFlag = _Or(OpSize::i64Bit, IsSpecial, IsOverflow);
@@ -180,7 +180,7 @@ void OpDispatchBuilder::FIST(OpcodeArgs, bool Truncate) {
Data = _F80CVTInt(Size, Data, Truncate);
StoreResult_WithOpSize(GPRClass, Op, Op->Dest, Data, Size, OpSize::i8Bit);
StoreResultGPR_WithOpSize(Op, Op->Dest, Data, Size, OpSize::i8Bit);
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
_PopStackDestroy();
@@ -206,10 +206,10 @@ void OpDispatchBuilder::FADD(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispa
// We have one memory argument
Ref Arg {};
if (Integer) {
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
Arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
Arg = _F80CVTToInt(Arg, Width);
} else {
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Arg = _F80CVTTo(Arg, Width);
}
@@ -236,10 +236,10 @@ void OpDispatchBuilder::FMUL(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispa
// We have one memory argument
Ref arg {};
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
arg = _F80CVTToInt(arg, Width);
} else {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
arg = _F80CVTTo(arg, Width);
}
@@ -273,10 +273,10 @@ void OpDispatchBuilder::FDIV(OpcodeArgs, IR::OpSize Width, bool Integer, bool Re
// We have one memory argument
Ref arg {};
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
arg = _F80CVTToInt(arg, Width);
} else {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
arg = _F80CVTTo(arg, Width);
}
@@ -314,10 +314,10 @@ void OpDispatchBuilder::FSUB(OpcodeArgs, IR::OpSize Width, bool Integer, bool Re
// We have one memory argument
Ref Arg {};
if (Integer) {
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
Arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
Arg = _F80CVTToInt(Arg, Width);
} else {
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Arg = _F80CVTTo(Arg, Width);
}
@@ -340,7 +340,7 @@ Ref OpDispatchBuilder::GetX87FTW_Helper() {
// bytes, we use the well-known bit twiddling algorithm:
//
// https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
Ref X = _LoadContext(OpSize::i8Bit, GPRClass, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
Ref X = _LoadContextGPR(OpSize::i8Bit, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
X = _Orlshl(OpSize::i32Bit, X, X, 4);
X = _And(OpSize::i32Bit, X, Constant(0x0f0f0f0f));
X = _Orlshl(OpSize::i32Bit, X, X, 2);
@@ -381,41 +381,41 @@ void OpDispatchBuilder::X87FNSTENV(OpcodeArgs) {
_SyncStackToSlow();
const auto Size = OpSizeFromSrc(Op);
Ref Mem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
Ref Mem = LoadSourceGPR(Op, Op->Dest, Op->Flags, {.LoadData = false});
Mem = AppendSegmentOffset(Mem, Op->Flags);
{
auto FCW = _LoadContext(OpSize::i16Bit, GPRClass, offsetof(FEXCore::Core::CPUState, FCW));
_StoreMem(GPRClass, Size, Mem, FCW, Size);
auto FCW = _LoadContextGPR(OpSize::i16Bit, offsetof(FEXCore::Core::CPUState, FCW));
_StoreMemGPR(Size, Mem, FCW, Size);
}
{ _StoreMem(GPRClass, Size, ReconstructFSW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 1), Size, MEM_OFFSET_SXTX, 1); }
{ _StoreMemGPR(Size, ReconstructFSW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 1), Size, MemOffsetType::SXTX, 1); }
auto ZeroConst = Constant(0);
{
// FTW
_StoreMem(GPRClass, Size, GetX87FTW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, GetX87FTW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MemOffsetType::SXTX, 1);
}
{
// Instruction Offset
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 3), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 3), Size, MemOffsetType::SXTX, 1);
}
{
// Instruction CS selector (+ Opcode)
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 4), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 4), Size, MemOffsetType::SXTX, 1);
}
{
// Data pointer offset
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 5), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 5), Size, MemOffsetType::SXTX, 1);
}
{
// Data pointer selector
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 6), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 6), Size, MemOffsetType::SXTX, 1);
}
}
@@ -441,20 +441,20 @@ void OpDispatchBuilder::X87LDENV(OpcodeArgs) {
_StackForceSlow();
const auto Size = OpSizeFromSrc(Op);
Ref Mem = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, {.LoadData = false});
Ref Mem = LoadSourceGPR(Op, Op->Src[0], Op->Flags, {.LoadData = false});
Mem = AppendSegmentOffset(Mem, Op->Flags);
auto NewFCW = _LoadMem(GPRClass, OpSize::i16Bit, Mem, OpSize::i16Bit);
_StoreContext(OpSize::i16Bit, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
auto NewFCW = _LoadMemGPR(OpSize::i16Bit, Mem, OpSize::i16Bit);
_StoreContextGPR(OpSize::i16Bit, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
Ref MemLocation = Add(OpSize::i64Bit, Mem, IR::OpSizeToSize(Size) * 1);
auto NewFSW = _LoadMem(GPRClass, Size, MemLocation, Size);
auto NewFSW = _LoadMemGPR(Size, MemLocation, Size);
ReconstructX87StateFromFSW_Helper(NewFSW);
{
// FTW
Ref MemLocation = Add(OpSize::i64Bit, Mem, IR::OpSizeToSize(Size) * 2);
SetX87FTW(_LoadMem(GPRClass, Size, MemLocation, Size));
SetX87FTW(_LoadMemGPR(Size, MemLocation, Size));
}
}
@@ -483,61 +483,61 @@ void OpDispatchBuilder::X87FNSAVE(OpcodeArgs) {
Ref Mem = MakeSegmentAddress(Op, Op->Dest);
Ref Top = GetX87Top();
{
auto FCW = _LoadContext(OpSize::i16Bit, GPRClass, offsetof(FEXCore::Core::CPUState, FCW));
_StoreMem(GPRClass, Size, Mem, FCW, Size);
auto FCW = _LoadContextGPR(OpSize::i16Bit, offsetof(FEXCore::Core::CPUState, FCW));
_StoreMemGPR(Size, Mem, FCW, Size);
}
{ _StoreMem(GPRClass, Size, ReconstructFSW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 1), Size, MEM_OFFSET_SXTX, 1); }
{ _StoreMemGPR(Size, ReconstructFSW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 1), Size, MemOffsetType::SXTX, 1); }
auto ZeroConst = Constant(0);
{
// FTW
_StoreMem(GPRClass, Size, GetX87FTW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, GetX87FTW_Helper(), Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MemOffsetType::SXTX, 1);
}
{
// Instruction Offset
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 3), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 3), Size, MemOffsetType::SXTX, 1);
}
{
// Instruction CS selector (+ Opcode)
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 4), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 4), Size, MemOffsetType::SXTX, 1);
}
{
// Data pointer offset
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 5), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 5), Size, MemOffsetType::SXTX, 1);
}
{
// Data pointer selector
_StoreMem(GPRClass, Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 6), Size, MEM_OFFSET_SXTX, 1);
_StoreMemGPR(Size, ZeroConst, Mem, Constant(IR::OpSizeToSize(Size) * 6), Size, MemOffsetType::SXTX, 1);
}
auto SevenConst = Constant(7);
const auto LoadSize = ReducedPrecisionMode ? OpSize::i64Bit : OpSize::i128Bit;
for (int i = 0; i < 7; ++i) {
Ref data = _LoadContextIndexed(Top, LoadSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit), FPRClass);
Ref data = _LoadContextFPRIndexed(Top, LoadSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit));
if (ReducedPrecisionMode) {
data = _F80CVTTo(data, OpSize::i64Bit);
}
_StoreMem(FPRClass, OpSize::i128Bit, data, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * i)), OpSize::i8Bit, MEM_OFFSET_SXTX, 1);
_StoreMemFPR(OpSize::i128Bit, data, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * i)), OpSize::i8Bit, MemOffsetType::SXTX, 1);
Top = _And(OpSize::i32Bit, Add(OpSize::i32Bit, Top, 1), SevenConst);
}
// The final st(7) needs a bit of special handling here
Ref data = _LoadContextIndexed(Top, LoadSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit), FPRClass);
Ref data = _LoadContextFPRIndexed(Top, LoadSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit));
if (ReducedPrecisionMode) {
data = _F80CVTTo(data, OpSize::i64Bit);
}
// ST7 broken in to two parts
// Lower 64bits [63:0]
// upper 16 bits [79:64]
_StoreMem(FPRClass, OpSize::i64Bit, data, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (7 * 10)), OpSize::i8Bit, MEM_OFFSET_SXTX, 1);
_StoreMemFPR(OpSize::i64Bit, data, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (7 * 10)), OpSize::i8Bit, MemOffsetType::SXTX, 1);
auto topBytes = _VDupElement(OpSize::i128Bit, OpSize::i16Bit, data, 4);
_StoreMem(FPRClass, OpSize::i16Bit, topBytes, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (7 * 10) + 8), OpSize::i8Bit, MEM_OFFSET_SXTX, 1);
_StoreMemFPR(OpSize::i16Bit, topBytes, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (7 * 10) + 8), OpSize::i8Bit, MemOffsetType::SXTX, 1);
// reset to default
FNINIT(Op);
@@ -548,8 +548,8 @@ void OpDispatchBuilder::X87FRSTOR(OpcodeArgs) {
const auto Size = OpSizeFromSrc(Op);
Ref Mem = MakeSegmentAddress(Op, Op->Src[0]);
auto NewFCW = _LoadMem(GPRClass, OpSize::i16Bit, Mem, OpSize::i16Bit);
_StoreContext(OpSize::i16Bit, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
auto NewFCW = _LoadMemGPR(OpSize::i16Bit, Mem, OpSize::i16Bit);
_StoreContextGPR(OpSize::i16Bit, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
if (ReducedPrecisionMode) {
// ignore the rounding precision, we're always 64-bit in F64.
// extract rounding mode
@@ -561,11 +561,11 @@ void OpDispatchBuilder::X87FRSTOR(OpcodeArgs) {
_SetRoundingMode(roundingMode, false, roundingMode);
}
auto NewFSW = _LoadMem(GPRClass, Size, Mem, Constant(IR::OpSizeToSize(Size) * 1), Size, MEM_OFFSET_SXTX, 1);
auto NewFSW = _LoadMemGPR(Size, Mem, Constant(IR::OpSizeToSize(Size) * 1), Size, MemOffsetType::SXTX, 1);
Ref Top = ReconstructX87StateFromFSW_Helper(NewFSW);
{
// FTW
SetX87FTW(_LoadMem(GPRClass, Size, Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MEM_OFFSET_SXTX, 1));
SetX87FTW(_LoadMemGPR(Size, Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MemOffsetType::SXTX, 1));
}
auto SevenConst = Constant(7);
@@ -574,14 +574,14 @@ void OpDispatchBuilder::X87FRSTOR(OpcodeArgs) {
Ref Mask = _VLoadTwoGPRs(low, high);
const auto StoreSize = ReducedPrecisionMode ? OpSize::i64Bit : OpSize::i128Bit;
for (int i = 0; i < 7; ++i) {
Ref Reg = _LoadMem(FPRClass, OpSize::i128Bit, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * i)), OpSize::i8Bit, MEM_OFFSET_SXTX, 1);
Ref Reg = _LoadMemFPR(OpSize::i128Bit, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * i)), OpSize::i8Bit, MemOffsetType::SXTX, 1);
// Mask off the top bits
Reg = _VAnd(OpSize::i128Bit, OpSize::i128Bit, Reg, Mask);
if (ReducedPrecisionMode) {
// Convert to double precision
Reg = _F80CVT(OpSize::i64Bit, Reg);
}
_StoreContextIndexed(Reg, Top, StoreSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit), FPRClass);
_StoreContextFPRIndexed(Reg, Top, StoreSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit));
Top = _And(OpSize::i32Bit, Add(OpSize::i32Bit, Top, 1), SevenConst);
}
@@ -590,19 +590,19 @@ void OpDispatchBuilder::X87FRSTOR(OpcodeArgs) {
// ST7 broken in to two parts
// Lower 64bits [63:0]
// upper 16 bits [79:64]
Ref Reg = _LoadMem(FPRClass, OpSize::i64Bit, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * 7)), OpSize::i8Bit, MEM_OFFSET_SXTX, 1);
Ref RegHigh = _LoadMem(FPRClass, OpSize::i16Bit, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * 7) + 8), OpSize::i8Bit, MEM_OFFSET_SXTX, 1);
Ref Reg = _LoadMemFPR(OpSize::i64Bit, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * 7)), OpSize::i8Bit, MemOffsetType::SXTX, 1);
Ref RegHigh = _LoadMemFPR(OpSize::i16Bit, Mem, Constant((IR::OpSizeToSize(Size) * 7) + (10 * 7) + 8), OpSize::i8Bit, MemOffsetType::SXTX, 1);
Reg = _VInsElement(OpSize::i128Bit, OpSize::i16Bit, 4, 0, Reg, RegHigh);
if (ReducedPrecisionMode) {
Reg = _F80CVT(OpSize::i64Bit, Reg); // Convert to double precision
}
_StoreContextIndexed(Reg, Top, StoreSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit), FPRClass);
_StoreContextFPRIndexed(Reg, Top, StoreSize, MMBaseOffset(), IR::OpSizeToSize(OpSize::i128Bit));
}
// Load / Store Control Word
void OpDispatchBuilder::X87FSTCW(OpcodeArgs) {
auto FCW = _LoadContext(OpSize::i16Bit, GPRClass, offsetof(FEXCore::Core::CPUState, FCW));
StoreResult(GPRClass, Op, FCW, OpSize::iInvalid);
auto FCW = _LoadContextGPR(OpSize::i16Bit, offsetof(FEXCore::Core::CPUState, FCW));
StoreResultGPR(Op, FCW);
}
void OpDispatchBuilder::X87FLDCW(OpcodeArgs) {
@@ -610,8 +610,8 @@ void OpDispatchBuilder::X87FLDCW(OpcodeArgs) {
// to switch for now to slow mode whenever these are manually changed.
// Remove the next line and try DF_04.asm in fast path.
_StackForceSlow();
Ref NewFCW = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
_StoreContext(OpSize::i16Bit, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
Ref NewFCW = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
_StoreContextGPR(OpSize::i16Bit, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
}
void OpDispatchBuilder::FXCH(OpcodeArgs) {
@@ -647,10 +647,10 @@ void OpDispatchBuilder::FCOMI(OpcodeArgs, IR::OpSize Width, bool Integer, OpDisp
if (Width == OpSize::i16Bit || Width == OpSize::i32Bit || Width == OpSize::i64Bit) {
// Memory arg
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
b = _F80CVTToInt(arg, Width);
} else {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
b = _F80CVTTo(arg, Width);
}
} else {
@@ -766,7 +766,7 @@ Ref OpDispatchBuilder::ReconstructFSW_Helper(Ref T) {
void OpDispatchBuilder::X87FNSTSW(OpcodeArgs) {
Ref TopValue = _SyncStackToSlow();
Ref StatusWord = ReconstructFSW_Helper(TopValue);
StoreResult(GPRClass, Op, StatusWord, OpSize::iInvalid);
StoreResultGPR(Op, StatusWord);
}
void OpDispatchBuilder::FNCLEX(OpcodeArgs) {
@@ -785,12 +785,12 @@ void OpDispatchBuilder::FNINIT(OpcodeArgs) {
// Init FCW to 0x037F
auto NewFCW = Constant(0x037F);
_StoreContext(OpSize::i16Bit, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
_StoreContextGPR(OpSize::i16Bit, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
// Set top to zero
SetX87Top(Zero);
// Tags all get marked as invalid
_StoreContext(OpSize::i8Bit, GPRClass, Zero, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
_StoreContextGPR(OpSize::i8Bit, Zero, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
// Reinits the simulated stack
_InitStack();
@@ -863,7 +863,7 @@ void OpDispatchBuilder::X87FXAM(OpcodeArgs) {
auto TopValid = _StackValidTag(0);
// In the case of top being invalid then C3:C2:C0 is 0b101
auto C3 = Select01(OpSize::i32Bit, CondClassType {COND_NEQ}, TopValid, Constant(1));
auto C3 = Select01(OpSize::i32Bit, CondClass::NEQ, TopValid, Constant(1));
auto C2 = TopValid;
auto C0 = C3; // Mirror C3 until something other than zero is supported
@@ -29,38 +29,38 @@ void OpDispatchBuilder::X87LDENVF64(OpcodeArgs) {
const auto Size = OpSizeFromSrc(Op);
Ref Mem = MakeSegmentAddress(Op, Op->Src[0]);
auto NewFCW = _LoadMem(GPRClass, OpSize::i16Bit, Mem, OpSize::i16Bit);
auto NewFCW = _LoadMemGPR(OpSize::i16Bit, Mem, OpSize::i16Bit);
// ignore the rounding precision, we're always 64-bit in F64.
// extract rounding mode
Ref roundingMode = _Bfe(OpSize::i32Bit, 3, 10, NewFCW);
_SetRoundingMode(roundingMode, false, roundingMode);
_StoreContext(OpSize::i16Bit, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
_StoreContextGPR(OpSize::i16Bit, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
auto NewFSW = _LoadMem(GPRClass, Size, Mem, Constant(IR::OpSizeToSize(Size)), Size, MEM_OFFSET_SXTX, 1);
auto NewFSW = _LoadMemGPR(Size, Mem, Constant(IR::OpSizeToSize(Size)), Size, MemOffsetType::SXTX, 1);
ReconstructX87StateFromFSW_Helper(NewFSW);
{
// FTW
SetX87FTW(_LoadMem(GPRClass, Size, Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MEM_OFFSET_SXTX, 1));
SetX87FTW(_LoadMemGPR(Size, Mem, Constant(IR::OpSizeToSize(Size) * 2), Size, MemOffsetType::SXTX, 1));
}
}
void OpDispatchBuilder::X87FLDCWF64(OpcodeArgs) {
_StackForceSlow();
Ref NewFCW = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
Ref NewFCW = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
// ignore the rounding precision, we're always 64-bit in F64.
// extract rounding mode
Ref roundingMode = _Bfe(OpSize::i32Bit, 3, 10, NewFCW);
_SetRoundingMode(roundingMode, false, roundingMode);
_StoreContext(OpSize::i16Bit, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
_StoreContextGPR(OpSize::i16Bit, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
}
// F64 ops
// Float load op with memory operand
void OpDispatchBuilder::FLDF64(OpcodeArgs, IR::OpSize Width) {
const auto ReadWidth = (Width == OpSize::f80Bit) ? OpSize::i128Bit : Width;
Ref Data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], Width, Op->Flags);
Ref Data = LoadSourceFPR_WithOpSize(Op, Op->Src[0], Width, Op->Flags);
// Convert to 64bit float
Ref ConvertedData = Data;
if (Width == OpSize::i32Bit) {
@@ -73,7 +73,7 @@ void OpDispatchBuilder::FLDF64(OpcodeArgs, IR::OpSize Width) {
void OpDispatchBuilder::FBLDF64(OpcodeArgs) {
// Read from memory
Ref Data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], OpSize::f80Bit, Op->Flags);
Ref Data = LoadSourceFPR_WithOpSize(Op, Op->Src[0], OpSize::f80Bit, Op->Flags);
Ref ConvertedData = _F80BCDLoad(Data);
ConvertedData = _F80CVT(OpSize::i64Bit, ConvertedData);
_PushStack(ConvertedData, Data, OpSize::i64Bit, true);
@@ -82,7 +82,7 @@ void OpDispatchBuilder::FBLDF64(OpcodeArgs) {
void OpDispatchBuilder::FBSTPF64(OpcodeArgs) {
Ref converted = _F80CVTTo(_ReadStackValue(0), OpSize::i64Bit);
converted = _F80BCDStore(converted);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, converted, OpSize::f80Bit, OpSize::i8Bit);
StoreResultFPR_WithOpSize(Op, Op->Dest, converted, OpSize::f80Bit, OpSize::i8Bit);
_PopStackDestroy();
}
@@ -95,7 +95,7 @@ void OpDispatchBuilder::FILDF64(OpcodeArgs) {
const auto ReadWidth = OpSizeFromSrc(Op);
// Read from memory
Ref Data = LoadSource_WithOpSize(GPRClass, Op, Op->Src[0], ReadWidth, Op->Flags);
Ref Data = LoadSourceGPR_WithOpSize(Op, Op->Src[0], ReadWidth, Op->Flags);
if (ReadWidth == OpSize::i16Bit) {
Data = _Sbfe(OpSize::i64Bit, IR::OpSizeAsBits(ReadWidth), 0, Data);
}
@@ -112,7 +112,7 @@ void OpDispatchBuilder::FISTF64(OpcodeArgs, bool Truncate) {
} else {
data = _Float_ToGPR_S(Size == OpSize::i32Bit ? OpSize::i32Bit : OpSize::i64Bit, OpSize::i64Bit, data);
}
StoreResult_WithOpSize(GPRClass, Op, Op->Dest, data, Size, OpSize::i8Bit);
StoreResultGPR_WithOpSize(Op, Op->Dest, data, Size, OpSize::i8Bit);
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
_PopStackDestroy();
@@ -138,16 +138,16 @@ void OpDispatchBuilder::FADDF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDi
Ref arg {};
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
if (Width == OpSize::i16Bit) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
arg = _Float_FromGPR_S(OpSize::i64Bit, Width == OpSize::i64Bit ? OpSize::i64Bit : OpSize::i32Bit, arg);
} else if (Width == OpSize::i32Bit) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
arg = _Float_FToF(OpSize::i64Bit, OpSize::i32Bit, arg);
} else if (Width == OpSize::i64Bit) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
} else {
FEX_UNREACHABLE;
}
@@ -176,16 +176,16 @@ void OpDispatchBuilder::FMULF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpDi
Ref arg {};
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
if (Width == OpSize::i16Bit) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
arg = _Float_FromGPR_S(OpSize::i64Bit, Width == OpSize::i64Bit ? OpSize::i64Bit : OpSize::i32Bit, arg);
} else if (Width == OpSize::i32Bit) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
arg = _Float_FToF(OpSize::i64Bit, OpSize::i32Bit, arg);
} else if (Width == OpSize::i64Bit) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
} else {
FEX_UNREACHABLE;
}
@@ -228,16 +228,16 @@ void OpDispatchBuilder::FDIVF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool
if (Width == OpSize::i16Bit || Width == OpSize::i32Bit || Width == OpSize::i64Bit) {
if (Integer) {
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
Arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
if (Width == OpSize::i16Bit) {
Arg = _Sbfe(OpSize::i64Bit, 16, 0, Arg);
}
Arg = _Float_FromGPR_S(OpSize::i64Bit, Width == OpSize::i64Bit ? OpSize::i64Bit : OpSize::i32Bit, Arg);
} else if (Width == OpSize::i32Bit) {
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Arg = _Float_FToF(OpSize::i64Bit, OpSize::i32Bit, Arg);
} else if (Width == OpSize::i64Bit) {
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
}
} else {
FEX_UNREACHABLE;
@@ -285,16 +285,16 @@ void OpDispatchBuilder::FSUBF64(OpcodeArgs, IR::OpSize Width, bool Integer, bool
if (Width == OpSize::i16Bit || Width == OpSize::i32Bit || Width == OpSize::i64Bit) {
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
if (Width == OpSize::i16Bit) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
arg = _Float_FromGPR_S(OpSize::i64Bit, Width == OpSize::i64Bit ? OpSize::i64Bit : OpSize::i32Bit, arg);
} else if (Width == OpSize::i32Bit) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
arg = _Float_FToF(OpSize::i64Bit, OpSize::i32Bit, arg);
} else if (Width == OpSize::i64Bit) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
}
} else {
FEX_UNREACHABLE;
@@ -332,16 +332,16 @@ void OpDispatchBuilder::FCOMIF64(OpcodeArgs, IR::OpSize Width, bool Integer, OpD
} else if (Width == OpSize::i16Bit || Width == OpSize::i32Bit || Width == OpSize::i64Bit) {
// Memory arg
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceGPR(Op, Op->Src[0], Op->Flags);
if (Width == OpSize::i16Bit) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
b = _Float_FromGPR_S(OpSize::i64Bit, Width == OpSize::i64Bit ? OpSize::i64Bit : OpSize::i32Bit, arg);
} else if (Width == OpSize::i32Bit) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
b = _Float_FToF(OpSize::i64Bit, OpSize::i32Bit, arg);
} else if (Width == OpSize::i64Bit) {
b = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
b = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
}
} else {
FEX_UNREACHABLE;
@@ -393,8 +393,8 @@ void OpDispatchBuilder::X87FXTRACTF64(OpcodeArgs) {
SaveNZCV();
_TestNZ(OpSize::i64Bit, Gpr, Constant(0x7fff'ffff'ffff'ffffUL));
Ref Sig = _NZCVSelectV(OpSize::i64Bit, {COND_EQ}, SigZV, SigNZV);
Ref Exp = _NZCVSelectV(OpSize::i64Bit, {COND_EQ}, ExpZV, ExpNZV);
Ref Sig = _NZCVSelectV(OpSize::i64Bit, CondClass::EQ, SigZV, SigNZV);
Ref Exp = _NZCVSelectV(OpSize::i64Bit, CondClass::EQ, ExpZV, ExpNZV);
_PopStackDestroy();
_PushStack(Exp, Exp, OpSize::i64Bit, true);
@@ -442,7 +442,7 @@ constexpr std::array<X86InstInfo, MAX_REPNE_MOD_TABLE_SIZE> RepNEModOps = []() c
{0x70, 1, X86InstInfo{"PSHUFLW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1}},
{0x71, 3, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0}},
{0x74, 4, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{0x78, 1, X86InstInfo{"INSERTQ", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_SF_MOD_REG_ONLY | FLAGS_XMM_FLAGS,2}},
{0x78, 1, X86InstInfo{"INSERTQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_REG_ONLY | FLAGS_XMM_FLAGS,2}},
{0x79, 1, X86InstInfo{"INSERTQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_REG_ONLY | FLAGS_XMM_FLAGS, 0}},
{0x7A, 2, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{0x7C, 1, X86InstInfo{"HADDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0}},
@@ -7,6 +7,7 @@ $end_info$
#pragma once
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <array>
@@ -559,21 +560,6 @@ constexpr static inline void GenerateTableWithCopy(X86InstInfo *FinalTable, X86T
}
};
template<typename OpcodeType>
static inline void LateInitCopyTable(X86InstInfo *FinalTable, X86TablesInfoStruct<OpcodeType> const *OtherLocal, size_t OtherTableSize) {
for (size_t j = 0; j < OtherTableSize; ++j) {
X86TablesInfoStruct<OpcodeType> const &OtherOp = OtherLocal[j];
auto OtherOpNum = OtherOp.first;
X86InstInfo const &OtherInfo = OtherOp.Info;
for (uint32_t i = 0; i < OtherOp.second; ++i) {
X86InstInfo &FinalOp = FinalTable[OtherOpNum + i];
if (FinalOp.Type == TYPE_COPY_OTHER) {
FinalOp = OtherInfo;
}
}
}
}
template<typename OpcodeType>
constexpr static inline void GenerateX87Table(X86InstInfo *FinalTable, X86TablesInfoStruct<OpcodeType> const *LocalTable, size_t TableSize) {
for (size_t j = 0; j < TableSize; ++j) {
@@ -604,12 +590,6 @@ constexpr static inline void GenerateX87Table(X86InstInfo *FinalTable, X86Tables
}
};
}
FEX_DEFINE_ENUM_FMT_PASSTHROUGH(FEXCore::X86Tables::DecodedOperand::OpType);
template <>
struct fmt::formatter<FEXCore::X86Tables::DecodedOperand::OpType> : formatter<uint32_t> {
template <typename FormatContext>
auto format(FEXCore::X86Tables::DecodedOperand::OpType type, FormatContext& ctx) const {
return fmt::formatter<uint32_t>::format(static_cast<uint32_t>(type), ctx);
}
};
} // namespace FEXCore::X86Tables
+9 -131
View File
@@ -20,7 +20,6 @@
namespace FEXCore::IR {
class OrderedNode;
class RegisterAllocationPass;
/**
* @brief The IROp_Header is an dynamically sized array
@@ -431,94 +430,6 @@ static_assert(sizeof(OrderedNode) == (sizeof(OrderedNodeHeader) + 2 * sizeof(uin
// };
using Ref = OrderedNode*;
struct FEX_PACKED RegisterClassType final {
using value_type = uint32_t;
value_type Val;
[[nodiscard]] constexpr operator value_type() const {
return Val;
}
[[nodiscard]]
friend constexpr bool operator==(const RegisterClassType&, const RegisterClassType&) = default;
};
struct FEX_PACKED CondClassType final {
uint8_t Val;
[[nodiscard]] constexpr operator uint8_t() const {
return Val;
}
[[nodiscard]]
friend constexpr bool operator==(const CondClassType&, const CondClassType&) = default;
};
struct FEX_PACKED MemOffsetType final {
uint8_t Val;
[[nodiscard]] constexpr operator uint8_t() const {
return Val;
}
[[nodiscard]]
friend constexpr bool operator==(const MemOffsetType&, const MemOffsetType&) = default;
};
struct FEX_PACKED TypeDefinition final {
uint16_t Val;
[[nodiscard]] constexpr operator uint16_t() const {
return Val;
}
[[nodiscard]]
static constexpr TypeDefinition Create(uint8_t Bytes) {
TypeDefinition Type {};
Type.Val = Bytes << 8;
return Type;
}
[[nodiscard]]
static constexpr TypeDefinition Create(uint8_t Bytes, uint8_t Elements) {
TypeDefinition Type {};
Type.Val = (Bytes << 8) | (Elements & 255);
return Type;
}
[[nodiscard]]
constexpr uint8_t Bytes() const {
return Val >> 8;
}
[[nodiscard]]
constexpr uint8_t Elements() const {
return Val & 255;
}
[[nodiscard]]
friend constexpr bool operator==(const TypeDefinition&, const TypeDefinition&) = default;
};
static_assert(std::is_trivially_copyable_v<TypeDefinition>);
struct FEX_PACKED FenceType final {
using value_type = uint8_t;
value_type Val;
[[nodiscard]] constexpr operator value_type() const {
return Val;
}
[[nodiscard]]
friend constexpr bool operator==(const FenceType&, const FenceType&) = default;
};
struct FEX_PACKED RoundType final {
uint8_t Val;
[[nodiscard]] constexpr operator uint8_t() const {
return Val;
}
[[nodiscard]]
friend constexpr bool operator==(const RoundType&, const RoundType&) = default;
};
class NodeIterator;
/* This iterator can be used to step though nodes.
* Due to how our IR is laid out, this can be used to either step
* though the CodeBlocks or though the code within a single block.
@@ -782,6 +693,15 @@ inline NodeID NodeWrapperBase<Type>::ID() const {
bool IsBlockExit(FEXCore::IR::IROps Op);
void Dump(fextl::stringstream* out, const IRListView* IR);
constexpr auto format_as(FEXCore::IR::NodeID ID) {
return ID.Value;
}
FEX_DEFINE_ENUM_FMT_PASSTHROUGH(FEXCore::IR::FenceType)
FEX_DEFINE_ENUM_FMT_PASSTHROUGH(FEXCore::IR::MemOffsetType)
FEX_DEFINE_ENUM_FMT_PASSTHROUGH(FEXCore::IR::OpSize)
FEX_DEFINE_ENUM_FMT_PASSTHROUGH(FEXCore::IR::RegClass)
} // namespace FEXCore::IR
template<>
@@ -790,45 +710,3 @@ struct std::hash<FEXCore::IR::NodeID> {
return std::hash<FEXCore::IR::NodeID::value_type> {}(ID.Value);
}
};
template<>
struct fmt::formatter<FEXCore::IR::NodeID> : fmt::formatter<FEXCore::IR::NodeID::value_type> {
using Base = fmt::formatter<FEXCore::IR::NodeID::value_type>;
// Pass-through the underlying value, so IDs can
// be formatted like any integral value.
template<typename FormatContext>
auto format(const FEXCore::IR::NodeID& ID, FormatContext& ctx) const {
return Base::format(ID.Value, ctx);
}
};
template<>
struct fmt::formatter<FEXCore::IR::RegisterClassType> : fmt::formatter<FEXCore::IR::RegisterClassType::value_type> {
using Base = fmt::formatter<FEXCore::IR::RegisterClassType::value_type>;
template<typename FormatContext>
auto format(const FEXCore::IR::RegisterClassType& Class, FormatContext& ctx) const {
return Base::format(Class.Val, ctx);
}
};
template<>
struct fmt::formatter<FEXCore::IR::FenceType> : fmt::formatter<FEXCore::IR::FenceType::value_type> {
using Base = fmt::formatter<FEXCore::IR::FenceType::value_type>;
template<typename FormatContext>
auto format(const FEXCore::IR::FenceType& Fence, FormatContext& ctx) const {
return Base::format(Fence.Val, ctx);
}
};
template<>
struct fmt::formatter<FEXCore::IR::OpSize> : fmt::formatter<std::underlying_type_t<FEXCore::IR::OpSize>> {
using Base = fmt::formatter<std::underlying_type_t<FEXCore::IR::OpSize>>;
template<typename FormatContext>
auto format(const FEXCore::IR::OpSize& OpSize, FormatContext& ctx) const {
return Base::format(FEXCore::ToUnderlying(OpSize), ctx);
}
};
+116 -111
View File
@@ -52,80 +52,68 @@
" * These are validations that can't be automatically inferred and need to be hand-written",
""
],
"Enums": {
"class CondClass : uint8_t": [
"EQ = 0,",
"NEQ = 1,",
"UGE = 2,",
"ULT = 3,",
"MI = 4,",
"PL = 5,",
"VS = 6,",
"VC = 7,",
"UGT = 8,",
"ULE = 9,",
"SGE = 10,",
"SLT = 11,",
"SGT = 12,",
"SLE = 13,",
"TSTZ = 14, /* bit test zero */",
"TSTNZ = 15, /* bit test nonzero */",
"",
"FLU = 16, /* float less or unordered */",
"FGE = 17, /* float greater or equal */",
"FLEU = 18, /* float less or equal or unordered */",
"FGT = 19, /* float greater */",
"FU = 20, /* float unordered */",
"FNU = 21, /* float not unordered */",
"",
"AL = 32, /* always */"
],
"class FenceType : uint8_t": [
"Load = 0,",
"Store = 1,",
"LoadStore = 2,",
"Inst = 3,"
],
"class MemOffsetType : uint8_t": [
"SXTX = 0,",
"UXTW = 1,",
"SXTW = 2,"
],
"class RegClass : uint32_t": [
"Invalid = 0,",
"GPR = 1,",
"GPRFixed = 2,",
"FPR = 3,",
"FPRFixed = 4,",
"Complex = 5,"
],
"class RoundMode : uint8_t": [
"Nearest = 0,",
"NegInfinity = 1,",
"PosInfinity = 2,",
"TowardsZero = 3, /* Truncate */",
"Host = 4,"
]
},
"Defines": [
"constexpr uint8_t COND_EQ = 0",
"constexpr uint8_t COND_NEQ = 1",
"constexpr uint8_t COND_UGE = 2",
"constexpr uint8_t COND_ULT = 3",
"constexpr uint8_t COND_MI = 4",
"constexpr uint8_t COND_PL = 5",
"constexpr uint8_t COND_VS = 6",
"constexpr uint8_t COND_VC = 7",
"constexpr uint8_t COND_UGT = 8",
"constexpr uint8_t COND_ULE = 9",
"constexpr uint8_t COND_SGE = 10",
"constexpr uint8_t COND_SLT = 11",
"constexpr uint8_t COND_SGT = 12",
"constexpr uint8_t COND_SLE = 13",
"constexpr uint8_t COND_TSTZ = 14 /* bit test zero */",
"constexpr uint8_t COND_TSTNZ = 15 /* bit test nonzero */",
"constexpr uint8_t COND_FLU = 16 /* float less or unordred */",
"constexpr uint8_t COND_FGE = 17 /* float greater or equal */",
"constexpr uint8_t COND_FLEU = 18 /* float less or equal or unordred */",
"constexpr uint8_t COND_FGT = 19 /* float greater */",
"constexpr uint8_t COND_FU = 20 /* float unordred */",
"constexpr uint8_t COND_FNU = 21 /* float not unordred */",
"constexpr uint8_t COND_AL = 32 /* always */",
"constexpr FEXCore::IR::RegisterClassType InvalidClass {0}",
"constexpr FEXCore::IR::RegisterClassType GPRClass {1}",
"constexpr FEXCore::IR::RegisterClassType GPRFixedClass {2}",
"constexpr FEXCore::IR::RegisterClassType FPRClass {3}",
"constexpr FEXCore::IR::RegisterClassType FPRFixedClass {4}",
"constexpr FEXCore::IR::RegisterClassType ComplexClass {5}",
"constexpr uint8_t NumClasses {6}",
"",
"constexpr FEXCore::IR::TypeDefinition i8 {TypeDefinition::Create(1, 0)}",
"constexpr FEXCore::IR::TypeDefinition i16 {TypeDefinition::Create(2, 0)}",
"constexpr FEXCore::IR::TypeDefinition i32 {TypeDefinition::Create(4, 0)}",
"constexpr FEXCore::IR::TypeDefinition i64 {TypeDefinition::Create(8, 0)}",
"constexpr FEXCore::IR::TypeDefinition i128 {TypeDefinition::Create(16, 0)}",
"",
"constexpr FEXCore::IR::TypeDefinition i8v8 {TypeDefinition::Create(1, 8)}",
"constexpr FEXCore::IR::TypeDefinition i8v16 {TypeDefinition::Create(1, 16)}",
"constexpr FEXCore::IR::TypeDefinition i16v4 {TypeDefinition::Create(2, 4)}",
"constexpr FEXCore::IR::TypeDefinition i16v8 {TypeDefinition::Create(2, 8)}",
"constexpr FEXCore::IR::TypeDefinition i32v2 {TypeDefinition::Create(4, 2)}",
"constexpr FEXCore::IR::TypeDefinition i32v4 {TypeDefinition::Create(4, 4)}",
"constexpr FEXCore::IR::TypeDefinition i64v2 {TypeDefinition::Create(8, 2)}",
"",
"constexpr uint8_t FCMP_FLAG_EQ = 0",
"constexpr uint8_t FCMP_FLAG_LT = 1",
"constexpr uint8_t FCMP_FLAG_UNORDERED = 2",
"constexpr FEXCore::IR::FenceType Fence_Load {0}",
"constexpr FEXCore::IR::FenceType Fence_Store {1}",
"constexpr FEXCore::IR::FenceType Fence_LoadStore {2}",
"constexpr FEXCore::IR::FenceType Fence_Inst {3}",
"constexpr uint8_t ROUND_MODE_NEAREST = 0",
"constexpr uint8_t ROUND_MODE_NEGATIVE_INFINITY = 1",
"constexpr uint8_t ROUND_MODE_POSITIVE_INFINITY = 2",
"constexpr uint8_t ROUND_MODE_TOWARDS_ZERO = 3",
"constexpr uint8_t ROUND_MODE_FLUSH_TO_ZERO = 1 << 2",
"constexpr FEXCore::IR::RoundType Round_Nearest {ROUND_MODE_NEAREST}",
"constexpr FEXCore::IR::RoundType Round_Negative_Infinity {ROUND_MODE_NEGATIVE_INFINITY}",
"constexpr FEXCore::IR::RoundType Round_Positive_Infinity {ROUND_MODE_POSITIVE_INFINITY}",
"constexpr FEXCore::IR::RoundType Round_Towards_Zero {ROUND_MODE_TOWARDS_ZERO} /* Truncate */",
"constexpr FEXCore::IR::RoundType Round_Host {ROUND_MODE_TOWARDS_ZERO + 1}",
"constexpr FEXCore::IR::MemOffsetType MEM_OFFSET_SXTX {0}",
"constexpr FEXCore::IR::MemOffsetType MEM_OFFSET_UXTW {1}",
"constexpr FEXCore::IR::MemOffsetType MEM_OFFSET_SXTW {2}",
"struct BreakDefinition {",
" uint16_t ErrorRegister;",
" uint8_t Signal;",
@@ -148,13 +136,13 @@
"GPR": "OrderedNode*",
"FPR": "OrderedNode*",
"FenceType": "FenceType",
"RegisterClass": "RegisterClassType",
"CondClass": "CondClassType",
"RegisterClass": "RegClass",
"CondClass": "CondClass",
"SyscallFlags": "FEXCore::IR::SyscallFlags",
"SHA256Sum": "SHA256Sum",
"MemOffsetType": "MemOffsetType",
"BreakDefinition": "BreakDefinition",
"RoundType": "RoundType",
"RoundType": "RoundMode",
"FloatCompareOp": "FloatCompareOp",
"NamedVectorConstant": "FEXCore::IR::NamedVectorConstant",
"IndexNamedVectorConstant": "FEXCore::IR::IndexNamedVectorConstant",
@@ -307,7 +295,7 @@
"HasSideEffects": true,
"RAOverride": "0"
},
"CondJump SSA:$Cmp1, SSA:$Cmp2, SSA:$TrueBlock, SSA:$FalseBlock, CondClass:$Cond{{COND_NEQ}}, OpSize:$CompareSize{OpSize::iInvalid}, i1:$FromNZCV{false}": {
"CondJump SSA:$Cmp1, SSA:$Cmp2, SSA:$TrueBlock, SSA:$FalseBlock, CondClass:$Cond{CondClass::NEQ}, OpSize:$CompareSize{OpSize::iInvalid}, i1:$FromNZCV{false}": {
"Inline": ["", "AddSub"],
"HasSideEffects": true,
"RAOverride": "2"
@@ -364,20 +352,6 @@
"GPR = Copy GPR:$Source": {
"Desc": ["GPR copy, generated by RA to split live ranges"],
"DestSize": "OpSize::i64Bit"
},
"GPR = Swap1 GPR:$A, GPR:$B": {
"Desc": ["GPR swap part 1, generated by RA. Returns value of first source.",
"Destination must be second GPR."],
"DestSize": "OpSize::i64Bit"
},
"GPR = Swap2": {
"Desc": ["GPR swap part 2, generated by RA. Returns source source.",
"Must immediately succeed Swap1 with no intervening instructions",
"Kludge to workaround single destination restriction on IR",
"Hopefully temporary"],
"DestSize": "OpSize::i64Bit"
}
},
"StaticRA": {
@@ -423,8 +397,8 @@
],
"DestSize": "ByteSize",
"EmitValidation": [
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't LoadContext to GPR\"",
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't LoadContext to XMM\""
]
@@ -437,8 +411,8 @@
"HasSideEffects": true,
"DestSize": "ByteSize",
"EmitValidation": [
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't LoadContext to GPR\"",
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't LoadContext to XMM\""
]
@@ -454,8 +428,8 @@
"HasSideEffects": true,
"DestSize": "ByteSize",
"EmitValidation": [
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't StoreContext to GPR\"",
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't StoreContext to XMM\""
]
@@ -472,8 +446,8 @@
"EmitValidation": [
"WalkFindRegClass($Value1) == $Class",
"WalkFindRegClass($Value2) == $Class",
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
"!($Offset >= offsetof(Core::CPUState, gregs[0]) && $Offset < offsetof(Core::CPUState, gregs[16])) && \"Can't StoreContext to GPR\"",
"!($Offset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $Offset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't StoreContext to XMM\""
]
@@ -485,8 +459,8 @@
],
"DestSize": "ByteSize",
"EmitValidation": [
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
"!($BaseOffset >= offsetof(Core::CPUState, gregs[0]) && $BaseOffset < offsetof(Core::CPUState, gregs[16])) && \"Can't LoadContextIndexed to GPR\"",
"!($BaseOffset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $BaseOffset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't LoadContextIndexed to XMM\""
]
@@ -499,8 +473,8 @@
"DestSize": "ByteSize",
"EmitValidation": [
"WalkFindRegClass($Value) == $Class",
"($Class == GPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == FPRClass",
"($Class == FPRClass && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == GPRClass",
"($Class == RegClass::GPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit)) || $Class == RegClass::FPR",
"($Class == RegClass::FPR && (#ByteSize == IR::OpSize::i8Bit || #ByteSize == IR::OpSize::i16Bit || #ByteSize == IR::OpSize::i32Bit || #ByteSize == IR::OpSize::i64Bit || #ByteSize == IR::OpSize::i128Bit || #ByteSize == IR::OpSize::i256Bit)) || $Class == RegClass::GPR",
"!($BaseOffset >= offsetof(Core::CPUState, gregs[0]) && $BaseOffset < offsetof(Core::CPUState, gregs[16])) && \"Can't StoreContextIndexed to GPR\"",
"!($BaseOffset >= offsetof(Core::CPUState, xmm.avx.data[0]) && $BaseOffset < offsetof(Core::CPUState, xmm.avx.data[16])) && \"Can't StoreContextIndexed to XMM\""
]
@@ -604,7 +578,8 @@
"Desc": ["Does a x86 TSO compatible load from memory. Offset must be Invalid()."
],
"Inline": ["", "Memtso"],
"DestSize": "Size"
"DestSize": "Size",
"DynamicDispatch": true
},
"StoreMemTSO RegisterClass:$Class, OpSize:#Size, SSA:$Value, GPR:$Addr, GPR:$Offset, OpSize:$Align, MemOffsetType:$OffsetType, u8:$OffsetScale": {
@@ -612,7 +587,8 @@
],
"Inline": ["Zero", "", "Memtso"],
"HasSideEffects": true,
"DestSize": "Size"
"DestSize": "Size",
"DynamicDispatch": true
},
"FPR = VLoadVectorMasked OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Mask, GPR:$Addr, GPR:$Offset, MemOffsetType:$OffsetType, u8:$OffsetScale": {
@@ -630,7 +606,7 @@
"DestSize": "RegisterSize",
"ElementSize": "ElementSize"
},
"FPR = VLoadVectorGatherMasked OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Incoming, FPR:$Mask, GPR:$AddrBase, FPR:$VectorIndexLow, FPR:$VectorIndexHigh, OpSize:$VectorIndexElementSize, u8:$OffsetScale, u8:$DataElementOffsetStart, u8:$IndexElementOffsetStart": {
"FPR = VLoadVectorGatherMasked OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Incoming, FPR:$Mask, GPR:$AddrBase, FPR:$VectorIndexLow, FPR:$VectorIndexHigh, OpSize:$VectorIndexElementSize, u8:$OffsetScale, u8:$DataElementOffsetStart, u8:$IndexElementOffsetStart, OpSize:$AddrSize": {
"Desc": [
"Does a masked load similar to VPGATHERD* where the upper bit of each element",
"determines whether or not that element will be loaded from memory.",
@@ -644,7 +620,7 @@
"$VectorIndexElementSize == OpSize::i32Bit || $VectorIndexElementSize == OpSize::i64Bit"
]
},
"FPR = VLoadVectorGatherMaskedQPS OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Incoming, FPR:$MaskReg, GPR:$AddrBase, FPR:$VectorIndexLow, FPR:$VectorIndexHigh, u8:$OffsetScale": {
"FPR = VLoadVectorGatherMaskedQPS OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Incoming, FPR:$MaskReg, GPR:$AddrBase, FPR:$VectorIndexLow, FPR:$VectorIndexHigh, u8:$OffsetScale, OpSize:$AddrSize": {
"Desc": [
"Does a masked load similar to VPGATHERQPS where the upper bit of each element",
"determines whether or not that element will be loaded from memory.",
@@ -759,9 +735,10 @@
},
"Fence FenceType:$Fence": {
"Desc": ["Does a memory fence operation of the desired type",
"Fence_Load: Ensures load memory operations are serialized",
"Fence_Store: Ensures store memory operations are serialized",
"Fence_LoadStore: Ensures loads and store memory operations are serialized",
"FenceType::Load: Ensures load memory operations are serialized",
"FenceType::Store: Ensures store memory operations are serialized",
"FenceType::LoadStore: Ensures loads and store memory operations are serialized",
"FenceType::Inst: Instruction barrier. Ensures all instructions after this point will be explicitly fetched",
"Ensures the memory operations are globally visible"
],
"HasSideEffects": true
@@ -1006,7 +983,7 @@
"DestSize": "OpSize::i64Bit"
},
"GPR = Neg OpSize:#Size, GPR:$Src, CondClass:$Cond{{COND_AL}}": {
"GPR = Neg OpSize:#Size, GPR:$Src, CondClass:$Cond{CondClass::AL}": {
"Desc": ["Integer negation, with optional predication",
"Dest = Cond ? -Src : Src",
"Will truncate to 64 or 32bits"
@@ -1084,6 +1061,13 @@
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = Rbit OpSize:#Size, GPR:$Src": {
"Desc": ["Reverses the bit order of the register"],
"DestSize": "Size",
"EmitValidation": [
"Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = Add OpSize:#Size, GPR:$Src1, GPR:$Src2": {
"Desc": [ "Integer Add",
"Will truncate to 64 or 32bits"
@@ -1551,6 +1535,15 @@
"ResultSize == FEXCore::IR::OpSize::i32Bit || ResultSize == FEXCore::IR::OpSize::i64Bit"
]
},
"GPR = MaskGenerateFromBitWidth GPR:$BitWidth": {
"Desc": ["Generates a bit mask from with a value from [0, 63]",
"0 is special cased to full-mask",
"Special operation for SSE4a bitmask generation."
],
"DestSize": "FEXCore::IR::OpSize::i64Bit",
"ImplicitFlagClobber": true
},
"GPR = Extr OpSize:#Size, GPR:$Upper, GPR:$Lower, u8:$LSB": {
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
"It then extracts a bitfield width that size of a GPR from the LSB",
@@ -2159,22 +2152,34 @@
"FPR = VAnd OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
"DestSize": "RegisterSize",
"ElementSize": "ElementSize"
"ElementSize": "ElementSize",
"EmitValidation": [
"RegisterSize == FEXCore::IR::OpSize::i256Bit || RegisterSize == FEXCore::IR::OpSize::i128Bit || RegisterSize == FEXCore::IR::OpSize::i64Bit"
]
},
"FPR = VAndn OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
"DestSize": "RegisterSize",
"ElementSize": "ElementSize"
"ElementSize": "ElementSize",
"EmitValidation": [
"RegisterSize == FEXCore::IR::OpSize::i256Bit || RegisterSize == FEXCore::IR::OpSize::i128Bit || RegisterSize == FEXCore::IR::OpSize::i64Bit"
]
},
"FPR = VOr OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
"DestSize": "RegisterSize",
"ElementSize": "ElementSize"
"ElementSize": "ElementSize",
"EmitValidation": [
"RegisterSize == FEXCore::IR::OpSize::i256Bit || RegisterSize == FEXCore::IR::OpSize::i128Bit || RegisterSize == FEXCore::IR::OpSize::i64Bit"
]
},
"FPR = VXor OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
"DestSize": "RegisterSize",
"ElementSize": "ElementSize"
"ElementSize": "ElementSize",
"EmitValidation": [
"RegisterSize == FEXCore::IR::OpSize::i256Bit || RegisterSize == FEXCore::IR::OpSize::i128Bit || RegisterSize == FEXCore::IR::OpSize::i64Bit"
]
},
"FPR = VUQAdd OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
@@ -2838,7 +2843,7 @@
"Int: 64-bit, 32-bit, 16-bit"
],
"EmitValidation": [
"WalkFindRegClass($OriginalValue) == FPRClass || WalkFindRegClass($OriginalValue) == GPRClass"
"WalkFindRegClass($OriginalValue) == RegClass::FPR || WalkFindRegClass($OriginalValue) == RegClass::GPR"
],
"HasSideEffects": true,
"X87": true
+147 -103
View File
@@ -38,8 +38,8 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, uint64_t Arg)
*out << fextl::fmt::format("#{:#x}", Arg);
}
static void PrintArg(fextl::stringstream* out, const IRListView*, CondClassType Arg) {
if (Arg == COND_AL) {
static void PrintArg(fextl::stringstream* out, const IRListView*, CondClass Arg) {
if (Arg == CondClass::AL) {
*out << "ALWAYS";
return;
}
@@ -48,7 +48,7 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, CondClassType
"UGT", "ULE", "SGE", "SLT", "SGT", "SLE", "TSTZ", "TSTNZ",
"FLU", "FGE", "FLEU", "FGT", "FU", "FNU"};
*out << CondNames[Arg];
*out << CondNames[FEXCore::ToUnderlying(Arg)];
}
static void PrintArg(fextl::stringstream* out, const IRListView*, MemOffsetType Arg) {
@@ -58,39 +58,39 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, MemOffsetType
"SXTW",
};
*out << Names[Arg];
*out << Names[FEXCore::ToUnderlying(Arg)];
}
static void PrintArg(fextl::stringstream* out, const IRListView*, RegisterClassType Arg) {
if (Arg == GPRClass.Val) {
*out << "GPR";
} else if (Arg == GPRFixedClass.Val) {
*out << "GPRFixed";
} else if (Arg == FPRClass.Val) {
*out << "FPR";
} else if (Arg == FPRFixedClass.Val) {
*out << "FPRFixed";
} else {
*out << "Unknown Registerclass " << Arg;
}
static void PrintArg(fextl::stringstream* out, const IRListView*, RegClass Arg) {
*out << [Arg] {
switch (Arg) {
case RegClass::Invalid: return "Invalid";
case RegClass::GPR: return "GPR";
case RegClass::GPRFixed: return "GPRFixed";
case RegClass::FPR: return "FPR";
case RegClass::FPRFixed: return "FPRFixed";
case RegClass::Complex: return "Complex";
}
return "<Unknown RegClass Type>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNodeWrapper Arg) {
if (Arg.IsImmediate()) {
auto PhyReg = PhysicalRegister(Arg);
switch (PhyReg.Class) {
case FEXCore::IR::GPRClass.Val: *out << "r"; break;
case FEXCore::IR::GPRFixedClass.Val: *out << "R"; break;
case FEXCore::IR::FPRClass.Val: *out << "v"; break;
case FEXCore::IR::FPRFixedClass.Val: *out << "V"; break;
case FEXCore::IR::ComplexClass.Val: *out << "c"; break;
case FEXCore::IR::InvalidClass.Val: *out << "invalid"; break;
switch (PhyReg.AsRegClass()) {
case RegClass::GPR: *out << "r"; break;
case RegClass::GPRFixed: *out << "R"; break;
case RegClass::FPR: *out << "v"; break;
case RegClass::FPRFixed: *out << "V"; break;
case RegClass::Complex: *out << "c"; break;
case RegClass::Invalid: *out << "invalid"; break;
default: *out << "unknown"; break;
}
if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) {
*out << std::dec << (uint32_t)PhyReg.Reg;
if (PhyReg.AsRegClass() != RegClass::Invalid) {
*out << std::dec << uint32_t(PhyReg.Reg);
}
return;
@@ -124,41 +124,46 @@ static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNode
}
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::FenceType Arg) {
if (Arg == IR::Fence_Load) {
*out << "Loads";
} else if (Arg == IR::Fence_Store) {
*out << "Stores";
} else if (Arg == IR::Fence_LoadStore) {
*out << "LoadStores";
} else {
*out << "<Unknown Fence Type>";
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FenceType Arg) {
*out << [Arg] {
switch (Arg) {
case FenceType::Load: return "Loads";
case FenceType::Store: return "Stores";
case FenceType::LoadStore: return "LoadStores";
case FenceType::Inst: return "Instruction";
}
return "<Unknown Fence Type>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::RoundType Arg) {
switch (Arg) {
case FEXCore::IR::Round_Nearest: *out << "Nearest"; break;
case FEXCore::IR::Round_Negative_Infinity: *out << "-Inf"; break;
case FEXCore::IR::Round_Positive_Infinity: *out << "+Inf"; break;
case FEXCore::IR::Round_Towards_Zero: *out << "Towards Zero"; break;
case FEXCore::IR::Round_Host: *out << "Host"; break;
default: *out << "<Unknown Round Type>"; break;
}
static void PrintArg(fextl::stringstream* out, const IRListView*, RoundMode Arg) {
*out << [Arg] {
switch (Arg) {
case RoundMode::Nearest: return "Nearest";
case RoundMode::NegInfinity: return "-Inf";
case RoundMode::PosInfinity: return "+Inf";
case RoundMode::TowardsZero: return "Towards Zero";
case RoundMode::Host: return "Host";
}
return "<Unknown Round Type>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::SyscallFlags Arg) {
switch (Arg) {
case FEXCore::IR::SyscallFlags::DEFAULT: *out << "Default"; break;
case FEXCore::IR::SyscallFlags::OPTIMIZETHROUGH: *out << "Optimize Through"; break;
case FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY: *out << "No Sync State on Entry"; break;
case FEXCore::IR::SyscallFlags::NORETURN: *out << "No Return"; break;
case FEXCore::IR::SyscallFlags::NOSIDEEFFECTS: *out << "No Side Effects"; break;
default: *out << "<Unknown Round Type>"; break;
}
static void PrintArg(fextl::stringstream* out, const IRListView*, SyscallFlags Arg) {
*out << [Arg] {
switch (Arg) {
case SyscallFlags::DEFAULT: return "Default";
case SyscallFlags::OPTIMIZETHROUGH: return "Optimize Through";
case SyscallFlags::NOSYNCSTATEONENTRY: return "No Sync State on Entry";
case SyscallFlags::NORETURN: return "No Return";
case SyscallFlags::NOSIDEEFFECTS: return "No Side Effects";
case SyscallFlags::NORETURNEDRESULT: return "No Returned Result";
}
return "<Unknown Syscall Flags>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::NamedVectorConstant Arg) {
static void PrintArg(fextl::stringstream* out, const IRListView*, NamedVectorConstant Arg) {
*out << [Arg] {
// clang-format off
switch (Arg) {
@@ -186,6 +191,22 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::N
return "movmskps_shift";
case NamedVectorConstant::NAMED_VECTOR_AESKEYGENASSIST_SWIZZLE:
return "aeskeygenassist_swizzle";
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_0110B:
return "blendps_0110b";
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_0111B:
return "blendps_0111b";
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1001B:
return "blendps_1001b";
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1011B:
return "blendps_1011b";
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1101B:
return "blendps_1101b";
case NamedVectorConstant::NAMED_VECTOR_BLENDPS_1110B:
return "blendps_1110b";
case NamedVectorConstant::NAMED_VECTOR_MOVMASKB:
return "movmaskb";
case NamedVectorConstant::NAMED_VECTOR_MOVMASKB_UPPER:
return "movmaskb_upper";
case NamedVectorConstant::NAMED_VECTOR_ZERO:
return "vectorzero";
case NamedVectorConstant::NAMED_VECTOR_X87_ONE:
@@ -216,9 +237,20 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::N
return "cvtmax_i32";
case NamedVectorConstant::NAMED_VECTOR_CVTMAX_I64:
return "cvtmax_i64";
default:
return "<Unknown Named Vector Constant>";
case NamedVectorConstant::NAMED_VECTOR_F80_SIGN_MASK:
return "f80_sign_mask";
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K0:
return "sha1rnds_k0";
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K1:
return "sha1rnds_k1";
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K2:
return "sha1rnds_k2";
case NamedVectorConstant::NAMED_VECTOR_SHA1RNDS_K3:
return "sha1rnds_k3";
case NamedVectorConstant::NAMED_VECTOR_MAX:
return "<Programming Error: Printing MAX value>";
}
return "<Unknown Named Vector Constant>";
// clang-format on
}();
}
@@ -241,36 +273,43 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, IndexNamedVect
return "dppd_mask";
case IndexNamedVectorConstant::INDEXED_NAMED_VECTOR_PBLENDW:
return "pblendw";
default:
return "<Unknown Indexed Named Vector Constant>";
case INDEXED_NAMED_VECTOR_MAX:
return "<Programming Error: Printing MAX value>";
}
return "<Unknown Indexed Named Vector Constant>";
// clang-format on
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::OpSize Arg) {
switch (Arg) {
case OpSize::i8Bit: *out << "i8"; break;
case OpSize::i16Bit: *out << "i16"; break;
case OpSize::i32Bit: *out << "i32"; break;
case OpSize::i64Bit: *out << "i64"; break;
case OpSize::i128Bit: *out << "i128"; break;
case OpSize::i256Bit: *out << "i256"; break;
case OpSize::f80Bit: *out << "f80"; break;
default: *out << "<Unknown OpSize Type>"; break;
}
static void PrintArg(fextl::stringstream* out, const IRListView*, OpSize Arg) {
*out << [Arg] {
switch (Arg) {
case OpSize::iUnsized: return "Unsized";
case OpSize::i8Bit: return "i8";
case OpSize::i16Bit: return "i16";
case OpSize::i32Bit: return "i32";
case OpSize::i64Bit: return "i64";
case OpSize::f80Bit: return "f80";
case OpSize::i128Bit: return "i128";
case OpSize::i256Bit: return "i256";
case OpSize::iInvalid: return "Invalid";
}
return "<Unknown OpSize Type>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::FloatCompareOp Arg) {
switch (Arg) {
case FloatCompareOp::EQ: *out << "FEQ"; break;
case FloatCompareOp::LT: *out << "FLT"; break;
case FloatCompareOp::LE: *out << "FLE"; break;
case FloatCompareOp::UNO: *out << "UNO"; break;
case FloatCompareOp::NEQ: *out << "NEQ"; break;
case FloatCompareOp::ORD: *out << "ORD"; break;
default: *out << "<Unknown OpSize Type>"; break;
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FloatCompareOp Arg) {
*out << [Arg] {
switch (Arg) {
case FloatCompareOp::EQ: return "FEQ";
case FloatCompareOp::LT: return "FLT";
case FloatCompareOp::LE: return "FLE";
case FloatCompareOp::UNO: return "UNO";
case FloatCompareOp::NEQ: return "NEQ";
case FloatCompareOp::ORD: return "ORD";
}
return "<Unknown FloatCompareOp Type>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::BreakDefinition Arg) {
@@ -280,23 +319,28 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::B
*out << static_cast<uint32_t>(Arg.si_code) << "}";
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::ShiftType Arg) {
switch (Arg) {
case ShiftType::LSL: *out << "LSL"; break;
case ShiftType::LSR: *out << "LSR"; break;
case ShiftType::ASR: *out << "ASR"; break;
case ShiftType::ROR: *out << "ROR"; break;
default: *out << "<Unknown Shift Type>"; break;
}
static void PrintArg(fextl::stringstream* out, const IRListView*, ShiftType Arg) {
*out << [Arg] {
switch (Arg) {
case ShiftType::LSL: return "LSL";
case ShiftType::LSR: return "LSR";
case ShiftType::ASR: return "ASR";
case ShiftType::ROR: return "ROR";
}
return "<Unknown Shift Type>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, FEXCore::IR::BranchHint Arg) {
switch (Arg) {
case BranchHint::None: *out << "None"; break;
case BranchHint::Call: *out << "Call"; break;
case BranchHint::Return: *out << "Return"; break;
default: *out << "<Unknown Branch Hint>"; break;
}
static void PrintArg(fextl::stringstream* out, const IRListView*, BranchHint Arg) {
*out << [Arg] {
switch (Arg) {
case BranchHint::None: return "None";
case BranchHint::Call: return "Call";
case BranchHint::Return: return "Return";
case BranchHint::CheckTF: return "CheckTF";
}
return "<Unknown Branch Hint>";
}();
}
static void PrintArg(fextl::stringstream* out, const IRListView*, const std::array<uint8_t, 0x10>& Arg) {
@@ -353,17 +397,17 @@ void Dump(fextl::stringstream* out, const IRListView* IR) {
auto PhyReg = PhysicalRegister(CodeNode);
if (!PhyReg.IsInvalid()) {
switch (PhyReg.Class) {
case FEXCore::IR::GPRClass.Val: *out << "(r"; break;
case FEXCore::IR::GPRFixedClass.Val: *out << "(R"; break;
case FEXCore::IR::FPRClass.Val: *out << "(v"; break;
case FEXCore::IR::FPRFixedClass.Val: *out << "(V"; break;
case FEXCore::IR::ComplexClass.Val: *out << "(complex"; break;
case FEXCore::IR::InvalidClass.Val: *out << "(invalid"; break;
switch (PhyReg.AsRegClass()) {
case RegClass::GPR: *out << "(r"; break;
case RegClass::GPRFixed: *out << "(R"; break;
case RegClass::FPR: *out << "(v"; break;
case RegClass::FPRFixed: *out << "(V"; break;
case RegClass::Complex: *out << "(complex"; break;
case RegClass::Invalid: *out << "(invalid"; break;
default: *out << "(unknown"; break;
}
if (PhyReg.Class != FEXCore::IR::InvalidClass.Val) {
*out << std::dec << (uint32_t)PhyReg.Reg << ")";
if (PhyReg.AsRegClass() != RegClass::Invalid) {
*out << std::dec << uint32_t(PhyReg.Reg) << ")";
} else {
*out << ")";
}
+7 -7
View File
@@ -33,14 +33,14 @@ bool IsBlockExit(FEXCore::IR::IROps Op) {
}
}
FEXCore::IR::RegisterClassType IREmitter::WalkFindRegClass(Ref Node) {
RegClass IREmitter::WalkFindRegClass(Ref Node) {
auto Class = GetOpRegClass(Node);
switch (Class) {
case GPRClass:
case FPRClass:
case GPRFixedClass:
case FPRFixedClass:
case InvalidClass: return Class;
case RegClass::GPR:
case RegClass::FPR:
case RegClass::GPRFixed:
case RegClass::FPRFixed:
case RegClass::Invalid: return Class;
default: break;
}
@@ -82,7 +82,7 @@ FEXCore::IR::RegisterClassType IREmitter::WalkFindRegClass(Ref Node) {
}
default: LOGMAN_MSG_A_FMT("Unhandled op type: {} {} in argument class validation", ToUnderlying(IROp->Op), GetOpName(Node)); break;
}
return InvalidClass;
return RegClass::Invalid;
}
void IREmitter::ResetWorkingList() {
+68 -18
View File
@@ -46,12 +46,12 @@ public:
*
* @{ */
FEXCore::IR::RegisterClassType WalkFindRegClass(Ref Node);
RegClass WalkFindRegClass(Ref Node);
// These inlining helpers are used by IRDefines.inc so define first.
Ref InlineMem(OpSize Size, Ref Offset, MemOffsetType OffsetType, uint8_t& OffsetScale, bool TSO = false) {
uint64_t Imm {};
if (OffsetType != MEM_OFFSET_SXTX || !IsValueConstant(WrapNode(Offset), &Imm)) {
if (OffsetType != MemOffsetType::SXTX || !IsValueConstant(WrapNode(Offset), &Imm)) {
return Offset;
}
@@ -108,36 +108,86 @@ public:
IRPair<IROp_Jump> _Jump() {
return _Jump(InvalidNode);
}
IRPair<IROp_CondJump> _CondJump(Ref ssa0, CondClassType cond = {COND_NEQ}) {
IRPair<IROp_CondJump> _CondJump(Ref ssa0, CondClass cond = CondClass::NEQ) {
return _CondJump(ssa0, _Constant(0), InvalidNode, InvalidNode, cond, GetOpSize(ssa0));
}
IRPair<IROp_CondJump> _CondJump(Ref ssa0, Ref ssa1, Ref ssa2, CondClassType cond = {COND_NEQ}) {
IRPair<IROp_CondJump> _CondJump(Ref ssa0, Ref ssa1, Ref ssa2, CondClass cond = CondClass::NEQ) {
return _CondJump(ssa0, _Constant(0), ssa1, ssa2, cond, GetOpSize(ssa0));
}
// TODO: Work to remove this implicit sized Select implementation.
IRPair<IROp_Select> _Select(uint8_t Cond, Ref ssa0, Ref ssa1, Ref ssa2, Ref ssa3, IR::OpSize CompareSize = OpSize::iUnsized) {
if (CompareSize == OpSize::iUnsized) {
CompareSize = std::max(OpSize::i32Bit, std::max(GetOpSize(ssa0), GetOpSize(ssa1)));
}
return _Select(std::max(OpSize::i32Bit, std::max(GetOpSize(ssa2), GetOpSize(ssa3))), CompareSize, CondClassType {Cond}, ssa0, ssa1, ssa2, ssa3);
IRPair<IROp_LoadContext> _LoadContextGPR(OpSize ByteSize, uint32_t Offset) {
return _LoadContext(ByteSize, RegClass::GPR, Offset);
}
IRPair<IROp_LoadMem> _LoadMem(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, Ref ssa0, IR::OpSize Align = OpSize::i8Bit) {
return _LoadMem(Class, Size, ssa0, Invalid(), Align, MEM_OFFSET_SXTX, 1);
IRPair<IROp_LoadContext> _LoadContextFPR(OpSize ByteSize, uint32_t Offset) {
return _LoadContext(ByteSize, RegClass::FPR, Offset);
}
IRPair<IROp_StoreMem> _StoreMem(FEXCore::IR::RegisterClassType Class, IR::OpSize Size, Ref Addr, Ref Value, IR::OpSize Align = OpSize::i8Bit) {
return _StoreMem(Class, Size, Value, Addr, Invalid(), Align, MEM_OFFSET_SXTX, 1);
IRPair<IROp_StoreContext> _StoreContextGPR(OpSize ByteSize, Ref Value, uint32_t Offset) {
return _StoreContext(ByteSize, RegClass::GPR, Value, Offset);
}
IRPair<IROp_StoreContext> _StoreContextFPR(OpSize ByteSize, Ref Value, uint32_t Offset) {
return _StoreContext(ByteSize, RegClass::FPR, Value, Offset);
}
IRPair<IROp_Select> Select01(FEXCore::IR::OpSize CompareSize, CondClassType Cond, OrderedNode* Cmp1, OrderedNode* Cmp2) {
IRPair<IROp_LoadContextIndexed> _LoadContextGPRIndexed(Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
return _LoadContextIndexed(Index, ByteSize, BaseOffset, Stride, RegClass::GPR);
}
IRPair<IROp_LoadContextIndexed> _LoadContextFPRIndexed(Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
return _LoadContextIndexed(Index, ByteSize, BaseOffset, Stride, RegClass::FPR);
}
IRPair<IROp_StoreContextIndexed> _StoreContextGPRIndexed(Ref Value, Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
return _StoreContextIndexed(Value, Index, ByteSize, BaseOffset, Stride, RegClass::GPR);
}
IRPair<IROp_StoreContextIndexed> _StoreContextFPRIndexed(Ref Value, Ref Index, OpSize ByteSize, uint32_t BaseOffset, uint32_t Stride) {
return _StoreContextIndexed(Value, Index, ByteSize, BaseOffset, Stride, RegClass::FPR);
}
IRPair<IROp_LoadMem> _LoadMem(RegClass Class, OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
return _LoadMem(Class, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
}
IRPair<IROp_LoadMem> _LoadMemGPR(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
return _LoadMem(RegClass::GPR, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
}
IRPair<IROp_LoadMem> _LoadMemGPR(OpSize Size, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
return _LoadMem(RegClass::GPR, Size, Addr, Offset, Align, OffsetType, OffsetScale);
}
IRPair<IROp_LoadMem> _LoadMemFPR(OpSize Size, Ref ssa0, OpSize Align = OpSize::i8Bit) {
return _LoadMem(RegClass::FPR, Size, ssa0, Invalid(), Align, MemOffsetType::SXTX, 1);
}
IRPair<IROp_LoadMem> _LoadMemFPR(OpSize Size, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
return _LoadMem(RegClass::FPR, Size, Addr, Offset, Align, OffsetType, OffsetScale);
}
IRPair<IROp_StoreMem> _StoreMem(RegClass Class, OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
return _StoreMem(Class, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
}
IRPair<IROp_StoreMem> _StoreMemGPR(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
return _StoreMem(RegClass::GPR, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
}
IRPair<IROp_StoreMem> _StoreMemGPR(OpSize Size, Ref Value, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
return _StoreMem(RegClass::GPR, Size, Value, Addr, Offset, Align, OffsetType, OffsetScale);
}
IRPair<IROp_StoreMem> _StoreMemFPR(OpSize Size, Ref Addr, Ref Value, OpSize Align = OpSize::i8Bit) {
return _StoreMem(RegClass::FPR, Size, Value, Addr, Invalid(), Align, MemOffsetType::SXTX, 1);
}
IRPair<IROp_StoreMem> _StoreMemFPR(OpSize Size, Ref Value, Ref Addr, Ref Offset, OpSize Align, MemOffsetType OffsetType, uint8_t OffsetScale) {
return _StoreMem(RegClass::FPR, Size, Value, Addr, Offset, Align, OffsetType, OffsetScale);
}
IRPair<IROp_StoreMemPair> _StoreMemPairGPR(OpSize Size, Ref Value1, Ref Value2, Ref Addr, uint32_t Offset) {
return _StoreMemPair(RegClass::GPR, Size, Value1, Value2, Addr, Offset);
}
IRPair<IROp_StoreMemPair> _StoreMemPairFPR(OpSize Size, Ref Value1, Ref Value2, Ref Addr, uint32_t Offset) {
return _StoreMemPair(RegClass::FPR, Size, Value1, Value2, Addr, Offset);
}
IRPair<IROp_Select> Select01(FEXCore::IR::OpSize CompareSize, CondClass Cond, OrderedNode* Cmp1, OrderedNode* Cmp2) {
return _Select(OpSize::i64Bit, CompareSize, Cond, Cmp1, Cmp2, _InlineConstant(1), _InlineConstant(0));
}
IRPair<IROp_Select> To01(FEXCore::IR::OpSize CompareSize, OrderedNode* Cmp1) {
return Select01(CompareSize, CondClassType {COND_NEQ}, Cmp1, Constant(0));
return Select01(CompareSize, CondClass::NEQ, Cmp1, Constant(0));
}
IRPair<IROp_NZCVSelect> _NZCVSelect01(CondClassType Cond) {
IRPair<IROp_NZCVSelect> _NZCVSelect01(CondClass Cond) {
return _NZCVSelect(OpSize::i64Bit, Cond, _InlineConstant(1), _InlineConstant(0));
}
@@ -250,7 +300,7 @@ public:
}
/** @} */
FEXCore::IR::RegisterClassType WalkFindRegClass(OrderedNodeWrapper ssa) {
RegClass WalkFindRegClass(OrderedNodeWrapper ssa) {
Ref RealNode = ssa.GetNode(DualListData.ListBegin());
return WalkFindRegClass(RealNode);
}
@@ -93,11 +93,11 @@ void IRValidation::Run(IREmitter* IREmit) {
// After RA, the destination needs to be assigned a register and class
auto PhyReg = PhysicalRegister(CodeNode);
FEXCore::IR::RegisterClassType ExpectedClass = IR::GetRegClass(IROp->Op);
FEXCore::IR::RegisterClassType AssignedClass = FEXCore::IR::RegisterClassType {PhyReg.Class};
const auto ExpectedClass = IR::GetRegClass(IROp->Op);
const auto AssignedClass = PhyReg.AsRegClass();
// If no register class was assigned
if (AssignedClass == IR::InvalidClass) {
if (AssignedClass == IR::RegClass::Invalid) {
HadError |= true;
Errors << "%" << ID << ": Had destination but with no register class assigned" << std::endl;
}
@@ -109,10 +109,10 @@ void IRValidation::Run(IREmitter* IREmit) {
}
// Assigned class wasn't the expected class and it is a non-complex op
if (AssignedClass != ExpectedClass && ExpectedClass != IR::ComplexClass) {
if (AssignedClass != ExpectedClass && ExpectedClass != IR::RegClass::Complex) {
HadWarning |= true;
Warnings << "%" << ID << ": Destination had register class " << AssignedClass.Val << " When register class "
<< ExpectedClass.Val << " Was expected" << std::endl;
Warnings << "%" << ID << ": Destination had register class " << uint32_t(AssignedClass) << " When register class "
<< uint32_t(ExpectedClass) << " Was expected" << std::endl;
}
}
}
@@ -2,22 +2,21 @@
/*
$info$
tags: ir|opts
desc: This is not used right now, possibly broken
$end_info$
*/
#include "FEXCore/Core/X86Enums.h"
#include "FEXCore/Utils/CompilerDefs.h"
#include "FEXCore/Utils/MathUtils.h"
#include "FEXCore/fextl/deque.h"
#include "Interface/IR/IR.h"
#include "Interface/IR/IREmitter.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/Profiler.h>
#include "Interface/IR/PassManager.h"
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/fextl/deque.h>
#include <FEXCore/fextl/vector.h>
// Flag bit flags
#define FLAG_V (1U << 0)
#define FLAG_C (1U << 1)
@@ -62,36 +61,36 @@ struct FlagInfo {
return {.Raw = R};
}
bool Trivial() {
bool Trivial() const {
return Raw == 0;
}
unsigned Read() {
unsigned Read() const {
return Bits(0, 8);
}
unsigned Write() {
unsigned Write() const {
return Bits(8, 8);
}
bool CanEliminate() {
bool CanEliminate() const {
return Bits(16, 1);
}
bool Special() {
bool Special() const {
return Bits(63, 1);
}
IROps Replacement() {
IROps Replacement() const {
return (IROps)Bits(32, 16);
}
IROps ReplacementNoWrite() {
IROps ReplacementNoWrite() const {
return (IROps)Bits(48, 16);
}
private:
unsigned Bits(unsigned Start, unsigned Count) {
unsigned Bits(unsigned Start, unsigned Count) const {
return (Raw >> Start) & ((1u << Count) - 1);
}
};
@@ -154,45 +153,44 @@ public:
private:
FlagInfo Classify(IROp_Header* Node);
unsigned FlagForReg(unsigned Reg);
unsigned FlagsForCondClassType(CondClassType Cond);
unsigned FlagsForCondClassType(CondClass Cond);
bool EliminateDeadCode(IREmitter* IREmit, Ref CodeNode, IROp_Header* IROp);
void FoldBranch(IREmitter* IREmit, IRListView& CurrentIR, IROp_CondJump* Op, Ref CodeNode);
CondClassType X86ToArmFloatCond(CondClassType X86);
CondClass X86ToArmFloatCond(CondClass X86);
bool ProcessBlock(IREmitter* IREmit, IRListView& CurrentIR, Ref Block, ControlFlowGraph& CFG);
void OptimizeParity(IREmitter* IREmit, IRListView& CurrentIR, ControlFlowGraph& CFG);
};
unsigned DeadFlagCalculationEliminination::FlagsForCondClassType(CondClassType Cond) {
unsigned DeadFlagCalculationEliminination::FlagsForCondClassType(CondClass Cond) {
switch (Cond) {
case COND_AL: return 0;
case CondClass::AL: return 0;
case COND_MI:
case COND_PL: return FLAG_N;
case CondClass::MI:
case CondClass::PL: return FLAG_N;
case COND_EQ:
case COND_NEQ: return FLAG_Z;
case CondClass::EQ:
case CondClass::NEQ: return FLAG_Z;
case COND_UGE:
case COND_ULT: return FLAG_C;
case CondClass::UGE:
case CondClass::ULT: return FLAG_C;
case COND_VS:
case COND_VC:
case COND_FU:
case COND_FNU: return FLAG_V;
case CondClass::VS:
case CondClass::VC:
case CondClass::FU:
case CondClass::FNU: return FLAG_V;
case COND_UGT:
case COND_ULE: return FLAG_Z | FLAG_C;
case CondClass::UGT:
case CondClass::ULE: return FLAG_Z | FLAG_C;
case COND_SGE:
case COND_SLT:
case COND_FLU:
case COND_FGE: return FLAG_N | FLAG_V;
case CondClass::SGE:
case CondClass::SLT:
case CondClass::FLU:
case CondClass::FGE: return FLAG_N | FLAG_V;
case COND_SGT:
case COND_SLE:
case COND_FLEU:
case COND_FGT: return FLAG_N | FLAG_Z | FLAG_V;
case CondClass::SGT:
case CondClass::SLE:
case CondClass::FLEU:
case CondClass::FGT: return FLAG_N | FLAG_Z | FLAG_V;
default: LOGMAN_THROW_A_FMT(false, "unknown cond class type"); return FLAG_NZCV;
}
@@ -456,7 +454,7 @@ bool DeadFlagCalculationEliminination::EliminateDeadCode(IREmitter* IREmit, Ref
return true;
}
CondClassType DeadFlagCalculationEliminination::X86ToArmFloatCond(CondClassType X86) {
CondClass DeadFlagCalculationEliminination::X86ToArmFloatCond(CondClass X86) {
// Table of x86 condition codes that map to arm64 condition codes, in the
// sense that fcmp+axflag+branch(x86) is equivalent to fcmp+branch(arm).
//
@@ -465,12 +463,12 @@ CondClassType DeadFlagCalculationEliminination::X86ToArmFloatCond(CondClassType
//
// SF/OF conditions are trivial and therefore shouldn't actually be generated
switch (X86) {
case COND_UGE /* A */: return {COND_FGE} /* GE */;
case COND_UGT /* AE */: return {COND_FGT} /* GT */;
case COND_ULT /* B */: return {COND_SLT} /* LT */;
case COND_ULE /* BE */: return {COND_SLE} /* LE */;
case COND_SLE /* LE */: return {COND_SLE} /* LE */;
default: return {COND_AL};
case CondClass::UGE /* A */: return CondClass::FGE /* GE */;
case CondClass::UGT /* AE */: return CondClass::FGT /* GT */;
case CondClass::ULT /* B */: return CondClass::SLT /* LT */;
case CondClass::ULE /* BE */: return CondClass::SLE /* LE */;
case CondClass::SLE /* LE */: return CondClass::SLE /* LE */;
default: return CondClass::AL;
}
}
@@ -485,8 +483,8 @@ void DeadFlagCalculationEliminination::FoldBranch(IREmitter* IREmit, IRListView&
auto Prev = CurrentIR.GetOp<IR::IROp_Header>(PrevWrap);
if (Prev->Op == OP_AXFLAG) {
// Pattern match a branch fed by AXFLAG.
CondClassType ArmCond = X86ToArmFloatCond(Op->Cond);
if (ArmCond == COND_AL) {
CondClass ArmCond = X86ToArmFloatCond(Op->Cond);
if (ArmCond == CondClass::AL) {
return;
}
@@ -495,7 +493,7 @@ void DeadFlagCalculationEliminination::FoldBranch(IREmitter* IREmit, IRListView&
// Pattern match a branch fed by a compare. We could also handle bit tests
// here, but tbz/tbnz has a limited offset range which we don't have a way to
// deal with yet. Let's hope that's not a big deal.
if (!(Op->Cond == COND_NEQ || Op->Cond == COND_EQ) || (Prev->Size < OpSize::i32Bit)) {
if (!(Op->Cond == CondClass::NEQ || Op->Cond == CondClass::EQ) || (Prev->Size < OpSize::i32Bit)) {
return;
}
@@ -629,9 +627,9 @@ void DeadFlagCalculationEliminination::OptimizeParity(IREmitter* IREmit, IRListV
}
for (auto [Block, BlockHeader] : CurrentIR.GetBlocks()) {
auto ID = BlockHeader->C<IROp_CodeBlock>()->ID;
const auto ID = BlockHeader->C<IROp_CodeBlock>()->ID;
const auto& Predecessors = CFG.Get(ID)->Predecessors;
bool Full = false;
auto Predecessors = CFG.Get(ID)->Predecessors;
if (Predecessors.empty()) {
// Conservatively assume there was full parity before the start block
@@ -12,6 +12,7 @@ $end_info$
#include "Interface/IR/Passes.h"
#include "Interface/Core/CPUID.h"
#include <FEXCore/IR/IR.h>
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/fextl/vector.h>
@@ -22,7 +23,7 @@ using namespace FEXCore;
namespace FEXCore::IR {
namespace {
struct RegisterClass {
struct RegisterClassData {
uint32_t Available;
uint32_t Count;
@@ -32,9 +33,9 @@ namespace {
Ref RegToSSA[32];
};
IR::RegisterClassType GetRegClassFromNode(IR::IRListView* IR, IR::IROp_Header* IROp) {
IR::RegisterClassType Class = IR::GetRegClass(IROp->Op);
if (Class != IR::ComplexClass) {
IR::RegClass GetRegClassFromNode(IR::IRListView* IR, IR::IROp_Header* IROp) {
const auto Class = IR::GetRegClass(IROp->Op);
if (Class != IR::RegClass::Complex) {
return Class;
}
@@ -46,7 +47,7 @@ namespace {
case IR::OP_LOADMEM:
case IR::OP_LOADMEMTSO: return IROp->C<IR::IROp_LoadMem>()->Class;
case IR::OP_FILLREGISTER: return IROp->C<IR::IROp_FillRegister>()->Class;
default: return IR::InvalidClass;
default: return IR::RegClass::Invalid;
}
};
} // Anonymous namespace
@@ -56,11 +57,11 @@ public:
explicit ConstrainedRAPass(const FEXCore::CPUIDEmu* CPUID)
: CPUID {CPUID} {}
void Run(IREmitter* IREmit) override;
void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override;
void AddRegisters(IR::RegClass Class, uint32_t RegisterCount) override;
bool TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp);
private:
RegisterClass Classes[IR::NumClasses];
RegisterClassData Classes[IR::NumClasses];
IREmitter* IREmit;
IRListView* IR;
@@ -101,7 +102,7 @@ private:
uint32_t SlotPlusOne = SpillSlots[IR->GetID(Node).Value];
LOGMAN_THROW_A_FMT(SlotPlusOne >= 1, "Node must have been spilled");
RegisterClassType RegClass = GetRegClassFromNode(IR, IROp);
const auto RegClass = GetRegClassFromNode(IR, IROp);
return IREmit->_FillRegister(IROp->Size, IROp->ElementSize, SlotPlusOne - 1, RegClass);
};
@@ -120,7 +121,7 @@ private:
return Op != OP_INLINECONSTANT && Op != OP_INLINEENTRYPOINTOFFSET;
};
RegisterClass* GetClass(PhysicalRegister Reg) {
RegisterClassData* GetClass(PhysicalRegister Reg) {
return &Classes[Reg.Class];
};
@@ -133,13 +134,13 @@ private:
LOGMAN_THROW_A_FMT(ID < SSAToReg.size(), "Only old nodes looked up");
PhysicalRegister Reg = SSAToReg[ID];
RegisterClass* Class = GetClass(Reg);
RegisterClassData* Class = GetClass(Reg);
return (Class->Available & GetRegBits(Reg)) == 0 && Class->RegToSSA[Reg.Reg] == Node;
};
void FreeReg(PhysicalRegister Reg) {
RegisterClass* Class = GetClass(Reg);
RegisterClassData* Class = GetClass(Reg);
uint32_t RegBits = GetRegBits(Reg);
LOGMAN_THROW_A_FMT(!(Class->Available & RegBits), "Register double-free");
@@ -187,22 +188,22 @@ private:
};
PhysicalRegister DecodeSRAReg(const IROp_Header* IROp, Ref Node) {
uint8_t FlagOffset = Classes[GPRFixedClass.Val].Count - 2;
uint8_t FlagOffset = Classes[FEXCore::ToUnderlying(RegClass::GPRFixed)].Count - 2;
if (IROp->Op == OP_STOREREGISTER) {
return PhysicalRegister(Node);
} else if (IROp->Op == OP_LOADPF || IROp->Op == OP_STOREPF) {
return PhysicalRegister {GPRFixedClass, FlagOffset};
return PhysicalRegister {RegClass::GPRFixed, FlagOffset};
} else if (IROp->Op == OP_LOADAF || IROp->Op == OP_STOREAF) {
return PhysicalRegister {GPRFixedClass, (uint8_t)(FlagOffset + 1)};
return PhysicalRegister {RegClass::GPRFixed, uint8_t(FlagOffset + 1)};
} else {
const IROp_LoadRegister* Op = IROp->C<IR::IROp_LoadRegister>();
LOGMAN_THROW_A_FMT(Op->Class == GPRClass || Op->Class == FPRClass, "SRA classes");
if (Op->Class == FPRClass) {
return PhysicalRegister {FPRFixedClass, (uint8_t)Op->Reg};
LOGMAN_THROW_A_FMT(Op->Class == RegClass::GPR || Op->Class == RegClass::FPR, "SRA classes");
if (Op->Class == RegClass::FPR) {
return PhysicalRegister {RegClass::FPRFixed, uint8_t(Op->Reg)};
} else {
return PhysicalRegister {GPRFixedClass, (uint8_t)Op->Reg};
return PhysicalRegister {RegClass::GPRFixed, uint8_t(Op->Reg)};
}
}
};
@@ -267,7 +268,7 @@ private:
SourceIndex = SourcesNextUses.size();
}
void SpillReg(RegisterClass* Class, IROp_CodeBlock* Block, IROp_Header* Exclude) {
void SpillReg(RegisterClassData* Class, IROp_CodeBlock* Block, IROp_Header* Exclude) {
// We're about to use next-use information, so calculate it.
if (!AnySpilled) {
CalculateNextUses(Block, Exclude);
@@ -318,7 +319,7 @@ private:
// If we already spilled the Candidate, we don't need to spill again.
// Similarly, if we can rematerialize the instruction, we don't spill it.
if (!Spilled && Header->Op != OP_CONSTANT) {
LOGMAN_THROW_A_FMT(Reg.Class == GetRegClassFromNode(IR, Header), "Consistent");
LOGMAN_THROW_A_FMT(Reg.AsRegClass() == GetRegClassFromNode(IR, Header), "Consistent");
// SpillSlots allocation is deferred.
if (SpillSlots.empty()) {
@@ -329,7 +330,7 @@ private:
uint32_t Slot = IR->GetHeader()->SpillSlots++;
// We must map here in case we're spilling something we shuffled.
auto SpillOp = IREmit->_SpillRegister(OrderedNodeWrapper::FromImmediate(Reg.Raw), Slot, RegisterClassType {Reg.Class});
auto SpillOp = IREmit->_SpillRegister(OrderedNodeWrapper::FromImmediate(Reg.Raw), Slot, Reg.AsRegClass());
SpillOp.first->Header.Size = Header->Size;
SpillOp.first->Header.ElementSize = Header->ElementSize;
SpillSlots[Value] = Slot + 1;
@@ -341,7 +342,7 @@ private:
};
void RemapReg(Ref Node, PhysicalRegister Reg) {
RegisterClass* Class = GetClass(Reg);
RegisterClassData* Class = GetClass(Reg);
Class->RegToSSA[Reg.Reg] = Node;
uint32_t Index = IR->GetID(Node).Value;
@@ -352,7 +353,7 @@ private:
// Record a given assignment of register Reg to Node.
void SetReg(Ref Node, PhysicalRegister Reg) {
RegisterClass* Class = GetClass(Reg);
RegisterClassData* Class = GetClass(Reg);
uint32_t RegBits = GetRegBits(Reg);
LOGMAN_THROW_A_FMT((Class->Available & RegBits) == RegBits, "Precondition");
@@ -370,7 +371,7 @@ private:
// Prioritize preferred registers.
if (Node < PreferredReg.size()) {
if (PhysicalRegister Reg = PreferredReg[Node]; !Reg.IsInvalid()) {
RegisterClass* Class = GetClass(Reg);
RegisterClassData* Class = GetClass(Reg);
uint32_t RegBits = GetRegBits(Reg);
if ((Class->Available & RegBits) == RegBits) {
@@ -383,10 +384,10 @@ private:
// Try to handle tied registers. This can fail, the JIT will insert moves.
if (int TiedIdx = IR::TiedSource(IROp->Op); TiedIdx >= 0) {
auto Reg = PhysicalRegister(IROp->Args[TiedIdx]);
RegisterClass* Class = GetClass(Reg);
RegisterClassData* Class = GetClass(Reg);
uint32_t RegBits = GetRegBits(Reg);
if (Reg.Class != GPRFixedClass && Reg.Class != FPRFixedClass && (Class->Available & RegBits) == RegBits) {
if (Reg.AsRegClass() != RegClass::GPRFixed && Reg.AsRegClass() != RegClass::FPRFixed && (Class->Available & RegBits) == RegBits) {
SetReg(CodeNode, Reg);
return;
}
@@ -394,7 +395,7 @@ private:
// Try to coalesce reserved pairs. Just a heuristic to remove some moves.
if (IROp->Op == OP_ALLOCATEGPR && IROp->C<IROp_AllocateGPR>()->ForPair) {
uint32_t Available = Classes[GPRClass].Available;
uint32_t Available = Classes[FEXCore::ToUnderlying(RegClass::GPR)].Available;
// Only choose base register R if R and R + 1 are both free
Available &= (Available >> 1);
@@ -405,20 +406,20 @@ private:
if (Available) {
unsigned Reg = std::countr_zero(Available);
SetReg(CodeNode, PhysicalRegister(GPRClass, Reg));
SetReg(CodeNode, PhysicalRegister(RegClass::GPR, Reg));
return;
}
} else if (IROp->Op == OP_ALLOCATEGPRAFTER) {
uint32_t Available = Classes[GPRClass].Available;
uint32_t Available = Classes[FEXCore::ToUnderlying(RegClass::GPR)].Available;
auto After = PhysicalRegister(IROp->Args[0]);
if ((After.Reg & 1) == 0 && Available & (1ull << (After.Reg + 1))) {
SetReg(CodeNode, PhysicalRegister(GPRClass, After.Reg + 1));
SetReg(CodeNode, PhysicalRegister(RegClass::GPR, After.Reg + 1));
return;
}
}
RegisterClassType ClassType = GetRegClassFromNode(IR, IROp);
RegisterClass* Class = &Classes[ClassType];
RegClass ClassType = GetRegClassFromNode(IR, IROp);
RegisterClassData* Class = &Classes[FEXCore::ToUnderlying(ClassType)];
// Spill to make room in the register file.
if (!Class->Available) {
@@ -433,10 +434,10 @@ private:
};
};
void ConstrainedRAPass::AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) {
void ConstrainedRAPass::AddRegisters(IR::RegClass Class, uint32_t RegisterCount) {
LOGMAN_THROW_A_FMT(RegisterCount <= 31, "Up to 31 regs supported");
Classes[Class].Count = RegisterCount;
Classes[FEXCore::ToUnderlying(Class)].Count = RegisterCount;
}
inline bool KillMove(IROp_Header* LastOp, IROp_Header* IROp, Ref LastNode, Ref CodeNode) {
@@ -530,7 +531,7 @@ bool ConstrainedRAPass::TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header*
const auto Result = CPUID->RunFunction(ConstantFunction, 0 /* leaf */);
IREmit->SetWriteCursorBefore(CodeNode);
IREmit->_Fence({FEXCore::IR::Fence_Inst});
IREmit->_Fence(IR::FenceType::Inst);
IREmit->_Constant(Result.eax).Node->Reg = PhysicalRegister(Op->OutEAX).Raw;
IREmit->_Constant(Result.ebx).Node->Reg = PhysicalRegister(Op->OutEBX).Raw;
IREmit->_Constant(Result.ecx).Node->Reg = PhysicalRegister(Op->OutECX).Raw;
@@ -663,7 +664,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
// Static registers must be consistent at SRA load/store. Evict to ensure.
if (auto Node = DecodeSRANode(IROp, CodeNode); Node != nullptr) {
auto Reg = DecodeSRAReg(IROp, CodeNode);
RegisterClass* Class = &Classes[Reg.Class];
RegisterClassData* Class = &Classes[Reg.Class];
if (!(Class->Available & (1u << Reg.Reg))) {
Ref Old = Class->RegToSSA[Reg.Reg];
@@ -678,7 +679,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
Ref Copy;
if (Reg.Class == FPRFixedClass) {
if (Reg.AsRegClass() == RegClass::FPRFixed) {
IROp_Header* Header = IR->GetOp<IROp_Header>(Old);
Copy = IREmit->_VMov(Header->Size, OrderedNodeWrapper::FromImmediate(Reg.Raw));
} else {
@@ -12,11 +12,11 @@ $end_info$
#include <stdint.h>
namespace FEXCore::IR {
struct RegisterClassType;
enum class RegClass : uint32_t;
class RegisterAllocationPass : public FEXCore::IR::Pass {
public:
virtual void AddRegisters(FEXCore::IR::RegisterClassType Class, uint32_t RegisterCount) = 0;
virtual void AddRegisters(RegClass Class, uint32_t RegisterCount) = 0;
// Number of GPRs usable for pairs at start of GPR set. Must be even.
uint32_t PairRegs;
@@ -158,7 +158,9 @@ public:
: Features(Features)
, GPROpSize(GPROpSize) {
FEX_CONFIG_OPT(ReducedPrecision, X87REDUCEDPRECISION);
FEX_CONFIG_OPT(StrictReducedPrecision, X87STRICTREDUCEDPRECISION);
ReducedPrecisionMode = ReducedPrecision;
StrictReducedPrecisionMode = StrictReducedPrecision;
}
void Run(IREmitter* Emit) override;
@@ -166,10 +168,12 @@ private:
const FEXCore::HostFeatures& Features;
const OpSize GPROpSize;
bool ReducedPrecisionMode;
bool StrictReducedPrecisionMode;
FEX_CONFIG_OPT(DisableVixlIndirectCalls, DISABLE_VIXL_INDIRECT_RUNTIME_CALLS);
// Helpers
Ref RotateRight8(uint32_t V, Ref Amount);
Ref SilenceNaN(Ref Value);
void F80SplitStore_Helper(const IROp_StoreStackMem* Op, Ref StackNode) {
Ref AddrNode = IR->GetNode(Op->Addr);
@@ -178,18 +182,18 @@ private:
MemOffsetType OffsetType = Op->OffsetType;
uint8_t OffsetScale = Op->OffsetScale;
IREmit->_StoreMem(FPRClass, OpSize::i64Bit, StackNode, AddrNode, Offset, Align, OffsetType, OffsetScale);
IREmit->_StoreMemFPR(OpSize::i64Bit, StackNode, AddrNode, Offset, Align, OffsetType, OffsetScale);
auto Upper = IREmit->_VExtractToGPR(OpSize::i128Bit, OpSize::i64Bit, StackNode, 1);
// Store the Upper part of the register (the remaining 2 bytes) into memory.
AddressMode A {.Base = AddrNode,
.Index = Op->Offset.IsInvalid() ? nullptr : Offset,
.IndexType = MEM_OFFSET_SXTX,
.IndexScale = OffsetScale,
.Offset = 8,
.IndexType = MemOffsetType::SXTX,
.IndexScale = OffsetScale,
.AddrSize = OpSize::i64Bit};
A = SelectAddressMode(IREmit, A, GPROpSize, Features.SupportsTSOImm9, false, false, OpSize::i16Bit);
IREmit->_StoreMem(GPRClass, OpSize::i16Bit, Upper, A.Base, A.Index, OpSize::i64Bit, MEM_OFFSET_SXTX, A.IndexScale);
IREmit->_StoreMemGPR(OpSize::i16Bit, Upper, A.Base, A.Index, OpSize::i64Bit, MemOffsetType::SXTX, A.IndexScale);
}
void StoreStackMem_Helper(const IROp_StoreStackMem* Op, Ref StackNode) {
@@ -204,7 +208,10 @@ private:
case OpSize::i32Bit:
case OpSize::i64Bit: {
StackNode = IREmit->_F80CVT(Op->StoreSize, StackNode);
IREmit->_StoreMem(FPRClass, Op->StoreSize, StackNode, AddrNode, Offset, Align, OffsetType, OffsetScale);
if (!ReducedPrecisionMode || StrictReducedPrecisionMode) {
StackNode = SilenceNaN(StackNode);
}
IREmit->_StoreMemFPR(Op->StoreSize, StackNode, AddrNode, Offset, Align, OffsetType, OffsetScale);
break;
}
@@ -212,7 +219,7 @@ private:
if (Features.SupportsSVE128 || Features.SupportsSVE256) {
AddressMode A {.Base = AddrNode,
.Index = Op->Offset.IsInvalid() ? nullptr : Offset,
.IndexType = MEM_OFFSET_SXTX,
.IndexType = MemOffsetType::SXTX,
.IndexScale = OffsetScale,
.AddrSize = OpSize::i64Bit};
AddrNode = LoadEffectiveAddress(IREmit, A, GPROpSize, false);
@@ -235,13 +242,17 @@ private:
MemOffsetType OffsetType = Op->OffsetType;
uint8_t OffsetScale = Op->OffsetScale;
if ((!ReducedPrecisionMode || StrictReducedPrecisionMode) && Op->StoreSize != OpSize::f80Bit) {
StackNode = SilenceNaN(StackNode);
}
switch (Op->StoreSize) {
case OpSize::i32Bit: {
StackNode = IREmit->_Float_FToF(OpSize::i32Bit, OpSize::i64Bit, StackNode);
[[fallthrough]];
}
case OpSize::i64Bit: {
IREmit->_StoreMem(FPRClass, Op->StoreSize, StackNode, AddrNode, Offset, Align, OffsetType, OffsetScale);
IREmit->_StoreMemFPR(Op->StoreSize, StackNode, AddrNode, Offset, Align, OffsetType, OffsetScale);
break;
}
@@ -401,8 +412,7 @@ inline void X87StackOptimization::MigrateToSlowPathIf(bool ShouldMigrate) {
inline Ref X87StackOptimization::GetTopWithCache_Slow() {
if (!TopOffsetCache[0]) {
TopOffsetCache[0] =
IREmit->_LoadContext(OpSize::i8Bit, GPRClass, offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
TopOffsetCache[0] = IREmit->_LoadContextGPR(OpSize::i8Bit, offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
}
return TopOffsetCache[0];
}
@@ -447,7 +457,7 @@ inline void X87StackOptimization::SetTopWithCache_Slow(Ref Value) {
inline Ref X87StackOptimization::GetFTW() {
if (!FTWCached) {
FTWCached = IREmit->_LoadContext(OpSize::i8Bit, GPRClass, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
FTWCached = IREmit->_LoadContextGPR(OpSize::i8Bit, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
}
return FTWCached;
}
@@ -470,7 +480,7 @@ inline Ref X87StackOptimization::LoadStackValueAtOffset_Slow(uint8_t Offset) {
OrderedNode* TopOffsetAddress = GetOffsetTopAddressWithCache_Slow(Offset);
auto Size = ReducedPrecisionMode ? OpSize::i64Bit : OpSize::i128Bit;
if (!TopValueCache[Offset]) {
TopValueCache[Offset] = IREmit->_LoadMem(FPRClass, Size, TopOffsetAddress, IREmit->_InlineConstant(MMBaseOffset()), Size, MEM_OFFSET_SXTX, 1);
TopValueCache[Offset] = IREmit->_LoadMemFPR(Size, TopOffsetAddress, IREmit->_InlineConstant(MMBaseOffset()), Size, MemOffsetType::SXTX, 1);
}
return TopValueCache[Offset];
}
@@ -489,6 +499,15 @@ inline Ref X87StackOptimization::RotateRight8(uint32_t V, Ref Amount) {
return IREmit->_Lshr(OpSize::i32Bit, GetConstant(V | (V << 8)), Amount);
}
inline Ref X87StackOptimization::SilenceNaN(Ref Value) {
Ref GPRValue = IREmit->_VExtractToGPR(OpSize::i64Bit, OpSize::i64Bit, Value, 0);
IREmit->_FCmp(OpSize::i64Bit, Value, Value); // Comparison with itself should set VS if nan
Ref QuietNaNGPR = IREmit->_Or(OpSize::i64Bit, GPRValue, IREmit->_Constant(0x0008000000000000ULL));
Ref SilencedValue = IREmit->_VCastFromGPR(OpSize::i64Bit, OpSize::i64Bit, QuietNaNGPR);
return IREmit->_NZCVSelectV(OpSize::i64Bit, CondClass::VS, SilencedValue, Value);
}
inline std::optional<X87StackOptimization::StackMemberInfo> X87StackOptimization::MigrateToSlowPath_IfInvalid(uint8_t Offset) {
const auto& [Valid, StackMember] = StackData.top(Offset);
MigrateToSlowPathIf(Valid != StackSlot::VALID);
@@ -595,7 +614,7 @@ inline void X87StackOptimization::UpdateTopForPush_Slow() {
void X87StackOptimization::FlushCachedRegs() {
if (FlushTopPending) {
IREmit->_StoreContext(OpSize::i8Bit, GPRClass, TopOffsetCache[0], offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
IREmit->_StoreContextGPR(OpSize::i8Bit, TopOffsetCache[0], offsetof(FEXCore::Core::CPUState, flags) + FEXCore::X86State::X87FLAG_TOP_LOC);
FlushTopPending = false;
}
@@ -603,7 +622,7 @@ void X87StackOptimization::FlushCachedRegs() {
for (size_t i = 0; i < FlushValuesPending.size(); i++) {
if (FlushValuesPending[i]) {
OrderedNode* TopOffsetAddress = GetOffsetTopAddressWithCache_Slow(i);
IREmit->_StoreMem(FPRClass, Size, TopValueCache[i], TopOffsetAddress, IREmit->_InlineConstant(MMBaseOffset()), Size, MEM_OFFSET_SXTX, 1);
IREmit->_StoreMemFPR(Size, TopValueCache[i], TopOffsetAddress, IREmit->_InlineConstant(MMBaseOffset()), Size, MemOffsetType::SXTX, 1);
// store
FlushValuesPending[i] = false;
}
@@ -654,7 +673,7 @@ void X87StackOptimization::FlushCachedRegs() {
}
}();
IREmit->_StoreContext(OpSize::i8Bit, GPRClass, NewFTW, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
IREmit->_StoreContextGPR(OpSize::i8Bit, NewFTW, offsetof(FEXCore::Core::CPUState, AbridgedFTW));
FTWCached = NewFTW;
}
@@ -995,8 +1014,8 @@ void X87StackOptimization::Run(IREmitter* Emit) {
// or similar. As long as the source size and dest size are one and the same.
// This will avoid any conversions between source and stack element size and conversion back.
if (!SlowPath && Value->Source && Value->Source->Size == Op->StoreSize && Value->InterpretAsFloat) {
IREmit->_StoreMem(Value->InterpretAsFloat ? FPRClass : GPRClass, Op->StoreSize, Value->Source->Node, AddrNode, Offset, Align,
OffsetType, OffsetScale);
const auto ClassType = Value->InterpretAsFloat ? RegClass::FPR : RegClass::GPR;
IREmit->_StoreMem(ClassType, Op->StoreSize, Value->Source->Node, AddrNode, Offset, Align, OffsetType, OffsetScale);
break;
}
@@ -1160,7 +1179,7 @@ void X87StackOptimization::Run(IREmitter* Emit) {
Ref Value {};
if (ReducedPrecisionMode) {
Value = IREmit->_Vector_FToI(OpSize::i64Bit, OpSize::i64Bit, St0, Round_Host);
Value = IREmit->_Vector_FToI(OpSize::i64Bit, OpSize::i64Bit, St0, RoundMode::Host);
} else {
Value = IREmit->_F80Round(St0);
}
@@ -19,9 +19,9 @@ union PhysicalRegister {
return Raw == Other.Raw;
}
PhysicalRegister(RegisterClassType Class, uint8_t Reg)
PhysicalRegister(RegClass Class, uint8_t Reg)
: Reg(Reg)
, Class(Class.Val) {}
, Class(uint8_t(Class)) {}
PhysicalRegister(OrderedNodeWrapper Arg)
: Raw(Arg.GetImmediate()) {}
@@ -29,12 +29,16 @@ union PhysicalRegister {
PhysicalRegister(Ref Node)
: Raw(Node->Reg) {}
RegClass AsRegClass() const {
return RegClass {Class};
}
static const PhysicalRegister Invalid() {
return PhysicalRegister(InvalidClass, 0);
return PhysicalRegister(RegClass::Invalid, 0);
}
bool IsInvalid() const {
static_assert(InvalidClass == 0);
static_assert(uint8_t(RegClass::Invalid) == 0);
return Raw == 0;
}
};
+5
View File
@@ -4,6 +4,7 @@
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/PrctlUtils.h>
#include <FEXCore/Utils/TypeDefines.h>
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/memory.h>
@@ -50,6 +51,10 @@ void* FEX_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off
errno = -(uint64_t)Result;
return (void*)-1;
}
if (flags & MAP_ANONYMOUS) {
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Result, length, "FEXMem");
}
return Result;
}
int FEX_munmap(void* addr, size_t length) {
-119
View File
@@ -1,119 +0,0 @@
// SPDX-License-Identifier: MIT
#include <FEXCore/Utils/LongJump.h>
namespace FEXCore::LongJump {
#if defined(_M_ARM_64)
[[nodiscard]]
FEX_DEFAULT_VISIBILITY FEX_NAKED uint64_t SetJump(JumpBuf& Buffer) {
__asm volatile(R"(
// x0 contains the jumpbuffer
stp x19, x20, [x0, #( 0 * 8)];
stp x21, x22, [x0, #( 2 * 8)];
stp x23, x24, [x0, #( 4 * 8)];
stp x25, x26, [x0, #( 6 * 8)];
stp x27, x28, [x0, #( 8 * 8)];
stp x29, x30, [x0, #(10 * 8)];
// FPRs
stp d8, d9, [x0, #(12 * 8)];
stp d10, d11, [x0, #(14 * 8)];
stp d12, d13, [x0, #(16 * 8)];
stp d14, d15, [x0, #(18 * 8)];
// Move SP in to a temporary to store.
mov x1, sp;
str x1, [x0, #(20 * 8)];
// Return zero to signify this is the SetJump.
mov x0, #0;
ret;
)" ::
: "memory");
}
[[noreturn]]
FEX_DEFAULT_VISIBILITY FEX_NAKED void LongJump(JumpBuf& Buffer, uint64_t Value) {
__asm volatile(R"(
// x0 contains the jumpbuffer
ldp x19, x20, [x0, #( 0 * 8)];
ldp x21, x22, [x0, #( 2 * 8)];
ldp x23, x24, [x0, #( 4 * 8)];
ldp x25, x26, [x0, #( 6 * 8)];
ldp x27, x28, [x0, #( 8 * 8)];
ldp x29, x30, [x0, #(10 * 8)];
// FPRs
ldp d8, d9, [x0, #(12 * 8)];
ldp d10, d11, [x0, #(14 * 8)];
ldp d12, d13, [x0, #(16 * 8)];
ldp d14, d15, [x0, #(18 * 8)];
// Load SP in to temporary then move
ldr x0, [x0, #(20 * 8)];
mov sp, x0;
// Move value in to result register
mov x0, x1;
ret;
)" ::
: "memory");
}
#else
[[nodiscard]]
FEX_DEFAULT_VISIBILITY FEX_NAKED uint64_t SetJump(JumpBuf& Buffer) {
__asm volatile(R"(
.intel_syntax noprefix;
// rdi contains the jumpbuffer
mov [rdi + (0 * 8)], rbx;
mov [rdi + (1 * 8)], rsp;
mov [rdi + (2 * 8)], rbp;
mov [rdi + (3 * 8)], r12;
mov [rdi + (4 * 8)], r13;
mov [rdi + (5 * 8)], r14;
mov [rdi + (6 * 8)], r15;
// Return address is on the stack, load it and store
mov rsi, [rsp];
mov [rdi + (7 * 8)], rsi;
// Return zero to signify this is the SetJump.
mov rax, 0;
ret;
.att_syntax prefix;
)" ::
: "memory");
}
[[noreturn]]
FEX_DEFAULT_VISIBILITY FEX_NAKED void LongJump(JumpBuf& Buffer, uint64_t Value) {
__asm volatile(R"(
.intel_syntax noprefix;
// rdi contains the jumpbuffer
mov rbx, [rdi + (0 * 8)];
mov rsp, [rdi + (1 * 8)];
mov rbp, [rdi + (2 * 8)];
mov r12, [rdi + (3 * 8)];
mov r13, [rdi + (4 * 8)];
mov r14, [rdi + (5 * 8)];
mov r15, [rdi + (6 * 8)];
// Move value in to result register
mov rax, rsi;
// Pop the dead return address off the stack
pop rsi;
// Load the original return address from the jumpbuffer
mov rsi, [rdi + (7 * 8)];
// Return using a jump
jmp rsi;
.att_syntax prefix;
)" ::
: "memory");
}
#endif
} // namespace FEXCore::LongJump
@@ -40,6 +40,7 @@ struct HostFeatures {
bool SupportsECV {};
bool SupportsWFXT {};
bool Supports3DNow {};
bool SupportsSSE4a {};
// Float exception behaviour
bool SupportsAFP {};
@@ -84,6 +84,6 @@ private:
class SourcecodeResolver {
public:
virtual fextl::unique_ptr<SourcecodeMap> GenerateMap(const std::string_view& GuestBinaryFile, const std::string_view& GuestBinaryFileId) = 0;
virtual fextl::unique_ptr<SourcecodeMap> GenerateMap(std::string_view GuestBinaryFile, std::string_view GuestBinaryFileId) = 0;
};
} // namespace FEXCore::HLE
@@ -54,6 +54,13 @@ namespace FEXCore {
return static_cast<T>(key) == 0; \
}
// Macro that defines a fmt formatter for a reasonable case where an enum
// is formatted as a purely integral type based on its underlying type.
#define FEX_DEFINE_ENUM_FMT_PASSTHROUGH(type) \
constexpr auto format_as(type t) { \
return FEXCore::ToUnderlying(t); \
}
// Equivalent to C++23's std::to_underlying.
template<typename Enum>
[[nodiscard]]
-36
View File
@@ -1,36 +0,0 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <FEXCore/Utils/CompilerDefs.h>
#include <cstdint>
// It's longjump without glibc fortification checks.
namespace FEXCore::LongJump {
// JumpBuf definition needs to be public because the frontend needs to understand it.
#if defined(_M_ARM_64)
struct JumpBuf {
// All the registers that are required by AAPCS64 to save.
// GPRs
// X19, X20, X21, X22,
// X23, X24, X25, X26,
// X27, X28, X29, X30,
//
// Lower 64-bits:
// V8, V9, V10, V11,
// V12, V13, V14, V15,
//
// SP,
uint64_t Registers[21];
};
#else
struct JumpBuf {
// Registers to preserve
// RBX, RSP, RBP, R12, R13, R14, R15,
// <return address>
uint64_t Registers[8];
};
#endif
[[nodiscard]] FEX_DEFAULT_VISIBILITY uint64_t SetJump(JumpBuf& Buffer);
[[noreturn]] FEX_DEFAULT_VISIBILITY void LongJump(JumpBuf& Buffer, uint64_t Value);
} // namespace FEXCore::LongJump
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
#pragma once
#ifndef _WIN32
#include <linux/prctl.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <sys/prctl.h>
#ifndef PR_SET_VMA
#define PR_SET_VMA 0x53564d41
#endif
#ifndef PR_SET_VMA_ANON_NAME
#define PR_SET_VMA_ANON_NAME 0
#endif
#ifndef PR_GET_MEM_MODEL
#define PR_GET_MEM_MODEL 0x6d4d444c
#endif
#ifndef PR_SET_MEM_MODEL
#define PR_SET_MEM_MODEL 0x4d4d444c
#endif
#ifndef PR_SET_MEM_MODEL_DEFAULT
#define PR_SET_MEM_MODEL_DEFAULT 0
#endif
#ifndef PR_SET_MEM_MODEL_TSO
#define PR_SET_MEM_MODEL_TSO 1
#endif
#ifndef PR_GET_COMPAT_INPUT
#define PR_GET_COMPAT_INPUT 0x63494e50
#endif
#ifndef PR_SET_COMPAT_INPUT
#define PR_SET_COMPAT_INPUT 0x43494e50
#endif
#ifndef PR_SET_COMPAT_INPUT_DISABLE
#define PR_SET_COMPAT_INPUT_DISABLE 0
#endif
#ifndef PR_SET_COMPAT_INPUT_ENABLE
#define PR_SET_COMPAT_INPUT_ENABLE 1
#endif
#ifndef PR_GET_SHADOW_STACK_STATUS
#define PR_GET_SHADOW_STACK_STATUS 74
#endif
#ifndef PR_LOCK_SHADOW_STACK_STATUS
#define PR_LOCK_SHADOW_STACK_STATUS 76
#endif
#ifndef PR_SHADOW_STACK_ENABLE
#define PR_SHADOW_STACK_ENABLE (1ULL << 0)
#endif
#endif // ifndef _WIN32
+1 -1
View File
@@ -2,7 +2,7 @@
#pragma once
#include <FEXCore/fextl/allocator.h>
#include <filesystem>
#include <functional>
#include <string>
namespace fextl {
+48 -48
View File
@@ -9,17 +9,17 @@ using namespace ARMEmitter;
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)adr(Reg::r30, &Label);
adr(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x10fffffe);
}
{
ForwardLabel Label;
(void)adr(Reg::r30, &Label);
(void)Bind(&Label);
adr(Reg::r30, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x1000003e);
@@ -27,17 +27,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)adr(Reg::r30, &Label);
adr(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x10fffffe);
}
{
BiDirectionalLabel Label;
(void)adr(Reg::r30, &Label);
(void)Bind(&Label);
adr(Reg::r30, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x1000003e);
@@ -45,80 +45,80 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)adrp(Reg::r30, &Label);
adrp(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x9000001e);
}
{
ForwardLabel Label;
(void)adrp(Reg::r30, &Label);
adrp(Reg::r30, &Label);
// Move label a page away
for (size_t i = 0; i < 1023; ++i) {
nop();
}
(void)Bind(&Label);
Bind(&Label);
CHECK(DisassembleEncoding(0) == 0xb000001e);
}
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)adrp(Reg::r30, &Label);
adrp(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x9000001e);
}
{
BiDirectionalLabel Label;
(void)adrp(Reg::r30, &Label);
adrp(Reg::r30, &Label);
// Move label a page away
for (size_t i = 0; i < 1023; ++i) {
nop();
}
(void)Bind(&Label);
Bind(&Label);
CHECK(DisassembleEncoding(0) == 0xb000001e);
}
{
// Will generate (void)adr.
// Will generate adr.
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x10fffffe);
}
{
// Will generate nop + (void)adr.
// Will generate nop + adr.
ForwardLabel Label;
(void)LongAddressGen(Reg::r30, &Label);
(void)Bind(&Label);
LongAddressGen(Reg::r30, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
CHECK(DisassembleEncoding(1) == 0x1000003e);
}
{
// Will generate (void)adr.
// Will generate adr.
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
CHECK(DisassembleEncoding(1) == 0x10fffffe);
}
{
// Will generate nop + (void)adr.
// Will generate nop + adr.
BiDirectionalLabel Label;
(void)LongAddressGen(Reg::r30, &Label);
(void)Bind(&Label);
LongAddressGen(Reg::r30, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
@@ -126,33 +126,33 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
}
{
// Will generate (void)adrp.
// Will generate adrp.
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
// Move (void)adrp 1MB away.
// Move adrp 1MB away.
for (size_t i = 0; i < (1 * 1024 * 1024 / 4); ++i) {
nop();
}
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
nop();
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
CHECK(DisassembleEncoding(262146) == 0xd503201f);
}
{
// Will generate nop + (void)adrp.
// Will generate nop + adrp.
ForwardLabel Label;
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, and then aligned to a page.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
nop();
}
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
@@ -160,16 +160,16 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
}
{
// Will generate (void)adrp + add.
// Will generate adrp + add.
ForwardLabel Label;
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, plus one instruction.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
nop();
}
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb000081e);
@@ -178,33 +178,33 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
{
// Will generate (void)adrp.
// Will generate adrp.
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
// Move (void)adrp 1MB away.
// Move adrp 1MB away.
for (size_t i = 0; i < (1 * 1024 * 1024 / 4); ++i) {
nop();
}
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
nop();
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
CHECK(DisassembleEncoding(262146) == 0xd503201f);
}
{
// Will generate nop + (void)adrp.
// Will generate nop + adrp.
BiDirectionalLabel Label;
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, and then aligned to a page.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
nop();
}
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd503201f);
@@ -212,16 +212,16 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
}
{
// Will generate (void)adrp + add.
// Will generate adrp + add.
BiDirectionalLabel Label;
(void)LongAddressGen(Reg::r30, &Label);
LongAddressGen(Reg::r30, &Label);
// Move label 1MB away, plus a page, plus one instruction.
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
nop();
}
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb000081e);
+96 -96
View File
@@ -9,17 +9,17 @@ using namespace ARMEmitter;
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Conditional branch immediate") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)b(Condition::CC_PL, &Label);
b(Condition::CC_PL, &Label);
CHECK(DisassembleEncoding(1) == 0x54ffffe5);
}
{
ForwardLabel Label;
(void)b(Condition::CC_PL, &Label);
(void)Bind(&Label);
b(Condition::CC_PL, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x54000025);
@@ -27,17 +27,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Conditional branch immediat
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)b(Condition::CC_PL, &Label);
b(Condition::CC_PL, &Label);
CHECK(DisassembleEncoding(1) == 0x54ffffe5);
}
{
BiDirectionalLabel Label;
(void)b(Condition::CC_PL, &Label);
(void)Bind(&Label);
b(Condition::CC_PL, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x54000025);
@@ -46,17 +46,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Conditional branch immediat
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Branch consistent conditional") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)bc(Condition::CC_PL, &Label);
bc(Condition::CC_PL, &Label);
CHECK(DisassembleEncoding(1) == 0x54fffff5);
}
{
ForwardLabel Label;
(void)bc(Condition::CC_PL, &Label);
(void)Bind(&Label);
bc(Condition::CC_PL, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x54000035);
@@ -64,17 +64,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Branch consistent condition
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)bc(Condition::CC_PL, &Label);
bc(Condition::CC_PL, &Label);
CHECK(DisassembleEncoding(1) == 0x54fffff5);
}
{
BiDirectionalLabel Label;
(void)bc(Condition::CC_PL, &Label);
(void)Bind(&Label);
bc(Condition::CC_PL, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x54000035);
@@ -89,17 +89,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch regist
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immediate") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)b(&Label);
b(&Label);
CHECK(DisassembleEncoding(1) == 0x17ffffff);
}
{
ForwardLabel Label;
(void)b(&Label);
(void)Bind(&Label);
b(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x14000001);
@@ -107,17 +107,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)b(&Label);
b(&Label);
CHECK(DisassembleEncoding(1) == 0x17ffffff);
}
{
BiDirectionalLabel Label;
(void)b(&Label);
(void)Bind(&Label);
b(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x14000001);
@@ -125,17 +125,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)bl(&Label);
bl(&Label);
CHECK(DisassembleEncoding(1) == 0x97ffffff);
}
{
ForwardLabel Label;
(void)bl(&Label);
(void)Bind(&Label);
bl(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x94000001);
@@ -143,17 +143,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)bl(&Label);
bl(&Label);
CHECK(DisassembleEncoding(1) == 0x97ffffff);
}
{
BiDirectionalLabel Label;
(void)bl(&Label);
(void)Bind(&Label);
bl(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x94000001);
@@ -162,17 +162,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbz(Size::i32Bit, Reg::r29, &Label);
cbz(Size::i32Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0x34fffffd);
}
{
ForwardLabel Label;
(void)cbz(Size::i32Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbz(Size::i32Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3400003d);
@@ -180,17 +180,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbz(Size::i32Bit, Reg::r29, &Label);
cbz(Size::i32Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0x34fffffd);
}
{
BiDirectionalLabel Label;
(void)cbz(Size::i32Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbz(Size::i32Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3400003d);
@@ -198,17 +198,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbz(Size::i64Bit, Reg::r29, &Label);
cbz(Size::i64Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0xb4fffffd);
}
{
ForwardLabel Label;
(void)cbz(Size::i64Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbz(Size::i64Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb400003d);
@@ -216,17 +216,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbz(Size::i64Bit, Reg::r29, &Label);
cbz(Size::i64Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0xb4fffffd);
}
{
BiDirectionalLabel Label;
(void)cbz(Size::i64Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbz(Size::i64Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb400003d);
@@ -234,17 +234,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
cbnz(Size::i32Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0x35fffffd);
}
{
ForwardLabel Label;
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbnz(Size::i32Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3500003d);
@@ -252,17 +252,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
cbnz(Size::i32Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0x35fffffd);
}
{
BiDirectionalLabel Label;
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbnz(Size::i32Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3500003d);
@@ -270,17 +270,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
cbnz(Size::i64Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0xb5fffffd);
}
{
ForwardLabel Label;
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbnz(Size::i64Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb500003d);
@@ -288,17 +288,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
cbnz(Size::i64Bit, Reg::r29, &Label);
CHECK(DisassembleEncoding(1) == 0xb5fffffd);
}
{
BiDirectionalLabel Label;
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
(void)Bind(&Label);
cbnz(Size::i64Bit, Reg::r29, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb500003d);
@@ -307,17 +307,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbz(Reg::r29, 0, &Label);
tbz(Reg::r29, 0, &Label);
CHECK(DisassembleEncoding(1) == 0x3607fffd);
}
{
ForwardLabel Label;
(void)tbz(Reg::r29, 0, &Label);
(void)Bind(&Label);
tbz(Reg::r29, 0, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3600003d);
@@ -325,17 +325,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbz(Reg::r29, 0, &Label);
tbz(Reg::r29, 0, &Label);
CHECK(DisassembleEncoding(1) == 0x3607fffd);
}
{
BiDirectionalLabel Label;
(void)tbz(Reg::r29, 0, &Label);
(void)Bind(&Label);
tbz(Reg::r29, 0, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3600003d);
@@ -343,17 +343,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbz(Reg::r29, 63, &Label);
tbz(Reg::r29, 63, &Label);
CHECK(DisassembleEncoding(1) == 0xb6fffffd);
}
{
ForwardLabel Label;
(void)tbz(Reg::r29, 63, &Label);
(void)Bind(&Label);
tbz(Reg::r29, 63, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb6f8003d);
@@ -361,17 +361,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbz(Reg::r29, 63, &Label);
tbz(Reg::r29, 63, &Label);
CHECK(DisassembleEncoding(1) == 0xb6fffffd);
}
{
BiDirectionalLabel Label;
(void)tbz(Reg::r29, 63, &Label);
(void)Bind(&Label);
tbz(Reg::r29, 63, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb6f8003d);
@@ -379,17 +379,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbnz(Reg::r29, 0, &Label);
tbnz(Reg::r29, 0, &Label);
CHECK(DisassembleEncoding(1) == 0x3707fffd);
}
{
ForwardLabel Label;
(void)tbnz(Reg::r29, 0, &Label);
(void)Bind(&Label);
tbnz(Reg::r29, 0, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3700003d);
@@ -397,17 +397,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbnz(Reg::r29, 0, &Label);
tbnz(Reg::r29, 0, &Label);
CHECK(DisassembleEncoding(1) == 0x3707fffd);
}
{
BiDirectionalLabel Label;
(void)tbnz(Reg::r29, 0, &Label);
(void)Bind(&Label);
tbnz(Reg::r29, 0, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x3700003d);
@@ -415,17 +415,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbnz(Reg::r29, 63, &Label);
tbnz(Reg::r29, 63, &Label);
CHECK(DisassembleEncoding(1) == 0xb7fffffd);
}
{
ForwardLabel Label;
(void)tbnz(Reg::r29, 63, &Label);
(void)Bind(&Label);
tbnz(Reg::r29, 63, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb7f8003d);
@@ -433,17 +433,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
{
BiDirectionalLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
(void)tbnz(Reg::r29, 63, &Label);
tbnz(Reg::r29, 63, &Label);
CHECK(DisassembleEncoding(1) == 0xb7fffffd);
}
{
BiDirectionalLabel Label;
(void)tbnz(Reg::r29, 63, &Label);
(void)Bind(&Label);
tbnz(Reg::r29, 63, &Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xb7f8003d);
+14 -14
View File
@@ -1323,7 +1323,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: LDAPR/STLR unscaled imme
TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal") {
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
ldr(WReg::w30, &Label);
@@ -1332,7 +1332,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
ldr(SReg::s30, &Label);
@@ -1341,7 +1341,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
ldr(XReg::x30, &Label);
@@ -1350,7 +1350,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
ldr(DReg::d30, &Label);
@@ -1359,7 +1359,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
ldrsw(XReg::x30, &Label);
@@ -1368,7 +1368,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
ldr(QReg::q30, &Label);
@@ -1377,7 +1377,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
BackwardLabel Label;
(void)Bind(&Label);
Bind(&Label);
dc32(0);
prfm(Prefetch::PLDL1KEEP, &Label);
@@ -1387,7 +1387,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
ForwardLabel Label;
ldr(WReg::w30, &Label);
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x1800003e);
@@ -1396,7 +1396,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
ForwardLabel Label;
ldr(SReg::s30, &Label);
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x1c00003e);
@@ -1405,7 +1405,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
ForwardLabel Label;
ldr(XReg::x30, &Label);
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x5800003e);
@@ -1414,7 +1414,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
ForwardLabel Label;
ldr(DReg::d30, &Label);
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x5c00003e);
@@ -1423,7 +1423,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
ForwardLabel Label;
ldrsw(XReg::x30, &Label);
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x9800003e);
@@ -1432,7 +1432,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
ForwardLabel Label;
ldr(QReg::q30, &Label);
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0x9c00003e);
@@ -1441,7 +1441,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
{
ForwardLabel Label;
prfm(Prefetch::PLDL1KEEP, &Label);
(void)Bind(&Label);
Bind(&Label);
dc32(0);
CHECK(DisassembleEncoding(0) == 0xd8000020);
-14
View File
@@ -7,7 +7,6 @@
#include <climits>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>
namespace FEXCore {
@@ -66,17 +65,4 @@ constexpr int FindFirstSetBit(T value) noexcept {
return trailing_zeroes + 1;
}
// Stand-in for std::bit_cast until libc++ implements it.
template<typename To, typename From>
[[nodiscard]]
inline To BitCast(const From& source) noexcept {
static_assert(sizeof(From) == sizeof(To), "BitCast source and destination types must be equal in size.");
static_assert(std::is_trivially_copyable_v<From>, "BitCast source type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<To>, "BitCast destination type must be trivially copyable.");
std::aligned_storage_t<sizeof(To), alignof(To)> storage;
std::memcpy(&storage, &source, sizeof(storage));
return reinterpret_cast<To&>(storage);
}
} // namespace FEXCore
+1
View File
@@ -436,6 +436,7 @@ static void OverrideFeatures(FEXCore::HostFeatures* Features, uint64_t ForceSVEW
ENABLE_DISABLE_OPTION(SupportsPreserveAllABI, PRESERVEALLABI, PRESERVEALLABI);
ENABLE_DISABLE_OPTION(SupportsWFXT, WFXT, WFXT);
ENABLE_DISABLE_OPTION(Supports3DNow, 3DNOW, 3DNOW);
ENABLE_DISABLE_OPTION(SupportsSSE4a, SSE4A, SSE4A);
GET_SINGLE_OPTION(Crypto, CRYPTO);
#undef ENABLE_DISABLE_OPTION
+3
View File
@@ -26,6 +26,9 @@ static inline void store_memory_barrier() {
namespace FEX::SHMStats {
class StatAllocBase {
public:
virtual ~StatAllocBase() = default;
protected:
FEXCore::SHMStats::ThreadStats* AllocateSlot(uint32_t TID);
void DeallocateSlot(FEXCore::SHMStats::ThreadStats* AllocatedSlot);
+32
View File
@@ -16,4 +16,36 @@ struct ExtendedVolatileMetadata {
};
fextl::unordered_map<fextl::string, ExtendedVolatileMetadata> ParseExtendedVolatileMetadata(std::string_view ListOfDescriptors);
inline void ApplyFEXExtendedVolatileMetadata(FEX::VolatileMetadata::ExtendedVolatileMetadata& ExtendedMetaData,
fextl::set<uint64_t>& VolatileInstructions, FEXCore::IntervalList<uint64_t>& VolatileValidRanges,
uint64_t Address, uint64_t EndAddress, uint64_t FileOffset = 0, uint64_t FileOffsetEnd = ~0ULL) {
// Load FEX extended volatile metadata.
// Walk the volatile instructions first if they exist.
for (const auto it_inst : ExtendedMetaData.VolatileInstructions) {
const auto inst_address = it_inst + Address;
if (inst_address < EndAddress) {
VolatileInstructions.emplace(Address + it_inst);
} else {
LogMan::Msg::DFmt("Volatile instruction 0x{:x} couldn't fit in to module range [0x{:x}, 0x{:x}). Not adding anymore volatile "
"instructions. Inspect your config!",
inst_address, Address, EndAddress);
return;
}
}
// Walk the volatile list
for (const auto it_ranges : ExtendedMetaData.VolatileValidRanges) {
if (it_ranges.Offset >= FileOffset && it_ranges.End < FileOffsetEnd) {
VolatileValidRanges.Insert({Address + it_ranges.Offset - FileOffset, Address + it_ranges.End - FileOffset});
}
}
// If it is fully disabled, then set the entire module range
if (ExtendedMetaData.ModuleTSODisabled) {
VolatileValidRanges.Clear();
VolatileValidRanges.Insert({Address, EndAddress});
}
}
} // namespace FEX::VolatileMetadata
+7
View File
@@ -721,10 +721,17 @@ ApplicationWindow {
}
ConfigCheckBox {
id: x87ReducedPrecisionCheckbox
text: qsTr("Reduced x87 precision")
config: "X87ReducedPrecision"
}
ConfigCheckBox {
text: qsTr("Strict reduced x87 precision")
config: "X87StrictReducedPrecision"
enabled: x87ReducedPrecisionCheckbox.checked
}
ConfigCheckBox {
text: qsTr("Unsafe local flags optimization")
config: "ABILocalFlags"
+1 -14
View File
@@ -2,9 +2,9 @@
#include "Common/cpp-optparse/OptionParser.h"
#include "Common/Config.h"
#include "Common/FEXServerClient.h"
#include "FEXHeaderUtils/Filesystem.h"
#include "git_version.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Utils/PrctlUtils.h>
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/string.h>
#include <FEXHeaderUtils/Filesystem.h>
@@ -23,19 +23,6 @@ struct TSOEmulationFacts {
#ifdef _M_ARM_64
bool CheckForHardwareTSO() {
// We need to check if these are defined or not. This is a very fresh feature.
#ifndef PR_GET_MEM_MODEL
#define PR_GET_MEM_MODEL 0x6d4d444c
#endif
#ifndef PR_SET_MEM_MODEL
#define PR_SET_MEM_MODEL 0x4d4d444c
#endif
#ifndef PR_SET_MEM_MODEL_DEFAULT
#define PR_SET_MEM_MODEL_DEFAULT 0
#endif
#ifndef PR_SET_MEM_MODEL_TSO
#define PR_SET_MEM_MODEL_TSO 1
#endif
// Check to see if this is supported.
auto Result = prctl(PR_GET_MEM_MODEL, 0, 0, 0, 0);
if (Result == -1) {
+2 -64
View File
@@ -31,6 +31,7 @@ $end_info$
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/Telemetry.h>
#include <FEXCore/Utils/Threads.h>
#include <FEXCore/Utils/PrctlUtils.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/memory.h>
@@ -160,19 +161,6 @@ bool QueryInterpreterInstalled(bool ExecutedWithFD, const FEX::Config::PortableI
namespace FEX::TSO {
void SetupTSOEmulation(FEXCore::Context::Context* CTX) {
// We need to check if these are defined or not. This is a very fresh feature.
#ifndef PR_GET_MEM_MODEL
#define PR_GET_MEM_MODEL 0x6d4d444c
#endif
#ifndef PR_SET_MEM_MODEL
#define PR_SET_MEM_MODEL 0x4d4d444c
#endif
#ifndef PR_SET_MEM_MODEL_DEFAULT
#define PR_SET_MEM_MODEL_DEFAULT 0
#endif
#ifndef PR_SET_MEM_MODEL_TSO
#define PR_SET_MEM_MODEL_TSO 1
#endif
// Check to see if this is supported.
auto Result = prctl(PR_GET_MEM_MODEL, 0, 0, 0, 0);
if (Result == -1) {
@@ -201,19 +189,6 @@ void SetupTSOEmulation(FEXCore::Context::Context* CTX) {
namespace FEX::CompatInput {
void SetupCompatInput(bool enable) {
// We need to check if these are defined or not. This is a very fresh feature.
#ifndef PR_GET_COMPAT_INPUT
#define PR_GET_COMPAT_INPUT 0x63494e50
#endif
#ifndef PR_SET_COMPAT_INPUT
#define PR_SET_COMPAT_INPUT 0x43494e50
#endif
#ifndef PR_SET_COMPAT_INPUT_DISABLE
#define PR_SET_COMPAT_INPUT_DISABLE 0
#endif
#ifndef PR_SET_COMPAT_INPUT_ENABLE
#define PR_SET_COMPAT_INPUT_ENABLE 1
#endif
// Check to see if this is supported.
auto Result = prctl(PR_GET_COMPAT_INPUT, 0, 0, 0, 0);
if (Result == -1) {
@@ -231,15 +206,6 @@ void SetupCompatInput(bool enable) {
namespace FEX::GCS {
void CheckForGCS() {
#ifndef PR_GET_SHADOW_STACK_STATUS
#define PR_GET_SHADOW_STACK_STATUS 74
#endif
#ifndef PR_LOCK_SHADOW_STACK_STATUS
#define PR_LOCK_SHADOW_STACK_STATUS 76
#endif
#ifndef PR_SHADOW_STACK_ENABLE
#define PR_SHADOW_STACK_ENABLE (1ULL << 0)
#endif
uint64_t ShadowStackWord {};
if (prctl(PR_GET_SHADOW_STACK_STATUS, &ShadowStackWord, 0, 0, 0) == -1) {
return;
@@ -256,28 +222,6 @@ void CheckForGCS() {
}
} // namespace FEX::GCS
namespace FEX::UnalignedAtomic {
void SetupKernelUnalignedAtomics() {
#ifndef PR_ARM64_SET_UNALIGN_ATOMIC
#define PR_ARM64_SET_UNALIGN_ATOMIC 0x46455849
#define PR_ARM64_UNALIGN_ATOMIC_EMULATE (1UL << 0)
#define PR_ARM64_UNALIGN_ATOMIC_BACKPATCH (1UL << 1)
#define PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1UL << 2)
#endif
// Interfaces with downstream FEX kernel patches to control unaligned atomic handling
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS);
uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) |
(ParanoidTSO() ? 0 : PR_ARM64_UNALIGN_ATOMIC_BACKPATCH) | PR_ARM64_UNALIGN_ATOMIC_EMULATE;
if (prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0) != -1) {
LogMan::Msg::IFmt("FEX: Kernel unaligned atomics enabled!");
}
}
} // namespace FEX::UnalignedAtomic
/**
* @brief Get an FD from an environment variable and then unset the environment variable.
*
@@ -502,12 +446,7 @@ int main(int argc, char** argv, char** const envp) {
free(data);
}
{
FEX_CONFIG_OPT(TraceProfiler, TRACEPROFILER);
if (TraceProfiler()) {
FEXCore::Profiler::Init(Program.ProgramName, Program.ProgramPath);
}
}
FEXCore::Profiler::Init(Program.ProgramName, Program.ProgramPath);
bool SupportsAVX {};
fextl::unique_ptr<FEXCore::Context::Context> CTX;
@@ -519,7 +458,6 @@ int main(int argc, char** argv, char** const envp) {
// Setup TSO hardware emulation immediately after initializing the context.
FEX::TSO::SetupTSOEmulation(CTX.get());
FEX::UnalignedAtomic::SetupKernelUnalignedAtomics();
if (!Loader.Is64BitMode()) {
// Tell the kernel we want to use the compat input syscalls even though we're
@@ -164,7 +164,7 @@ uint64_t BPFEmitter::HandleJmp(uint32_t BPFIP, uint32_t NumInst, const sock_filt
TargetLabel = JumpLabels.try_emplace(Target, ARMEmitter::ForwardLabel {}).first;
}
EMIT_INST((void)b(&TargetLabel->second));
EMIT_INST(b(&TargetLabel->second));
break;
}
case BPF_JEQ:
@@ -208,8 +208,8 @@ uint64_t BPFEmitter::HandleJmp(uint32_t BPFIP, uint32_t NumInst, const sock_filt
TargetFalseLabel = JumpLabels.try_emplace(TargetFalse, ARMEmitter::ForwardLabel {}).first;
}
EMIT_INST((void)b(CompareResultOp, &TargetTrueLabel->second));
EMIT_INST((void)b(&TargetFalseLabel->second));
EMIT_INST(b(CompareResultOp, &TargetTrueLabel->second));
EMIT_INST(b(&TargetFalseLabel->second));
break;
}
default: RETURN_ERROR(-EINVAL); // Unknown jump type
@@ -263,7 +263,7 @@ uint64_t BPFEmitter::HandleEmission(uint32_t flags, const sock_fprog* prog) {
if constexpr (!CalculateSize) {
auto jump_label = JumpLabels.find(i);
if (jump_label != JumpLabels.end()) {
(void)Bind(&jump_label->second);
Bind(&jump_label->second);
}
}
@@ -359,7 +359,7 @@ uint64_t BPFEmitter::JITFilter(uint32_t flags, const sock_fprog* prog) {
// Emit the constant pool.
Align();
for (auto& Const : ConstPool) {
(void)Bind(&Const.second);
Bind(&Const.second);
dc32(Const.first);
}
@@ -771,6 +771,9 @@ SyscallHandler::SyscallHandler(FEXCore::Context::Context* _CTX, FEX::HLE::Signal
Alloc32Handler = FEX::HLE::Create32BitAllocator();
SignalDelegation->RegisterHostSignalHandler(SIGSEGV, HandleSegfault, true);
FEX_CONFIG_OPT(ExtendedVolatileMetadataConfig, EXTENDEDVOLATILEMETADATA);
ExtendedMetaData = FEX::VolatileMetadata::ParseExtendedVolatileMetadata(ExtendedVolatileMetadataConfig());
}
SyscallHandler::~SyscallHandler() {
@@ -905,9 +908,7 @@ static bool isHEX(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
}
fextl::unique_ptr<FEXCore::HLE::SourcecodeMap>
SyscallHandler::GenerateMap(const std::string_view& GuestBinaryFile, const std::string_view& GuestBinaryFileId) {
fextl::unique_ptr<FEXCore::HLE::SourcecodeMap> SyscallHandler::GenerateMap(std::string_view GuestBinaryFile, std::string_view GuestBinaryFileId) {
ELFParser GuestELF;
if (!GuestELF.ReadElf(fextl::string(GuestBinaryFile))) {
@@ -8,6 +8,7 @@ $end_info$
#pragma once
#include "Common/VolatileMetadata.h"
#include "LinuxSyscalls/FileManagement.h"
#include "LinuxSyscalls/LinuxAllocator.h"
#include "LinuxSyscalls/ThreadManager.h"
@@ -78,7 +79,6 @@ void RegisterFS(FEX::HLE::SyscallHandler* Handler);
void RegisterInfo(FEX::HLE::SyscallHandler* Handler);
void RegisterIO(FEX::HLE::SyscallHandler* Handler);
void RegisterMemory(FEX::HLE::SyscallHandler* Handler);
void RegisterNuma(FEX::HLE::SyscallHandler* Handler);
void RegisterSignals(FEX::HLE::SyscallHandler* Handler);
void RegisterThread(FEX::HLE::SyscallHandler* Handler);
void RegisterTimer(FEX::HLE::SyscallHandler* Handler);
@@ -128,8 +128,6 @@ public:
using SyscallPtrArg6 = uint64_t (*)(FEXCore::Core::CpuStateFrame* Frame, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
struct SyscallFunctionDefinition {
uint8_t NumArgs;
FEXCore::IR::SyscallFlags Flags;
union {
void* Ptr;
SyscallPtrArg0 Ptr0;
@@ -141,6 +139,8 @@ public:
SyscallPtrArg6 Ptr6;
};
int32_t HostSyscallNumber;
uint8_t NumArgs;
FEXCore::IR::SyscallFlags Flags;
#ifdef DEBUG_STRACE
fextl::string StraceFmt;
#endif
@@ -253,7 +253,12 @@ public:
uint64_t GuestShmdt(bool Is64Bit, FEXCore::Core::InternalThreadState*, const void* shmaddr);
///// Memory Manager tracking /////
void TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, int prot, int flags, int fd, off_t offset);
struct LateApplyExtendedVolatileMetadata {
fextl::set<uint64_t> VolatileInstructions {};
FEXCore::IntervalList<uint64_t> VolatileValidRanges {};
};
std::optional<LateApplyExtendedVolatileMetadata>
TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, int prot, int flags, int fd, off_t offset);
void TrackMunmap(FEXCore::Core::InternalThreadState* Thread, void* addr, size_t length);
void TrackMremap(FEXCore::Core::InternalThreadState* Thread, uint64_t OldAddress, size_t OldSize, size_t NewSize, int flags, uint64_t NewAddress);
void TrackShmat(FEXCore::Core::InternalThreadState* Thread, int shmid, uint64_t shmaddr, int shmflg, uint64_t Length);
@@ -330,8 +335,8 @@ protected:
fextl::vector<SyscallFunctionDefinition> Definitions {std::max<std::size_t>(FEX::HLE::x64::SYSCALL_x64_MAX, FEX::HLE::x32::SYSCALL_x86_MAX),
{
.NumArgs = 255,
.Ptr = reinterpret_cast<void*>(&UnimplementedSyscall),
.NumArgs = 255,
}};
std::mutex MMapMutex;
@@ -345,11 +350,13 @@ protected:
uint32_t GuestKernelVersion {};
FEXCore::Context::Context* CTX;
private:
private:
FEX::HLE::SignalDelegator* SignalDelegation;
FEX::HLE::ThunkHandler* ThunkHandler;
fextl::unordered_map<fextl::string, FEX::VolatileMetadata::ExtendedVolatileMetadata> ExtendedMetaData {};
std::mutex FutexMutex;
std::mutex SyscallMutex;
FEX::CodeLoader* LocalLoader {};
@@ -358,17 +365,12 @@ private:
#ifdef DEBUG_STRACE
void Strace(FEXCore::HLE::SyscallArguments* Args, uint64_t Ret);
#endif
fextl::unique_ptr<FEXCore::HLE::SourcecodeMap> GenerateMap(std::string_view GuestBinaryFile, std::string_view GuestBinaryFileId) override;
fextl::unique_ptr<FEX::HLE::MemAllocator> Alloc32Handler {};
fextl::unique_ptr<FEXCore::HLE::SourcecodeMap>
GenerateMap(const std::string_view& GuestBinaryFile, const std::string_view& GuestBinaryFileId) override;
std::atomic<uint64_t> AnonSharedId {1};
};
uint64_t HandleSyscall(SyscallHandler* Handler, FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args);
#define SYSCALL_ERRNO() \
do { \
if (Result == -1) return -errno; \
@@ -22,6 +22,7 @@ $end_info$
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/Utils/SignalScopeGuards.h>
#include <FEXCore/Utils/TypeDefines.h>
#include <FEXHeaderUtils/Filesystem.h>
namespace FEX::HLE {
// SMC interactions
@@ -200,6 +201,7 @@ void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState
uint64_t Result {};
size_t Size = FEXCore::AlignUp(length, FEXCore::Utils::FEX_PAGE_SIZE);
std::optional<LateApplyExtendedVolatileMetadata> LateMetadata = std::nullopt;
{
// NOTE: Frontend calls this with a nullptr Thread during initialization, but
@@ -221,10 +223,16 @@ void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState
}
}
FEX::HLE::_SyscallHandler->TrackMmap(Thread, Result, length, prot, flags, fd, offset);
LateMetadata = FEX::HLE::_SyscallHandler->TrackMmap(Thread, Result, length, prot, flags, fd, offset);
}
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, Result, Size);
if (LateMetadata) {
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), Thread);
CTX->AddForceTSOInformation(LateMetadata->VolatileValidRanges, std::move(LateMetadata->VolatileInstructions));
}
return reinterpret_cast<void*>(Result);
}
@@ -256,6 +264,11 @@ uint64_t SyscallHandler::GuestMunmap(bool Is64Bit, FEXCore::Core::InternalThread
}
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, reinterpret_cast<uint64_t>(addr), Size);
if (length) {
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), Thread);
CTX->RemoveForceTSOInformation(reinterpret_cast<uint64_t>(addr), length);
}
return Result;
}
@@ -360,11 +373,15 @@ uint64_t SyscallHandler::GuestShmdt(bool Is64Bit, FEXCore::Core::InternalThreadS
}
// MMan Tracking
void SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, int prot, int flags, int fd, off_t offset) {
std::optional<SyscallHandler::LateApplyExtendedVolatileMetadata> SyscallHandler::TrackMmap(
FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, int prot, int flags, int fd, off_t offset) {
size_t Size = FEXCore::AlignUp(length, FEXCore::Utils::FEX_PAGE_SIZE);
const auto ProtMapping = VMATracking::VMAProt::fromProt(prot);
VMATracking::MappedResource* Resource = nullptr;
std::optional<SyscallHandler::LateApplyExtendedVolatileMetadata> VolatileMetadata = std::nullopt;
if (!(flags & MAP_ANONYMOUS)) {
struct stat64 buf;
fstat64(fd, &buf);
@@ -382,6 +399,21 @@ void SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint6
Resource->MappedFile->Filename = fextl::string(Tmp, PathLength);
Resource->Iterator = Iter;
}
const fextl::string Filename = FHU::Filesystem::GetFilename(Resource->MappedFile->Filename);
// We now have the filename and the offset in the filename getting mapped.
// Check for extended volatile metadata.
auto it = ExtendedMetaData.find(Filename);
if (it != ExtendedMetaData.end()) {
SyscallHandler::LateApplyExtendedVolatileMetadata LateMetadata;
FEX::VolatileMetadata::ApplyFEXExtendedVolatileMetadata(
it->second, LateMetadata.VolatileInstructions, LateMetadata.VolatileValidRanges, addr, addr + length, offset, offset + length);
if (!LateMetadata.VolatileInstructions.empty() || !LateMetadata.VolatileValidRanges.Empty()) {
VolatileMetadata.emplace(std::move(LateMetadata));
}
}
}
} else if (flags & MAP_SHARED) {
VMATracking::MRID mrid {VMATracking::SpecialDev::Anon, AnonSharedId++};
@@ -394,7 +426,8 @@ void SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint6
Resource = nullptr;
}
VMATracking.TrackVMARange(CTX, Resource, addr, offset, Size, VMATracking::VMAFlags::fromFlags(flags), VMATracking::VMAProt::fromProt(prot));
VMATracking.TrackVMARange(CTX, Resource, addr, offset, Size, VMATracking::VMAFlags::fromFlags(flags), ProtMapping);
return VolatileMetadata;
}
void SyscallHandler::TrackMunmap(FEXCore::Core::InternalThreadState* Thread, void* addr, size_t length) {
@@ -46,7 +46,7 @@ void ThreadManager::StatAlloc::Initialize() {
// 1 page: 99 slots
// 1 MB: 26211 slots
// 128 MB: 3355440 slots
Base = ::mmap(nullptr, MAX_STATS_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
Base = FEXCore::Allocator::mmap(nullptr, MAX_STATS_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
if (Base == MAP_FAILED) {
LogMan::Msg::EFmt("[StatAlloc] mmap base failed");
Base = nullptr;
@@ -55,10 +55,10 @@ void ThreadManager::StatAlloc::Initialize() {
// Allocate a small working shared space for now, grow as necessary.
{
auto SharedBase = ::mmap(Base, CurrentSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
auto SharedBase = FEXCore::Allocator::mmap(Base, CurrentSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
if (SharedBase == MAP_FAILED) {
LogMan::Msg::EFmt("[StatAlloc] mmap shm failed");
munmap(Base, MAX_STATS_SIZE);
FEXCore::Allocator::munmap(Base, MAX_STATS_SIZE);
Base = nullptr;
goto err;
}
@@ -89,7 +89,7 @@ uint32_t ThreadManager::StatAlloc::FrontendAllocateSlots(uint32_t NewSize) {
}
{
auto SharedBase = ::mmap(Base, NewSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
auto SharedBase = FEXCore::Allocator::mmap(Base, NewSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
if (SharedBase == MAP_FAILED) {
LogMan::Msg::EFmt("[StatAlloc] allocate more mmap shm failed");
goto err;
@@ -140,7 +140,7 @@ void ThreadManager::StatAlloc::UnlockAfterFork(FEXCore::Core::InternalThreadStat
// shm_memory ownership is retained by the parent process, so the child must replace it with its own one.
// Otherwise this process will keep reporting in the original parent thread's stats region.
munmap(Base, MAX_STATS_SIZE);
FEXCore::Allocator::munmap(Base, MAX_STATS_SIZE);
Base = nullptr;
CurrentSize = 0;
Head = nullptr;
@@ -173,7 +173,8 @@ FEX::HLE::ThreadStateObject* ThreadManager::CreateThread(uint64_t InitialRIP, ui
auto Frame = ThreadStateObject->Thread->CurrentFrame;
// Allocate the call-ret stack with guard pages on both sides
auto AllocBase = reinterpret_cast<uint64_t>(::mmap(nullptr, CALLRET_STACK_ALLOC_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
auto AllocBase =
reinterpret_cast<uint64_t>(FEXCore::Allocator::mmap(nullptr, CALLRET_STACK_ALLOC_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
// Set the base used for invalidation to the start past the guard pages
ThreadStateObject->Thread->CallRetStackBase = reinterpret_cast<void*>(AllocBase + FEXCore::Utils::FEX_PAGE_SIZE);
::mprotect(ThreadStateObject->Thread->CallRetStackBase, FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE, PROT_READ | PROT_WRITE);
@@ -273,7 +274,7 @@ void ThreadManager::HandleThreadDeletion(FEX::HLE::ThreadStateObject* Thread, bo
}
// Free the call-ret stack
::munmap(reinterpret_cast<void*>(Thread->GetCallRetStackInfo().AllocationBase), CALLRET_STACK_ALLOC_SIZE);
FEXCore::Allocator::munmap(reinterpret_cast<void*>(Thread->GetCallRetStackInfo().AllocationBase), CALLRET_STACK_ALLOC_SIZE);
// If the LDT segment exists then deallocate it.
if (Thread->ldt_entry_count) {
@@ -163,8 +163,6 @@ public:
Stat.CleanupForExit();
}
StatAlloc Stat;
///< Returns the ThreadStateObject from a CpuStateFrame object.
static inline FEX::HLE::ThreadStateObject* GetStateObjectFromCPUState(FEXCore::Core::CpuStateFrame* Frame) {
return static_cast<FEX::HLE::ThreadStateObject*>(Frame->Thread->FrontendPtr);
@@ -240,6 +238,7 @@ public:
}
private:
StatAlloc Stat;
FEXCore::Context::Context* CTX;
FEX::HLE::SignalDelegator* SignalDelegation;
@@ -4,7 +4,6 @@
#include <FEXCore/Core/Context.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/LongJump.h>
#include <FEXCore/Utils/Threads.h>
namespace FEX::LinuxEmulation::Threads {
@@ -190,6 +189,143 @@ __attribute__((naked)) void StackPivotAndCall(void* Arg, FEXCore::Threads::Threa
}
#endif
namespace PThreads {
namespace LongJump {
// This is a custom long jump implementation that avoids the glibc implementation.
// This is required behaviour because glibc's fortification checks don't understand stack pivots.
// FEX requires a stack pivot to work through a long jump, so these two features are at odds with each other.
#ifdef _M_ARM_64
struct JumpBuf {
// All the registers that are required by AAPCS64 to save.
// GPRs
// X19, X20, X21, X22,
// X23, X24, X25, X26,
// X27, X28, X29, X30,
//
// Lower 64-bits:
// V8, V9, V10, V11,
// V12, V13, V14, V15,
//
// SP,
uint64_t Registers[21];
};
FEX_NAKED uint64_t SetJump(JumpBuf& Buffer) {
__asm volatile(R"(
// x0 contains the jumpbuffer
stp x19, x20, [x0, #( 0 * 8)];
stp x21, x22, [x0, #( 2 * 8)];
stp x23, x24, [x0, #( 4 * 8)];
stp x25, x26, [x0, #( 6 * 8)];
stp x27, x28, [x0, #( 8 * 8)];
stp x29, x30, [x0, #(10 * 8)];
// FPRs
stp d8, d9, [x0, #(12 * 8)];
stp d10, d11, [x0, #(14 * 8)];
stp d12, d13, [x0, #(16 * 8)];
stp d14, d15, [x0, #(18 * 8)];
// Move SP in to a temporary to store.
mov x1, sp;
str x1, [x0, #(19 * 8)];
// Return zero to signify this is the SetJump.
mov x0, #0;
ret;
)" ::
: "memory");
}
[[noreturn]]
FEX_NAKED void LongJump(JumpBuf& Buffer, uint64_t Value) {
__asm volatile(R"(
// x0 contains the jumpbuffer
ldp x19, x20, [x0, #( 0 * 8)];
ldp x21, x22, [x0, #( 2 * 8)];
ldp x23, x24, [x0, #( 4 * 8)];
ldp x25, x26, [x0, #( 6 * 8)];
ldp x27, x28, [x0, #( 8 * 8)];
ldp x29, x30, [x0, #(10 * 8)];
// FPRs
ldp d8, d9, [x0, #(12 * 8)];
ldp d10, d11, [x0, #(14 * 8)];
ldp d12, d13, [x0, #(16 * 8)];
ldp d14, d15, [x0, #(18 * 8)];
// Load SP in to temporary then move
ldr x0, [x0, #(19 * 8)];
mov sp, x0;
// Move value in to result register
mov x0, x1;
ret;
)" ::
: "memory");
}
#else
struct JumpBuf {
// Registers to preserve
// RBX, RSP, RBP, R12, R13, R14, R15,
// <return address>
uint64_t Registers[8];
};
__attribute__((naked)) uint64_t SetJump(JumpBuf& Buffer) {
__asm volatile(R"(
.intel_syntax noprefix;
// rdi contains the jumpbuffer
mov [rdi + (0 * 8)], rbx;
mov [rdi + (1 * 8)], rsp;
mov [rdi + (2 * 8)], rbp;
mov [rdi + (3 * 8)], r12;
mov [rdi + (4 * 8)], r13;
mov [rdi + (5 * 8)], r14;
mov [rdi + (6 * 8)], r15;
// Return address is on the stack, load it and store
mov rsi, [rsp];
mov [rdi + (7 * 8)], rsi;
// Return zero to signify this is the SetJump.
mov rax, 0;
ret;
.att_syntax prefix;
)" ::
: "memory");
}
[[noreturn]]
__attribute__((naked)) void LongJump(JumpBuf& Buffer, uint64_t Value) {
__asm volatile(R"(
.intel_syntax noprefix;
// rdi contains the jumpbuffer
mov rbx, [rdi + (0 * 8)];
mov rsp, [rdi + (1 * 8)];
mov rbp, [rdi + (2 * 8)];
mov r12, [rdi + (3 * 8)];
mov r13, [rdi + (4 * 8)];
mov r14, [rdi + (5 * 8)];
mov r15, [rdi + (6 * 8)];
// Move value in to result register
mov rax, rsi;
// Pop the dead return address off the stack
pop rsi;
// Load the original return address from the jumpbuffer
mov rsi, [rdi + (7 * 8)];
// Return using a jump
jmp rsi;
.att_syntax prefix;
)" ::
: "memory");
}
#endif
}; // namespace LongJump
void* InitializeThread(void* Ptr);
class PThread final : public FEXCore::Threads::Thread {
@@ -260,7 +396,7 @@ namespace PThreads {
return STracker;
}
void SetupLongJump(FEXCore::LongJump::JumpBuf* exit_resolver) {
void SetupLongJump(LongJump::JumpBuf* exit_resolver) {
_exit_resolver = exit_resolver;
}
@@ -268,7 +404,7 @@ namespace PThreads {
void LongJumpExit(FEX::HLE::ThreadStateObject* ThreadObject, uint32_t Status) {
this->Status = Status;
this->ThreadObject = ThreadObject;
FEXCore::LongJump::LongJump(*_exit_resolver, 1);
LongJump::LongJump(*_exit_resolver, 1);
FEX_UNREACHABLE;
}
@@ -287,7 +423,7 @@ namespace PThreads {
void* UserArg;
void* Stack {};
FEXCore::LongJump::JumpBuf* _exit_resolver {};
LongJump::JumpBuf* _exit_resolver {};
FEX::HLE::ThreadStateObject* ThreadObject {};
uint32_t Status {};
};
@@ -298,11 +434,11 @@ namespace PThreads {
PThread* Thread {reinterpret_cast<PThread*>(Ptr)};
StackBase = Thread->GetPivotStack();
STracker = Thread->GetStackTracker();
FEXCore::LongJump::JumpBuf exit_resolver {};
LongJump::JumpBuf exit_resolver {};
bool LongJumpExit {};
if (FEXCore::LongJump::SetJump(exit_resolver) == 0) {
if (LongJump::SetJump(exit_resolver) == 0) {
Thread->SetupLongJump(&exit_resolver);
// Run the user function.
// `Thread` object is dead after this function returns.
@@ -63,10 +63,10 @@ void x64SyscallHandler::RegisterSyscallHandlers() {
constexpr int SYSCALL_GAP_END = 424;
const SyscallFunctionDefinition InvalidSyscall {
.NumArgs = 0,
.Flags = FEXCore::IR::SyscallFlags::DEFAULT,
.Ptr = reinterpret_cast<void*>(&UnimplementedSyscall),
.HostSyscallNumber = SYSCALL_DEF(MAX),
.NumArgs = 0,
.Flags = FEXCore::IR::SyscallFlags::DEFAULT,
#ifdef DEBUG_STRACE
.StraceFmt = "Invalid",
#endif
+5 -9
View File
@@ -39,10 +39,10 @@ $end_info$
#include "Common/CRT/CRT.h"
#include "Common/PortabilityInfo.h"
#include "Common/Handle.h"
#include "Common/VolatileMetadata.h"
#include "DummyHandlers.h"
#include "BTInterface.h"
#include "Windows/Common/SHMStats.h"
#include "Windows/Common/VolatileMetadata.h"
#include <cstdint>
#include <cstdio>
@@ -314,7 +314,7 @@ void LoadImageVolatileMetadata(const fextl::string& ModuleName, uint64_t Address
auto it = ExtendedMetaData.find(ModuleName);
if (it != ExtendedMetaData.end()) {
FEX::Windows::ApplyFEXExtendedVolatileMetadata(it->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
FEX::VolatileMetadata::ApplyFEXExtendedVolatileMetadata(it->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
}
if (VolatileInstructions.empty() && VolatileValidRanges.Empty()) {
@@ -640,18 +640,14 @@ NTSTATUS ProcessInit() {
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, "1");
{
FEX_CONFIG_OPT(TraceProfiler, TRACEPROFILER);
if (TraceProfiler()) {
FEXCore::Profiler::Init("", "");
}
}
FEXCore::Profiler::Init("", "");
FEX_CONFIG_OPT(ExtendedVolatileMetadataConfig, EXTENDEDVOLATILEMETADATA);
ExtendedMetaData = FEX::VolatileMetadata::ParseExtendedVolatileMetadata(ExtendedVolatileMetadataConfig());
SignalDelegator = fextl::make_unique<FEX::DummyHandlers::DummySignalDelegator>();
SyscallHandler = fextl::make_unique<Exception::ECSyscallHandler>();
Exception::HandlerConfig.emplace();
const auto NtDll = GetModuleHandle("ntdll.dll");
const bool IsWine = !!GetProcAddress(NtDll, "wine_get_version");
@@ -665,7 +661,7 @@ NTSTATUS ProcessInit() {
CTX->SetSignalDelegator(SignalDelegator.get());
CTX->SetSyscallHandler(SyscallHandler.get());
CTX->InitCore();
Exception::HandlerConfig.emplace(*CTX);
InvalidationTracker.emplace(*CTX, Threads);
HandleImageMap(NtDllBase);
-4
View File
@@ -56,10 +56,6 @@ long long atoll(const char*) {
UNIMPLEMENTED();
}
long long strtoll(const char* __restrict__, char** __restrict, int) {
UNIMPLEMENTED();
}
long double strtold(const char* __restrict__, char** __restrict__) {
UNIMPLEMENTED();
}
+1 -19
View File
@@ -1,14 +1,13 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <FEXCore/Core/Context.h>
#include <FEXCore/Config/Config.h>
#include <FEXCore/Utils/ArchHelpers/Arm64.h>
namespace FEX::Windows {
class TSOHandlerConfig final {
public:
TSOHandlerConfig(FEXCore::Context::Context& CTX) {
TSOHandlerConfig() {
if (ParanoidTSO()) {
UnalignedHandlerType = FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::Paranoid;
} else if (HalfBarrierTSOEnabled()) {
@@ -16,21 +15,6 @@ public:
} else {
UnalignedHandlerType = FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::NonAtomic;
}
if (TSOEnabled()) {
BOOL Enable = TRUE;
NTSTATUS Status = NtSetInformationProcess(NtCurrentProcess(), ProcessFexHardwareTso, &Enable, sizeof(Enable));
if (Status == STATUS_SUCCESS) {
CTX.SetHardwareTSOSupport(true);
}
}
uint64_t Flags = (StrictInProcessSplitLocks() ? FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) |
(ParanoidTSO() ? 0 : FEX_UNALIGN_ATOMIC_BACKPATCH) | FEX_UNALIGN_ATOMIC_EMULATE;
if (NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)) == STATUS_SUCCESS) {
LogMan::Msg::IFmt("FEX: Kernel unaligned atomics enabled!");
}
}
FEXCore::ArchHelpers::Arm64::UnalignedHandlerType GetUnalignedHandlerType() const {
@@ -38,10 +22,8 @@ public:
}
private:
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
FEX_CONFIG_OPT(HalfBarrierTSOEnabled, HALFBARRIERTSOENABLED);
FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS);
FEXCore::ArchHelpers::Arm64::UnalignedHandlerType UnalignedHandlerType {FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::HalfBarrier};
};
-42
View File
@@ -1,42 +0,0 @@
// SPDX-License-Identifier: MIT
#pragma once
#include "Common/VolatileMetadata.h"
#include <FEXCore/fextl/unordered_map.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/set.h>
#include <FEXCore/Utils/IntervalList.h>
namespace FEX::Windows {
inline void ApplyFEXExtendedVolatileMetadata(FEX::VolatileMetadata::ExtendedVolatileMetadata& ExtendedMetaData,
fextl::set<uint64_t>& VolatileInstructions,
FEXCore::IntervalList<uint64_t>& VolatileValidRanges, uint64_t Address, uint64_t EndAddress) {
// Load FEX extended volatile metadata.
// Walk the volatile instructions first if they exist.
for (const auto it_inst : ExtendedMetaData.VolatileInstructions) {
const auto inst_address = it_inst + Address;
if (inst_address < EndAddress) {
VolatileInstructions.emplace(Address + it_inst);
} else {
LogMan::Msg::DFmt("Volatile instruction 0x{:x} couldn't fit in to module range [0x{:x}, 0x{:x}). Not adding anymore volatile "
"instructions. Inspect your config!",
inst_address, Address, EndAddress);
return;
}
}
// Walk the volatile list
for (const auto it_ranges : ExtendedMetaData.VolatileValidRanges) {
VolatileValidRanges.Insert({Address + it_ranges.Offset, Address + it_ranges.End});
}
// If it is fully disabled, then set the entire module range
if (ExtendedMetaData.ModuleTSODisabled) {
VolatileValidRanges.Clear();
VolatileValidRanges.Insert({Address, EndAddress});
}
}
} // namespace FEX::Windows
+10 -10
View File
@@ -40,10 +40,10 @@ $end_info$
#include "Common/CRT/CRT.h"
#include "Common/PortabilityInfo.h"
#include "Common/Handle.h"
#include "Common/VolatileMetadata.h"
#include "DummyHandlers.h"
#include "BTInterface.h"
#include "Windows/Common/SHMStats.h"
#include "Windows/Common/VolatileMetadata.h"
#include <cstdint>
#include <type_traits>
@@ -173,7 +173,7 @@ void LoadImageVolatileMetadata(const fextl::string& ModuleName, uint64_t Address
// Load FEX extended volatile metadata.
auto it = ExtendedMetaData.find(ModuleName);
if (it != ExtendedMetaData.end()) {
FEX::Windows::ApplyFEXExtendedVolatileMetadata(it->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
FEX::VolatileMetadata::ApplyFEXExtendedVolatileMetadata(it->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
}
if (VolatileInstructions.empty() && VolatileValidRanges.Empty()) {
@@ -516,18 +516,14 @@ void BTCpuProcessInit() {
FEXCore::Config::Set(FEXCore::Config::CONFIG_INTERPRETER_INSTALLED, "0");
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, "0");
{
FEX_CONFIG_OPT(TraceProfiler, TRACEPROFILER);
if (TraceProfiler()) {
FEXCore::Profiler::Init("", "");
}
}
FEXCore::Profiler::Init("", "");
FEX_CONFIG_OPT(ExtendedVolatileMetadataConfig, EXTENDEDVOLATILEMETADATA);
ExtendedMetaData = FEX::VolatileMetadata::ParseExtendedVolatileMetadata(ExtendedVolatileMetadataConfig());
SignalDelegator = fextl::make_unique<FEX::DummyHandlers::DummySignalDelegator>();
SyscallHandler = fextl::make_unique<WowSyscallHandler>();
Context::HandlerConfig.emplace();
const auto NtDll = GetModuleHandle("ntdll.dll");
const bool IsWine = !!GetProcAddress(NtDll, "wine_get_version");
OvercommitTracker.emplace(IsWine);
@@ -540,7 +536,7 @@ void BTCpuProcessInit() {
CTX->SetSignalDelegator(SignalDelegator.get());
CTX->SetSyscallHandler(SyscallHandler.get());
CTX->InitCore();
Context::HandlerConfig.emplace(*CTX);
InvalidationTracker.emplace(*CTX, Threads);
auto NtDllX86 = reinterpret_cast<SYSTEM_DLL_INIT_BLOCK*>(GetProcAddress(NtDll, "LdrSystemDllInitBlock"))->ntdll_handle;
@@ -1007,7 +1003,11 @@ NTSTATUS BTCpuNotifyMapViewOfSection(void* Unk1, void* Address, void* Unk2, SIZE
void BTCpuNotifyUnmapViewOfSection(void* Address, BOOL After, ULONG Status) {
if (!After) {
ThreadCreationMutex.lock();
InvalidationTracker->InvalidateContainingSection(reinterpret_cast<uint64_t>(Address), true);
auto [Start, Size] = InvalidationTracker->InvalidateContainingSection(reinterpret_cast<uint64_t>(Address), true);
if (Size) {
std::scoped_lock Lock(CTX->GetCodeInvalidationMutex());
CTX->RemoveForceTSOInformation(Start, Size);
}
} else {
ThreadCreationMutex.unlock();
}
-6
View File
@@ -455,12 +455,6 @@ typedef enum _MEMORY_INFORMATION_CLASS {
#define SystemEmulationBasicInformation (SYSTEM_INFORMATION_CLASS)62
#define ProcessFexHardwareTso (PROCESSINFOCLASS)2000
#define ProcessFexUnalignAtomic (PROCESSINFOCLASS)2001
// These match the prctl flag values
#define FEX_UNALIGN_ATOMIC_EMULATE (1ULL << 0)
#define FEX_UNALIGN_ATOMIC_BACKPATCH (1ULL << 1)
#define FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1ULL << 2)
typedef enum _KEY_VALUE_INFORMATION_CLASS {
KeyValueBasicInformation,
+1 -1
View File
@@ -26,7 +26,7 @@ FEX在ARMv8.0,ARMv8.1+和x86-64(支持AVX或更新处理器)硬件上进行过
在AArch64 host端,用户需要准备x86-64 RootFS[创建RootFS](#RootFS-Generation)。
### 源码导览
详见[源码大纲](docs/SourceOutline.md)。
详见[源码大纲](SourceOutline.md)。
### 编译依赖
* cmake (version 3.14 minimum)
+2 -2
View File
@@ -1,4 +1,4 @@
# FEX-2509
# FEX-2510
## FEXCore
See [FEXCore/Readme.md](../FEXCore/Readme.md) for more details
@@ -103,7 +103,7 @@ IR to IR Optimization
- [PassManager.cpp](../FEXCore/Source/Interface/IR/PassManager.cpp): Defines which passes are run, and runs them
- [PassManager.h](../FEXCore/Source/Interface/IR/PassManager.h)
- [IRValidation.cpp](../FEXCore/Source/Interface/IR/Passes/IRValidation.cpp): Sanity checking pass
- [RedundantFlagCalculationElimination.cpp](../FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp): This is not used right now, possibly broken
- [RedundantFlagCalculationElimination.cpp](../FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp)
- [RegisterAllocationPass.cpp](../FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp)
- [RegisterAllocationPass.h](../FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.h)
+1 -1
View File
@@ -38,7 +38,7 @@ foreach(ASM_SRC ${ASM_SOURCES})
add_custom_command(OUTPUT ${OUTPUT_NAME}
DEPENDS "${TMP_FILE}"
COMMAND "nasm" ARGS "${TMP_FILE}" "-o" "${OUTPUT_NAME}")
COMMAND "nasm" ARGS "-i" "${CMAKE_SOURCE_DIR}/unittests/32Bit_ASM/Includes/" "${TMP_FILE}" "-o" "${OUTPUT_NAME}")
add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME}
DEPENDS "${ASM_SRC}"
@@ -0,0 +1,130 @@
; NaN Testing Macros for 32-bit Assembly Tests
; Implements NaN triple testing system:
; - Bit 2: 1 if value is NaN
; - Bit 1: 1 if quiet NaN
; - Bit 0: 1 if signaling NaN
;
; Triple values:
; 0b000 (0): Not a NaN
; 0b101 (5): Signaling NaN
; 0b110 (6): Quiet NaN
;
; ASSUMPTION: All input pointers (edx) are valid and non-null
; Macro: CHECK_NAN_TRIPLE_32
; Checks 32-bit float NaN classification and returns triple in EAX
; Input: 32-bit float value in xmm0
; Output: NaN triple in EAX (bits 2:0)
%macro CHECK_NAN_TRIPLE_32 0
push ecx
push esi
push edx
xor eax, eax
ucomiss xmm0, xmm0
setp al
mov ecx, eax
shl ecx, 2
; Extract and check quiet bit (bit 22)
movd edx, xmm0
and edx, 0x00400000
mov esi, edx
shr esi, 22
and esi, eax
and esi, 1
shl esi, 1
add ecx, esi
; Check for signaling NaN (NaN but not quiet)
test edx, edx
sete dl
and dl, al
movzx eax, dl
or eax, ecx
pop edx
pop esi
pop ecx
%endmacro
; Macro: CHECK_NAN_TRIPLE_64
; Checks 64-bit double NaN classification and returns triple in EAX
; Input: 64-bit double value should be pre-stored at [edx] by caller
; Output: NaN triple in EAX (bits 2:0)
%macro CHECK_NAN_TRIPLE_64 0
push ebx
push esi
sub esp, 12
; Load 64-bit double and use SSE for NaN comparison
movsd xmm0, qword [edx]
xor eax, eax
ucomisd xmm0, xmm0
setp al
mov ecx, eax
movsd qword [esp], xmm0
mov edx, 524288
and edx, [esp + 4]
shl ecx, 2
; Extract quiet bit (bit 51)
mov ebx, edx
shr ebx, 19
and bl, al
movzx esi, bl
lea ecx, [ecx + 2*esi]
; Check for signaling NaN (NaN but not quiet)
test edx, edx
sete dl
and dl, al
movzx eax, dl
or eax, ecx
add esp, 12
pop esi
pop ebx
%endmacro
; Macro: CHECK_NAN_TRIPLE_80
; Checks 80-bit extended precision NaN classification and returns triple in EAX
; Input: 80-bit extended precision value in memory at [eax] (10 bytes)
; Output: NaN triple in EAX (bits 2:0)
%macro CHECK_NAN_TRIPLE_80 0
push ebx
push esi
sub esp, 20
; Load the 80-bit value and store copy for bit manipulation
fld tword [eax]
fld st0
fstp tword [esp]
; Get bits 63:32 from stored significand
mov ecx, [esp + 4]
xor eax, eax
fucomip st0
setp al
mov edx, eax
shl edx, 2
; Extract quiet bit (bit 30 in high dword)
mov ebx, ecx
shr ebx, 30
and bl, al
movzx esi, bl
lea edx, [edx + 2*esi]
; Check for signaling NaN using bt instruction
bt ecx, 30
setae cl
and cl, al
movzx eax, cl
or eax, edx
add esp, 20
pop esi
pop ebx
%endmacro
@@ -0,0 +1,69 @@
%ifdef CONFIG
{
"RegData": {
"XMM1": ["0", "0", "0", "0"],
"XMM7": ["0x2db6b85f6135a9d7", "0xe273f1177f80d2ec", "0", "0"]
},
"Mode": "32BIT",
"Env": { "FEX_HOSTFEATURES" : "enableavx" },
"HostFeatures": ["AVX"]
}
%endif
; 128-bit
; 1x displacement
; 64-bit indexes
; Calculate an address that heavily overflows
lea eax, [rel .data_mid]
mov ebx, -1
sub ebx, eax
mov [rel .index_overflow + 0 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 8
mov [rel .index_overflow + 1 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 16
mov [rel .index_overflow + 2 * 8], ebx
mov ebx, -1
sub ebx, eax
add bx, 16
mov [rel .index_overflow + 3 * 8], ebx
; Calculate new base which offsets from the overflow
lea eax, [rel .data_mid]
shl eax, 1
vmovapd ymm7, [rel .data]
vmovaps xmm0, [rel .index_overflow]
vmovaps xmm1, [rel .mask_1111]
vgatherqpd xmm7, [xmm0 * 1 + eax + 1], xmm1
hlt
align 32
.mask_1111:
dq 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000
; Indexing is a signed 64-bit integer.
.index_overflow:
dq 0, 0, 0, 0
; Random data, 512-byte per line
.data:
db 0x1d, 0x30, 0x09, 0x22, 0x56, 0xa2, 0xcd, 0xf1, 0xb7, 0x62, 0x91, 0x40, 0x67, 0x07, 0x35, 0x0f, 0x59, 0x33, 0x63, 0x52, 0x26, 0xd2, 0x2f, 0x00, 0x8a, 0x36, 0xb7, 0xf7, 0xaf, 0x4f, 0xa1, 0xc0, 0xe3, 0xbe, 0x99, 0x67, 0x5f, 0x80, 0x60, 0xcb, 0x43, 0xfa, 0x5b, 0x86, 0xb1, 0x11, 0xbc, 0xb3, 0x7b, 0x43, 0x5b, 0x45, 0x9e, 0x33, 0x89, 0xb5, 0x1b, 0xb9, 0x33, 0x4f, 0xdb, 0x5d, 0x93, 0xd6, 0x4f, 0xbc, 0x37, 0xde, 0xeb, 0xdb, 0x43, 0x2b, 0x05, 0x60, 0xb8, 0x98, 0x5c, 0xa3, 0xe3, 0x1b, 0x33, 0x03, 0x29, 0x4b, 0x12, 0x4c, 0x1e, 0xe6, 0x5e, 0x0e, 0x6c, 0xa1, 0xb9, 0x36, 0xfa, 0x6c, 0x7f, 0xc6, 0xa8, 0x38, 0x73, 0x2a, 0x0a, 0x25, 0x69, 0xa5, 0x97, 0x3f, 0x24, 0x00, 0x30, 0x4d, 0x27, 0xb3, 0x94, 0x48, 0xef, 0x47, 0x98, 0x71, 0x0d, 0x56, 0x76, 0xec, 0x41, 0x12, 0x9b, 0x7b, 0x9c, 0xf5, 0x85, 0x07, 0x2d, 0x6b, 0xc6, 0xc1, 0x2e, 0x72, 0x22, 0x5a, 0x43, 0xff, 0x1e, 0xec, 0x67, 0x2b, 0x31, 0x96, 0x14, 0x2c, 0xb1, 0x5f, 0x5d, 0x0c, 0xc9, 0xad, 0x15, 0x5f, 0xab, 0x66, 0x14, 0x1c, 0x72, 0xfa, 0x23, 0xef, 0x9f, 0x77, 0xf6, 0x50, 0xb0, 0x70, 0xb8, 0x3c, 0x85, 0x9e, 0x90, 0x69, 0x17, 0x25, 0xae, 0x6e, 0xe2, 0x16, 0x7d, 0x42, 0x38, 0xdf, 0x74, 0x72, 0x7b, 0x97, 0xa9, 0x9e, 0x40, 0x24, 0x85, 0xdc, 0x64, 0xfa, 0xb1, 0x8b, 0x95, 0xe6, 0xe4, 0x13, 0x72, 0xf1, 0x52, 0x2f, 0xa0, 0xd6, 0x52, 0xc0, 0x11, 0xa7, 0xfe, 0xd5, 0x3b, 0x56, 0xca, 0xbc, 0x01, 0xce, 0x3d, 0xd2, 0x30, 0x97, 0x1d, 0xdc, 0xeb, 0x9d, 0xa9, 0x3e, 0x09, 0xef, 0xee, 0x7f, 0x09, 0x7b, 0x82, 0x43, 0x15, 0x2e, 0xa4, 0x2e, 0x97, 0x21, 0x92, 0x7e, 0x69, 0x21, 0x25, 0xda, 0x46, 0x7c, 0x0c, 0xcd, 0x1d, 0xde, 0x42, 0x11, 0xa2, 0xef, 0xa2, 0xc8, 0x32, 0x9a, 0x82, 0xcf, 0x72, 0x7e, 0x22, 0xa6, 0x11, 0xfa, 0xec, 0x0b, 0x77, 0x99, 0x38, 0x03, 0xf6, 0x80, 0xba, 0xea, 0x75, 0x19, 0xb0, 0x48, 0x02, 0xb2, 0x6b, 0xc0, 0x8c, 0xfb, 0xfe, 0xaf, 0x94, 0x4f, 0x6f, 0xb4, 0xcb, 0x1c, 0x27, 0xf0, 0x41, 0xb6, 0x46, 0x41, 0x68, 0x3d, 0x05, 0x79, 0x6b, 0xcd, 0xb7, 0x20, 0xdc, 0x40, 0x81, 0x58, 0xcb, 0x33, 0xa3, 0xf3, 0x34, 0xdc, 0x63, 0x2d, 0xa5, 0xb5, 0xa1, 0xd1, 0xfd, 0x49, 0x5b, 0x46, 0x94, 0x01, 0xa8, 0xf2, 0xd8, 0x93, 0x2c, 0xbb, 0x57, 0xfe, 0x7c, 0x77, 0x3b, 0x19, 0x6f, 0x3c, 0xaa, 0x23, 0x5b, 0xc0, 0xe7, 0x00, 0x41, 0x97, 0x91, 0xe8, 0x00, 0x12, 0xdf, 0xf6, 0x5c, 0x2e, 0xc6, 0x8e, 0xc6, 0x77, 0x59, 0x78, 0x9b, 0xef, 0x63, 0xb0, 0xd7, 0xbb, 0xc4, 0x0b, 0x60, 0x65, 0x3f, 0xfe, 0xbf, 0x04, 0x3e, 0xae, 0xc2, 0xa5, 0x90, 0xe1, 0x2a, 0x56, 0x3f, 0x4c, 0x3f, 0x7a, 0x7d, 0xda, 0x81, 0x50, 0xea, 0x4c, 0xfe, 0xc3, 0xf8, 0x5c, 0x2b, 0x67, 0xb3, 0x9f, 0x8b, 0x95, 0xda, 0x6f, 0x5d, 0xdd, 0x82, 0x7f, 0x52, 0xa2, 0xcc, 0x57, 0xec, 0xc4, 0x14, 0xd2, 0x4f, 0x1b, 0xcb, 0xea, 0xaf, 0x0e, 0x0f, 0x53, 0xaa, 0x56, 0x63, 0xea, 0x36, 0xa6, 0x89, 0x1a, 0x66, 0xc0, 0x4e, 0xf4, 0x1e, 0x02, 0x43, 0xde, 0xde, 0xc8, 0x9e, 0x88, 0x6e, 0x32, 0xd4, 0xcb, 0x47, 0x24, 0x7c, 0x28, 0x38, 0xd4, 0x95, 0xb6, 0xa3, 0x91, 0x69, 0xc7, 0x8d, 0xfd, 0x15, 0xf5, 0xbf, 0xb1, 0x98, 0x8c, 0x57, 0x51, 0xbf, 0x83, 0x6a, 0x35, 0x10, 0x03, 0x50, 0xe5, 0xf7, 0xfa, 0xf8, 0xa5, 0xb0, 0xdb, 0xfb, 0x42, 0x93, 0xbb, 0x17, 0xf7, 0x36, 0xbe, 0x26, 0x66, 0x61, 0xe2
db 0xaf, 0xe0, 0x22, 0x25, 0x2a, 0xae, 0x78, 0x67, 0x8f, 0x7e, 0x9e, 0x59, 0xd7, 0xa3, 0x71, 0xcc, 0x43, 0x85, 0x09, 0xf9, 0x18, 0x52, 0x7b, 0x01, 0x73, 0xcb, 0x31, 0x18, 0x66, 0x79, 0x67, 0x10, 0x67, 0xd8, 0xdf, 0x43, 0xaf, 0x2d, 0x9a, 0x09, 0x9c, 0xd1, 0x37, 0x7e, 0xf5, 0x1c, 0x3c, 0x4f, 0x15, 0xe1, 0x6f, 0xfd, 0x13, 0x3d, 0x53, 0x81, 0xa9, 0x93, 0x5f, 0x92, 0x41, 0x48, 0xec, 0x87, 0x87, 0x1d, 0x0b, 0xaa, 0xaa, 0xd3, 0xc2, 0x98, 0x20, 0xce, 0x28, 0xaf, 0x9d, 0x84, 0x69, 0x4a, 0xfd, 0xc0, 0x9c, 0x2e, 0x50, 0x20, 0xb2, 0x00, 0xc1, 0x81, 0x2a, 0x32, 0x8e, 0x95, 0x20, 0xa7, 0xca, 0x39, 0x28, 0x12, 0x23, 0x0e, 0x43, 0xd3, 0x82, 0x76, 0x73, 0x3c, 0xbf, 0xa9, 0x98, 0xf6, 0x39, 0x6d, 0xd9, 0x15, 0x33, 0x1e, 0x07, 0x7c, 0x08, 0x12, 0x23, 0xbd, 0xd3, 0x34, 0x2d, 0x9a, 0x23, 0x21, 0x46, 0xf3, 0x9a, 0x04, 0x25, 0x62, 0xeb, 0x7e, 0x9a, 0xaa, 0xb6, 0x26, 0xaa, 0x85, 0x01, 0x3a, 0xd8, 0xfc, 0x57, 0x98, 0xb9, 0xe4, 0xc4, 0xe9, 0x11, 0x3e, 0x22, 0x95, 0x3b, 0x41, 0x2b, 0x02, 0x04, 0x6c, 0x75, 0xa5, 0xf2, 0xaa, 0x09, 0x9e, 0x6f, 0xab, 0x1d, 0x2a, 0x5c, 0xde, 0x21, 0xb1, 0x96, 0x2d, 0x86, 0x3f, 0xd0, 0x07, 0x18, 0x1f, 0x87, 0xc2, 0x8f, 0xdf, 0x6a, 0x57, 0x6d, 0x3f, 0x80, 0xc5, 0x08, 0x19, 0xa5, 0x09, 0x65, 0x3d, 0xdc, 0x9e, 0x80, 0x3c, 0x2a, 0x0e, 0x7a, 0x40, 0x04, 0x0b, 0xcc, 0x61, 0xdb, 0x73, 0xfc, 0xa5, 0x0a, 0x42, 0x18, 0xc1, 0xd5, 0xbd, 0x18, 0x78, 0xa1, 0xe4, 0xde, 0x44, 0xec, 0x79, 0xb0, 0x27, 0xaa, 0x45, 0x21, 0x57, 0x19, 0x75, 0x09, 0x5c, 0x58, 0xd5, 0xb9, 0x6f, 0x3b, 0x48, 0x59, 0x41, 0x3e, 0xfd, 0x17, 0x43, 0x27, 0xc3, 0x8d, 0x76, 0x8e, 0x38, 0x47, 0xe7, 0xd2, 0xea, 0x54, 0x73, 0x8a, 0x65, 0x4c, 0x49, 0x91, 0xaf, 0x29, 0x65, 0x0d, 0x81, 0xa4, 0x77, 0xd7, 0x32, 0xd0, 0x69, 0xd9, 0x6b, 0xa3, 0x9b, 0x24, 0xd6, 0x0a, 0xd2, 0x77, 0x38, 0x59, 0x0b, 0xc8, 0x5c, 0xc7, 0x0b, 0x1d, 0xd1, 0xfa, 0xa7, 0x45, 0x3c, 0xeb, 0x5c, 0x8e, 0x25, 0x35, 0x81, 0x6d, 0x6d, 0xfe, 0xb4, 0x63, 0x89, 0xe4, 0xf0, 0xa8, 0xda, 0xb7, 0xd4, 0xff, 0x5d, 0x28, 0x97, 0x11, 0xf9, 0x8d, 0xab, 0x29, 0xd5, 0xd3, 0x1c, 0x70, 0x20, 0x4c, 0x41, 0x16, 0x42, 0xfd, 0xfc, 0x62, 0x82, 0x40, 0x59, 0x34, 0x28, 0xd0, 0xd5, 0xfc, 0xac, 0x97, 0xb8, 0x82, 0x0e, 0x4b, 0xae, 0x51, 0x28, 0x1a, 0xf1, 0x87, 0xd3, 0x20, 0xa3, 0xe7, 0x74, 0x69, 0x3c, 0x54, 0x8d, 0xc5, 0x56, 0x1d, 0xcd, 0x75, 0xae, 0x88, 0x17, 0x30, 0xdf, 0x46, 0x4a, 0xbc, 0x64, 0xff, 0xa2, 0xc1, 0x62, 0xbd, 0x88, 0x7b, 0x3e, 0xa1, 0x0c, 0xa9, 0x13, 0x0e, 0xc1, 0xb4, 0x24, 0xe6, 0x96, 0x1b, 0x9c, 0x9b, 0xac, 0x44, 0x33, 0x5b, 0xda, 0xd5, 0x88, 0x4d, 0xfe, 0x81, 0x09, 0x07, 0x17, 0xcf, 0x14, 0x05, 0xaf, 0xf8, 0x72, 0x14, 0x49, 0x5f, 0x06, 0x62, 0xab, 0xe0, 0x42, 0x70, 0x12, 0x59, 0x41, 0x0f, 0x18, 0x83, 0x68, 0x6d, 0xc6, 0x3c, 0xea, 0xe0, 0x6d, 0xd4, 0xae, 0xa6, 0xf1, 0x63, 0x21, 0x7f, 0xb5, 0x9d, 0x22, 0xf4, 0xd2, 0x49, 0x49, 0xed, 0x07, 0xb1, 0x11, 0xf9, 0x2e, 0x74, 0xbe, 0x35, 0x47, 0xdc, 0xef, 0x85, 0x0b, 0x4d, 0x46, 0xe6, 0x1f, 0x60, 0x6a, 0xa1, 0x8a, 0x4d, 0x46, 0x87, 0x30, 0x8e, 0x9a, 0xba, 0x97, 0x3e, 0x15, 0xb7, 0x33, 0x76, 0x81, 0x69, 0xdb, 0x82, 0x5e, 0xe6, 0x7b, 0xec, 0xd2, 0x80, 0x7f, 0x17, 0xf1, 0x73, 0xe2
.data_mid:
db 0xd7, 0xa9, 0x35, 0x61, 0x5f, 0xb8, 0xb6, 0x2d, 0x29, 0x34, 0x63, 0xbf, 0xe2, 0x1c, 0x34, 0xe9, 0xf5, 0xff, 0x34, 0x8a, 0x2f, 0xea, 0xd4, 0x3f, 0x3b, 0xfe, 0x6e, 0xdf, 0xa6, 0xd8, 0xc6, 0xb8, 0xc5, 0xff, 0x12, 0x97, 0x53, 0xda, 0x86, 0xa8, 0x0a, 0x12, 0x4e, 0x5d, 0x96, 0x65, 0x51, 0x22, 0xe2, 0x9d, 0x08, 0x71, 0x84, 0x19, 0x8b, 0xbf, 0x29, 0xd3, 0x3f, 0xab, 0xde, 0xe4, 0x27, 0x8b, 0x99, 0xcc, 0xb1, 0x7c, 0xa5, 0x71, 0x91, 0x9a, 0x0b, 0xad, 0x75, 0x86, 0xe3, 0x9c, 0x4e, 0x0c, 0x01, 0xb3, 0x12, 0x33, 0x90, 0x81, 0x7c, 0x71, 0x2c, 0x70, 0x61, 0xd5, 0x39, 0x0c, 0x45, 0xfc, 0x27, 0xaf, 0xbb, 0xd9, 0x26, 0x1b, 0x33, 0xb4, 0x0d, 0xf8, 0xd6, 0x2d, 0x09, 0xc7, 0x8c, 0xbf, 0x48, 0x53, 0x14, 0x94, 0x76, 0x25, 0xc7, 0x0c, 0x69, 0x49, 0x82, 0xb4, 0x2f, 0x48, 0x38, 0x44, 0x9d, 0x90, 0x6d, 0x66, 0x35, 0xe9, 0x3e, 0x2f, 0x2a, 0xb7, 0xe1, 0xb1, 0x2b, 0x99, 0x08, 0x6f, 0x5c, 0x6c, 0xdf, 0xdb, 0x10, 0xe2, 0xaa, 0x86, 0xe7, 0xf8, 0x9e, 0x62, 0xde, 0xa5, 0x81, 0x6b, 0x20, 0x47, 0xa9, 0x06, 0x49, 0xc0, 0x78, 0x8c, 0x70, 0x93, 0x7e, 0xda, 0xda, 0x5e, 0x3b, 0x23, 0xf9, 0xcc, 0x87, 0xdf, 0x48, 0x4f, 0xd6, 0x77, 0xce, 0x45, 0xe1, 0xdc, 0x0c, 0x7a, 0x0c, 0x50, 0x15, 0x63, 0x8c, 0x48, 0xd3, 0x8e, 0xfa, 0xcc, 0xac, 0x1a, 0x83, 0xde, 0xb1, 0x87, 0x2a, 0x58, 0x5c, 0xa5, 0x20, 0x3d, 0xaa, 0x1e, 0x5d, 0x71, 0xa6, 0x57, 0x75, 0x82, 0xb7, 0x33, 0x9e, 0x6b, 0xf3, 0x35, 0x02, 0x98, 0x03, 0xe1, 0x3b, 0xd2, 0x9f, 0x7a, 0x06, 0x85, 0xef, 0x7d, 0xd9, 0xf2, 0x0c, 0x9e, 0xce, 0xb9, 0xce, 0x13, 0x4a, 0x9e, 0x8a, 0x29, 0xe6, 0xe5, 0xe4, 0x39, 0xba, 0xfd, 0xa3, 0x33, 0xa8, 0x13, 0x9e, 0xa5, 0x11, 0x37, 0x69, 0xbc, 0xda, 0x11, 0x49, 0x2d, 0x4a, 0xef, 0x20, 0x8b, 0x7a, 0xb8, 0x9c, 0xc3, 0xaf, 0x26, 0x71, 0xd9, 0xa2, 0xf6, 0x0f, 0x85, 0x87, 0xa8, 0x6c, 0xf9, 0x99, 0xa2, 0xb2, 0x36, 0x2d, 0x78, 0x10, 0xe4, 0x33, 0x8d, 0xa4, 0x63, 0xea, 0x02, 0xb9, 0xac, 0x2f, 0x90, 0x39, 0x2d, 0x0e, 0x2e, 0xf5, 0x08, 0xa5, 0x5c, 0x8e, 0x71, 0x30, 0x0d, 0x1b, 0x84, 0x7a, 0xd7, 0xd4, 0xab, 0x81, 0x82, 0x18, 0x37, 0xf3, 0x28, 0x6f, 0x4e, 0x28, 0x71, 0xda, 0xc9, 0x99, 0x46, 0x14, 0x46, 0x77, 0x01, 0x16, 0x21, 0xae, 0x83, 0x93, 0x86, 0x7f, 0x5a, 0xee, 0xd5, 0xdf, 0x48, 0x5b, 0x15, 0xc8, 0x09, 0x30, 0x8f, 0x01, 0xcc, 0x95, 0x30, 0xd9, 0xf7, 0x72, 0x97, 0xfd, 0x9d, 0xec, 0x9f, 0xbf, 0x5c, 0xbf, 0x4f, 0xca, 0x33, 0xb4, 0xd2, 0xa2, 0xb9, 0x08, 0x9c, 0x40, 0x25, 0x3f, 0x86, 0xdc, 0x83, 0x70, 0x2f, 0xfb, 0x2a, 0xf8, 0x61, 0x1f, 0xa1, 0x1f, 0x36, 0x04, 0xe2, 0xef, 0x1c, 0xa4, 0xcd, 0x3c, 0x7f, 0xc5, 0x73, 0x9c, 0x2e, 0xeb, 0x03, 0x79, 0xd1, 0x02, 0xfc, 0x6f, 0xbd, 0x5a, 0x95, 0xb2, 0xf6, 0x25, 0x96, 0xe6, 0x80, 0x0a, 0xc5, 0xc7, 0xca, 0x8d, 0x31, 0xae, 0xf0, 0x49, 0xcf, 0x43, 0x06, 0x27, 0x7f, 0x25, 0xc7, 0x4c, 0xb7, 0xfc, 0x73, 0xd3, 0x04, 0xd3, 0xb9, 0x9f, 0x74, 0xed, 0x9e, 0x3c, 0xf0, 0xcf, 0x26, 0x2b, 0xd9, 0xcb, 0x78, 0x2a, 0xef, 0x72, 0xf7, 0xb6, 0x78, 0x30, 0x2d, 0x8c, 0x83, 0x73, 0x66, 0x74, 0x3d, 0x66, 0x0a, 0x74, 0x5a, 0x3f, 0x9f, 0x6e, 0x56, 0x68, 0x01, 0xc2, 0xca, 0x2b, 0xa1, 0x25, 0x36, 0x9c, 0x3b, 0xa4, 0x5e, 0x44, 0xf1, 0x18, 0x1d, 0xb6, 0x1a, 0x3a, 0xee, 0x8d, 0x67, 0x34, 0x9c
db 0xdd, 0x48, 0x14, 0xc2, 0x5f, 0xd8, 0xe5, 0x71, 0x22, 0xbf, 0xbc, 0x84, 0xda, 0xc1, 0xb1, 0x22, 0x55, 0xa4, 0x63, 0x41, 0x77, 0xac, 0x40, 0x2d, 0x44, 0x73, 0x8c, 0x14, 0xba, 0x5e, 0x63, 0x68, 0x65, 0x61, 0x6d, 0xec, 0xe2, 0x6d, 0x37, 0x22, 0x04, 0xeb, 0xc7, 0xd4, 0xc9, 0x62, 0x56, 0x13, 0x96, 0x29, 0x03, 0xf4, 0x55, 0xe2, 0x58, 0x7d, 0xda, 0x52, 0x2e, 0x94, 0x07, 0xe6, 0xef, 0xc0, 0xee, 0x9e, 0x0b, 0xf7, 0xcd, 0x13, 0x8b, 0x7d, 0xea, 0xdc, 0xf8, 0xf1, 0xcb, 0xad, 0x49, 0x97, 0xc9, 0x98, 0x0b, 0xcf, 0x84, 0x8e, 0x8e, 0xbb, 0x06, 0x2e, 0x54, 0xf5, 0xa7, 0xbd, 0x70, 0x7e, 0x38, 0x69, 0x8d, 0xb0, 0x01, 0x7b, 0x41, 0x80, 0x09, 0x44, 0xfd, 0x7e, 0x21, 0xb4, 0xbe, 0x6b, 0x4a, 0xb7, 0xca, 0x2d, 0x19, 0xfe, 0x6d, 0xd6, 0x11, 0x29, 0xbb, 0xb2, 0x16, 0xf1, 0xe7, 0x92, 0x71, 0xda, 0x7e, 0x68, 0x3a, 0xe0, 0xea, 0x89, 0x8d, 0xe0, 0x44, 0x48, 0x25, 0x92, 0x37, 0x54, 0x26, 0xf2, 0xab, 0xb3, 0x3b, 0xdb, 0xbb, 0x2b, 0x5c, 0xf5, 0xbc, 0xc7, 0x97, 0xdb, 0xc7, 0x49, 0x25, 0x7c, 0xc2, 0x80, 0x02, 0x69, 0xd4, 0xda, 0xda, 0xe1, 0x04, 0xf3, 0x19, 0xb8, 0xc9, 0xb2, 0xfb, 0x1e, 0x47, 0xa9, 0x0c, 0xa3, 0x48, 0xce, 0xc2, 0x9e, 0x3b, 0x28, 0x23, 0x5a, 0x20, 0x44, 0x77, 0x40, 0xe2, 0xd7, 0x20, 0xd5, 0x71, 0x6f, 0xd4, 0x3c, 0x68, 0x38, 0x9b, 0x89, 0x2e, 0x2d, 0xa8, 0x1f, 0x99, 0xb5, 0x8a, 0x66, 0x07, 0x59, 0x75, 0x9e, 0xf8, 0xd9, 0xbe, 0x85, 0x6a, 0x20, 0x92, 0x9d, 0xd2, 0x5e, 0x45, 0xc0, 0x60, 0xbe, 0x85, 0x0b, 0x84, 0x47, 0xf5, 0xa8, 0x43, 0x87, 0xf1, 0x21, 0x21, 0xb0, 0x3b, 0x04, 0x13, 0x16, 0x3e, 0xdf, 0xc3, 0xc6, 0x04, 0x73, 0xcd, 0x92, 0x76, 0xfb, 0xe7, 0x9c, 0xd3, 0x46, 0x11, 0x78, 0xca, 0x12, 0xd9, 0x4a, 0x35, 0xf1, 0x6e, 0x89, 0x8b, 0xe9, 0x7a, 0x04, 0xba, 0x18, 0x25, 0x7c, 0x9e, 0xe6, 0x4f, 0xc2, 0x56, 0x05, 0x72, 0xc3, 0x76, 0xee, 0x7d, 0x77, 0x19, 0x7a, 0x73, 0x2c, 0x81, 0xb8, 0xc7, 0xd9, 0x7f, 0x17, 0x5d, 0x30, 0xda, 0x77, 0x3c, 0x14, 0x88, 0xe8, 0xe4, 0xbf, 0xee, 0x21, 0x1c, 0x29, 0x4e, 0x58, 0xa8, 0x8a, 0x5c, 0xae, 0xa2, 0x1c, 0x7c, 0x25, 0x7c, 0x1c, 0x39, 0xa4, 0x28, 0x4b, 0x78, 0x52, 0xae, 0x2c, 0xbb, 0x5f, 0xbf, 0x51, 0x09, 0x20, 0x76, 0xb2, 0x7d, 0xb1, 0x63, 0x84, 0xc5, 0x49, 0x8a, 0x73, 0xdb, 0x76, 0x1d, 0x25, 0x31, 0xf2, 0x1e, 0x19, 0x38, 0xc8, 0x3b, 0x51, 0x3c, 0x13, 0x52, 0x84, 0xae, 0xc2, 0xe4, 0x8a, 0x57, 0x0d, 0xde, 0x8d, 0x18, 0x48, 0x9a, 0xbd, 0xbf, 0xf3, 0xea, 0x79, 0x17, 0x06, 0x96, 0x72, 0x08, 0x60, 0x95, 0xf9, 0x6f, 0x25, 0x0c, 0xb7, 0x9d, 0x98, 0x23, 0x01, 0xc8, 0x7a, 0xdb, 0x75, 0x63, 0x64, 0x14, 0x5e, 0x10, 0xf5, 0x16, 0x48, 0xbc, 0xc6, 0x7e, 0x24, 0xf3, 0xad, 0x57, 0x3f, 0x7d, 0x6c, 0xab, 0x18, 0x8c, 0x12, 0xc5, 0x0c, 0xd8, 0xb5, 0x1e, 0x43, 0x7c, 0x23, 0x17, 0x48, 0xba, 0x76, 0x3b, 0xd9, 0x2b, 0xae, 0x1b, 0xef, 0x58, 0xfa, 0x87, 0xad, 0x9b, 0x6d, 0xf9, 0xab, 0xa8, 0x3c, 0xfc, 0x59, 0x67, 0xa6, 0x2c, 0xc7, 0x75, 0xa4, 0x97, 0xca, 0x18, 0x18, 0x04, 0x2c, 0xb3, 0x0e, 0xa9, 0x69, 0x33, 0x67, 0xa2, 0xc6, 0xbc, 0x98, 0x48, 0x71, 0x11, 0x05, 0x30, 0xf6, 0xa9, 0x61, 0x40, 0x46, 0xf1, 0x41, 0x37, 0xd0, 0x6b, 0x7c, 0x1f, 0x03, 0x5c, 0xe9, 0xf4, 0x59, 0x1d, 0x35, 0xf0, 0x98, 0x42, 0x4a, 0x92, 0x2a, 0xc3, 0x9a, 0xb8, 0xa5
@@ -0,0 +1,73 @@
%ifdef CONFIG
{
"RegData": {
"XMM1": ["0", "0", "0", "0"],
"XMM7": ["0x2db6b85f6135a9d7", "0xe273f1177f80d2ec", "0", "0"]
},
"Mode": "32BIT",
"Env": { "FEX_HOSTFEATURES" : "enableavx" },
"HostFeatures": ["AVX"]
}
%endif
; 128-bit
; 2x displacement
; 64-bit indexes
; Calculate an address that heavily overflows
lea eax, [rel .data_mid]
mov ebx, -1
sub ebx, eax
sar ebx, 1
mov [rel .index_overflow + 0 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 8
sar ebx, 1
mov [rel .index_overflow + 1 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 16
sar ebx, 1
mov [rel .index_overflow + 2 * 8], ebx
mov ebx, -1
sub ebx, eax
add bx, 16
sar ebx, 1
mov [rel .index_overflow + 3 * 8], ebx
; Calculate new base which offsets from the overflow
lea eax, [rel .data_mid]
shl eax, 1
vmovapd ymm7, [rel .data]
vmovaps xmm0, [rel .index_overflow]
vmovaps xmm1, [rel .mask_1111]
vgatherqpd xmm7, [xmm0 * 2 + eax + 2], xmm1
hlt
align 32
.mask_1111:
dq 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000
; Indexing is a signed 64-bit integer.
.index_overflow:
dq 0, 0, 0, 0
; Random data, 512-byte per line
.data:
db 0x1d, 0x30, 0x09, 0x22, 0x56, 0xa2, 0xcd, 0xf1, 0xb7, 0x62, 0x91, 0x40, 0x67, 0x07, 0x35, 0x0f, 0x59, 0x33, 0x63, 0x52, 0x26, 0xd2, 0x2f, 0x00, 0x8a, 0x36, 0xb7, 0xf7, 0xaf, 0x4f, 0xa1, 0xc0, 0xe3, 0xbe, 0x99, 0x67, 0x5f, 0x80, 0x60, 0xcb, 0x43, 0xfa, 0x5b, 0x86, 0xb1, 0x11, 0xbc, 0xb3, 0x7b, 0x43, 0x5b, 0x45, 0x9e, 0x33, 0x89, 0xb5, 0x1b, 0xb9, 0x33, 0x4f, 0xdb, 0x5d, 0x93, 0xd6, 0x4f, 0xbc, 0x37, 0xde, 0xeb, 0xdb, 0x43, 0x2b, 0x05, 0x60, 0xb8, 0x98, 0x5c, 0xa3, 0xe3, 0x1b, 0x33, 0x03, 0x29, 0x4b, 0x12, 0x4c, 0x1e, 0xe6, 0x5e, 0x0e, 0x6c, 0xa1, 0xb9, 0x36, 0xfa, 0x6c, 0x7f, 0xc6, 0xa8, 0x38, 0x73, 0x2a, 0x0a, 0x25, 0x69, 0xa5, 0x97, 0x3f, 0x24, 0x00, 0x30, 0x4d, 0x27, 0xb3, 0x94, 0x48, 0xef, 0x47, 0x98, 0x71, 0x0d, 0x56, 0x76, 0xec, 0x41, 0x12, 0x9b, 0x7b, 0x9c, 0xf5, 0x85, 0x07, 0x2d, 0x6b, 0xc6, 0xc1, 0x2e, 0x72, 0x22, 0x5a, 0x43, 0xff, 0x1e, 0xec, 0x67, 0x2b, 0x31, 0x96, 0x14, 0x2c, 0xb1, 0x5f, 0x5d, 0x0c, 0xc9, 0xad, 0x15, 0x5f, 0xab, 0x66, 0x14, 0x1c, 0x72, 0xfa, 0x23, 0xef, 0x9f, 0x77, 0xf6, 0x50, 0xb0, 0x70, 0xb8, 0x3c, 0x85, 0x9e, 0x90, 0x69, 0x17, 0x25, 0xae, 0x6e, 0xe2, 0x16, 0x7d, 0x42, 0x38, 0xdf, 0x74, 0x72, 0x7b, 0x97, 0xa9, 0x9e, 0x40, 0x24, 0x85, 0xdc, 0x64, 0xfa, 0xb1, 0x8b, 0x95, 0xe6, 0xe4, 0x13, 0x72, 0xf1, 0x52, 0x2f, 0xa0, 0xd6, 0x52, 0xc0, 0x11, 0xa7, 0xfe, 0xd5, 0x3b, 0x56, 0xca, 0xbc, 0x01, 0xce, 0x3d, 0xd2, 0x30, 0x97, 0x1d, 0xdc, 0xeb, 0x9d, 0xa9, 0x3e, 0x09, 0xef, 0xee, 0x7f, 0x09, 0x7b, 0x82, 0x43, 0x15, 0x2e, 0xa4, 0x2e, 0x97, 0x21, 0x92, 0x7e, 0x69, 0x21, 0x25, 0xda, 0x46, 0x7c, 0x0c, 0xcd, 0x1d, 0xde, 0x42, 0x11, 0xa2, 0xef, 0xa2, 0xc8, 0x32, 0x9a, 0x82, 0xcf, 0x72, 0x7e, 0x22, 0xa6, 0x11, 0xfa, 0xec, 0x0b, 0x77, 0x99, 0x38, 0x03, 0xf6, 0x80, 0xba, 0xea, 0x75, 0x19, 0xb0, 0x48, 0x02, 0xb2, 0x6b, 0xc0, 0x8c, 0xfb, 0xfe, 0xaf, 0x94, 0x4f, 0x6f, 0xb4, 0xcb, 0x1c, 0x27, 0xf0, 0x41, 0xb6, 0x46, 0x41, 0x68, 0x3d, 0x05, 0x79, 0x6b, 0xcd, 0xb7, 0x20, 0xdc, 0x40, 0x81, 0x58, 0xcb, 0x33, 0xa3, 0xf3, 0x34, 0xdc, 0x63, 0x2d, 0xa5, 0xb5, 0xa1, 0xd1, 0xfd, 0x49, 0x5b, 0x46, 0x94, 0x01, 0xa8, 0xf2, 0xd8, 0x93, 0x2c, 0xbb, 0x57, 0xfe, 0x7c, 0x77, 0x3b, 0x19, 0x6f, 0x3c, 0xaa, 0x23, 0x5b, 0xc0, 0xe7, 0x00, 0x41, 0x97, 0x91, 0xe8, 0x00, 0x12, 0xdf, 0xf6, 0x5c, 0x2e, 0xc6, 0x8e, 0xc6, 0x77, 0x59, 0x78, 0x9b, 0xef, 0x63, 0xb0, 0xd7, 0xbb, 0xc4, 0x0b, 0x60, 0x65, 0x3f, 0xfe, 0xbf, 0x04, 0x3e, 0xae, 0xc2, 0xa5, 0x90, 0xe1, 0x2a, 0x56, 0x3f, 0x4c, 0x3f, 0x7a, 0x7d, 0xda, 0x81, 0x50, 0xea, 0x4c, 0xfe, 0xc3, 0xf8, 0x5c, 0x2b, 0x67, 0xb3, 0x9f, 0x8b, 0x95, 0xda, 0x6f, 0x5d, 0xdd, 0x82, 0x7f, 0x52, 0xa2, 0xcc, 0x57, 0xec, 0xc4, 0x14, 0xd2, 0x4f, 0x1b, 0xcb, 0xea, 0xaf, 0x0e, 0x0f, 0x53, 0xaa, 0x56, 0x63, 0xea, 0x36, 0xa6, 0x89, 0x1a, 0x66, 0xc0, 0x4e, 0xf4, 0x1e, 0x02, 0x43, 0xde, 0xde, 0xc8, 0x9e, 0x88, 0x6e, 0x32, 0xd4, 0xcb, 0x47, 0x24, 0x7c, 0x28, 0x38, 0xd4, 0x95, 0xb6, 0xa3, 0x91, 0x69, 0xc7, 0x8d, 0xfd, 0x15, 0xf5, 0xbf, 0xb1, 0x98, 0x8c, 0x57, 0x51, 0xbf, 0x83, 0x6a, 0x35, 0x10, 0x03, 0x50, 0xe5, 0xf7, 0xfa, 0xf8, 0xa5, 0xb0, 0xdb, 0xfb, 0x42, 0x93, 0xbb, 0x17, 0xf7, 0x36, 0xbe, 0x26, 0x66, 0x61, 0xe2
db 0xaf, 0xe0, 0x22, 0x25, 0x2a, 0xae, 0x78, 0x67, 0x8f, 0x7e, 0x9e, 0x59, 0xd7, 0xa3, 0x71, 0xcc, 0x43, 0x85, 0x09, 0xf9, 0x18, 0x52, 0x7b, 0x01, 0x73, 0xcb, 0x31, 0x18, 0x66, 0x79, 0x67, 0x10, 0x67, 0xd8, 0xdf, 0x43, 0xaf, 0x2d, 0x9a, 0x09, 0x9c, 0xd1, 0x37, 0x7e, 0xf5, 0x1c, 0x3c, 0x4f, 0x15, 0xe1, 0x6f, 0xfd, 0x13, 0x3d, 0x53, 0x81, 0xa9, 0x93, 0x5f, 0x92, 0x41, 0x48, 0xec, 0x87, 0x87, 0x1d, 0x0b, 0xaa, 0xaa, 0xd3, 0xc2, 0x98, 0x20, 0xce, 0x28, 0xaf, 0x9d, 0x84, 0x69, 0x4a, 0xfd, 0xc0, 0x9c, 0x2e, 0x50, 0x20, 0xb2, 0x00, 0xc1, 0x81, 0x2a, 0x32, 0x8e, 0x95, 0x20, 0xa7, 0xca, 0x39, 0x28, 0x12, 0x23, 0x0e, 0x43, 0xd3, 0x82, 0x76, 0x73, 0x3c, 0xbf, 0xa9, 0x98, 0xf6, 0x39, 0x6d, 0xd9, 0x15, 0x33, 0x1e, 0x07, 0x7c, 0x08, 0x12, 0x23, 0xbd, 0xd3, 0x34, 0x2d, 0x9a, 0x23, 0x21, 0x46, 0xf3, 0x9a, 0x04, 0x25, 0x62, 0xeb, 0x7e, 0x9a, 0xaa, 0xb6, 0x26, 0xaa, 0x85, 0x01, 0x3a, 0xd8, 0xfc, 0x57, 0x98, 0xb9, 0xe4, 0xc4, 0xe9, 0x11, 0x3e, 0x22, 0x95, 0x3b, 0x41, 0x2b, 0x02, 0x04, 0x6c, 0x75, 0xa5, 0xf2, 0xaa, 0x09, 0x9e, 0x6f, 0xab, 0x1d, 0x2a, 0x5c, 0xde, 0x21, 0xb1, 0x96, 0x2d, 0x86, 0x3f, 0xd0, 0x07, 0x18, 0x1f, 0x87, 0xc2, 0x8f, 0xdf, 0x6a, 0x57, 0x6d, 0x3f, 0x80, 0xc5, 0x08, 0x19, 0xa5, 0x09, 0x65, 0x3d, 0xdc, 0x9e, 0x80, 0x3c, 0x2a, 0x0e, 0x7a, 0x40, 0x04, 0x0b, 0xcc, 0x61, 0xdb, 0x73, 0xfc, 0xa5, 0x0a, 0x42, 0x18, 0xc1, 0xd5, 0xbd, 0x18, 0x78, 0xa1, 0xe4, 0xde, 0x44, 0xec, 0x79, 0xb0, 0x27, 0xaa, 0x45, 0x21, 0x57, 0x19, 0x75, 0x09, 0x5c, 0x58, 0xd5, 0xb9, 0x6f, 0x3b, 0x48, 0x59, 0x41, 0x3e, 0xfd, 0x17, 0x43, 0x27, 0xc3, 0x8d, 0x76, 0x8e, 0x38, 0x47, 0xe7, 0xd2, 0xea, 0x54, 0x73, 0x8a, 0x65, 0x4c, 0x49, 0x91, 0xaf, 0x29, 0x65, 0x0d, 0x81, 0xa4, 0x77, 0xd7, 0x32, 0xd0, 0x69, 0xd9, 0x6b, 0xa3, 0x9b, 0x24, 0xd6, 0x0a, 0xd2, 0x77, 0x38, 0x59, 0x0b, 0xc8, 0x5c, 0xc7, 0x0b, 0x1d, 0xd1, 0xfa, 0xa7, 0x45, 0x3c, 0xeb, 0x5c, 0x8e, 0x25, 0x35, 0x81, 0x6d, 0x6d, 0xfe, 0xb4, 0x63, 0x89, 0xe4, 0xf0, 0xa8, 0xda, 0xb7, 0xd4, 0xff, 0x5d, 0x28, 0x97, 0x11, 0xf9, 0x8d, 0xab, 0x29, 0xd5, 0xd3, 0x1c, 0x70, 0x20, 0x4c, 0x41, 0x16, 0x42, 0xfd, 0xfc, 0x62, 0x82, 0x40, 0x59, 0x34, 0x28, 0xd0, 0xd5, 0xfc, 0xac, 0x97, 0xb8, 0x82, 0x0e, 0x4b, 0xae, 0x51, 0x28, 0x1a, 0xf1, 0x87, 0xd3, 0x20, 0xa3, 0xe7, 0x74, 0x69, 0x3c, 0x54, 0x8d, 0xc5, 0x56, 0x1d, 0xcd, 0x75, 0xae, 0x88, 0x17, 0x30, 0xdf, 0x46, 0x4a, 0xbc, 0x64, 0xff, 0xa2, 0xc1, 0x62, 0xbd, 0x88, 0x7b, 0x3e, 0xa1, 0x0c, 0xa9, 0x13, 0x0e, 0xc1, 0xb4, 0x24, 0xe6, 0x96, 0x1b, 0x9c, 0x9b, 0xac, 0x44, 0x33, 0x5b, 0xda, 0xd5, 0x88, 0x4d, 0xfe, 0x81, 0x09, 0x07, 0x17, 0xcf, 0x14, 0x05, 0xaf, 0xf8, 0x72, 0x14, 0x49, 0x5f, 0x06, 0x62, 0xab, 0xe0, 0x42, 0x70, 0x12, 0x59, 0x41, 0x0f, 0x18, 0x83, 0x68, 0x6d, 0xc6, 0x3c, 0xea, 0xe0, 0x6d, 0xd4, 0xae, 0xa6, 0xf1, 0x63, 0x21, 0x7f, 0xb5, 0x9d, 0x22, 0xf4, 0xd2, 0x49, 0x49, 0xed, 0x07, 0xb1, 0x11, 0xf9, 0x2e, 0x74, 0xbe, 0x35, 0x47, 0xdc, 0xef, 0x85, 0x0b, 0x4d, 0x46, 0xe6, 0x1f, 0x60, 0x6a, 0xa1, 0x8a, 0x4d, 0x46, 0x87, 0x30, 0x8e, 0x9a, 0xba, 0x97, 0x3e, 0x15, 0xb7, 0x33, 0x76, 0x81, 0x69, 0xdb, 0x82, 0x5e, 0xe6, 0x7b, 0xec, 0xd2, 0x80, 0x7f, 0x17, 0xf1, 0x73, 0xe2
.data_mid:
db 0xd7, 0xa9, 0x35, 0x61, 0x5f, 0xb8, 0xb6, 0x2d, 0x29, 0x34, 0x63, 0xbf, 0xe2, 0x1c, 0x34, 0xe9, 0xf5, 0xff, 0x34, 0x8a, 0x2f, 0xea, 0xd4, 0x3f, 0x3b, 0xfe, 0x6e, 0xdf, 0xa6, 0xd8, 0xc6, 0xb8, 0xc5, 0xff, 0x12, 0x97, 0x53, 0xda, 0x86, 0xa8, 0x0a, 0x12, 0x4e, 0x5d, 0x96, 0x65, 0x51, 0x22, 0xe2, 0x9d, 0x08, 0x71, 0x84, 0x19, 0x8b, 0xbf, 0x29, 0xd3, 0x3f, 0xab, 0xde, 0xe4, 0x27, 0x8b, 0x99, 0xcc, 0xb1, 0x7c, 0xa5, 0x71, 0x91, 0x9a, 0x0b, 0xad, 0x75, 0x86, 0xe3, 0x9c, 0x4e, 0x0c, 0x01, 0xb3, 0x12, 0x33, 0x90, 0x81, 0x7c, 0x71, 0x2c, 0x70, 0x61, 0xd5, 0x39, 0x0c, 0x45, 0xfc, 0x27, 0xaf, 0xbb, 0xd9, 0x26, 0x1b, 0x33, 0xb4, 0x0d, 0xf8, 0xd6, 0x2d, 0x09, 0xc7, 0x8c, 0xbf, 0x48, 0x53, 0x14, 0x94, 0x76, 0x25, 0xc7, 0x0c, 0x69, 0x49, 0x82, 0xb4, 0x2f, 0x48, 0x38, 0x44, 0x9d, 0x90, 0x6d, 0x66, 0x35, 0xe9, 0x3e, 0x2f, 0x2a, 0xb7, 0xe1, 0xb1, 0x2b, 0x99, 0x08, 0x6f, 0x5c, 0x6c, 0xdf, 0xdb, 0x10, 0xe2, 0xaa, 0x86, 0xe7, 0xf8, 0x9e, 0x62, 0xde, 0xa5, 0x81, 0x6b, 0x20, 0x47, 0xa9, 0x06, 0x49, 0xc0, 0x78, 0x8c, 0x70, 0x93, 0x7e, 0xda, 0xda, 0x5e, 0x3b, 0x23, 0xf9, 0xcc, 0x87, 0xdf, 0x48, 0x4f, 0xd6, 0x77, 0xce, 0x45, 0xe1, 0xdc, 0x0c, 0x7a, 0x0c, 0x50, 0x15, 0x63, 0x8c, 0x48, 0xd3, 0x8e, 0xfa, 0xcc, 0xac, 0x1a, 0x83, 0xde, 0xb1, 0x87, 0x2a, 0x58, 0x5c, 0xa5, 0x20, 0x3d, 0xaa, 0x1e, 0x5d, 0x71, 0xa6, 0x57, 0x75, 0x82, 0xb7, 0x33, 0x9e, 0x6b, 0xf3, 0x35, 0x02, 0x98, 0x03, 0xe1, 0x3b, 0xd2, 0x9f, 0x7a, 0x06, 0x85, 0xef, 0x7d, 0xd9, 0xf2, 0x0c, 0x9e, 0xce, 0xb9, 0xce, 0x13, 0x4a, 0x9e, 0x8a, 0x29, 0xe6, 0xe5, 0xe4, 0x39, 0xba, 0xfd, 0xa3, 0x33, 0xa8, 0x13, 0x9e, 0xa5, 0x11, 0x37, 0x69, 0xbc, 0xda, 0x11, 0x49, 0x2d, 0x4a, 0xef, 0x20, 0x8b, 0x7a, 0xb8, 0x9c, 0xc3, 0xaf, 0x26, 0x71, 0xd9, 0xa2, 0xf6, 0x0f, 0x85, 0x87, 0xa8, 0x6c, 0xf9, 0x99, 0xa2, 0xb2, 0x36, 0x2d, 0x78, 0x10, 0xe4, 0x33, 0x8d, 0xa4, 0x63, 0xea, 0x02, 0xb9, 0xac, 0x2f, 0x90, 0x39, 0x2d, 0x0e, 0x2e, 0xf5, 0x08, 0xa5, 0x5c, 0x8e, 0x71, 0x30, 0x0d, 0x1b, 0x84, 0x7a, 0xd7, 0xd4, 0xab, 0x81, 0x82, 0x18, 0x37, 0xf3, 0x28, 0x6f, 0x4e, 0x28, 0x71, 0xda, 0xc9, 0x99, 0x46, 0x14, 0x46, 0x77, 0x01, 0x16, 0x21, 0xae, 0x83, 0x93, 0x86, 0x7f, 0x5a, 0xee, 0xd5, 0xdf, 0x48, 0x5b, 0x15, 0xc8, 0x09, 0x30, 0x8f, 0x01, 0xcc, 0x95, 0x30, 0xd9, 0xf7, 0x72, 0x97, 0xfd, 0x9d, 0xec, 0x9f, 0xbf, 0x5c, 0xbf, 0x4f, 0xca, 0x33, 0xb4, 0xd2, 0xa2, 0xb9, 0x08, 0x9c, 0x40, 0x25, 0x3f, 0x86, 0xdc, 0x83, 0x70, 0x2f, 0xfb, 0x2a, 0xf8, 0x61, 0x1f, 0xa1, 0x1f, 0x36, 0x04, 0xe2, 0xef, 0x1c, 0xa4, 0xcd, 0x3c, 0x7f, 0xc5, 0x73, 0x9c, 0x2e, 0xeb, 0x03, 0x79, 0xd1, 0x02, 0xfc, 0x6f, 0xbd, 0x5a, 0x95, 0xb2, 0xf6, 0x25, 0x96, 0xe6, 0x80, 0x0a, 0xc5, 0xc7, 0xca, 0x8d, 0x31, 0xae, 0xf0, 0x49, 0xcf, 0x43, 0x06, 0x27, 0x7f, 0x25, 0xc7, 0x4c, 0xb7, 0xfc, 0x73, 0xd3, 0x04, 0xd3, 0xb9, 0x9f, 0x74, 0xed, 0x9e, 0x3c, 0xf0, 0xcf, 0x26, 0x2b, 0xd9, 0xcb, 0x78, 0x2a, 0xef, 0x72, 0xf7, 0xb6, 0x78, 0x30, 0x2d, 0x8c, 0x83, 0x73, 0x66, 0x74, 0x3d, 0x66, 0x0a, 0x74, 0x5a, 0x3f, 0x9f, 0x6e, 0x56, 0x68, 0x01, 0xc2, 0xca, 0x2b, 0xa1, 0x25, 0x36, 0x9c, 0x3b, 0xa4, 0x5e, 0x44, 0xf1, 0x18, 0x1d, 0xb6, 0x1a, 0x3a, 0xee, 0x8d, 0x67, 0x34, 0x9c
db 0xdd, 0x48, 0x14, 0xc2, 0x5f, 0xd8, 0xe5, 0x71, 0x22, 0xbf, 0xbc, 0x84, 0xda, 0xc1, 0xb1, 0x22, 0x55, 0xa4, 0x63, 0x41, 0x77, 0xac, 0x40, 0x2d, 0x44, 0x73, 0x8c, 0x14, 0xba, 0x5e, 0x63, 0x68, 0x65, 0x61, 0x6d, 0xec, 0xe2, 0x6d, 0x37, 0x22, 0x04, 0xeb, 0xc7, 0xd4, 0xc9, 0x62, 0x56, 0x13, 0x96, 0x29, 0x03, 0xf4, 0x55, 0xe2, 0x58, 0x7d, 0xda, 0x52, 0x2e, 0x94, 0x07, 0xe6, 0xef, 0xc0, 0xee, 0x9e, 0x0b, 0xf7, 0xcd, 0x13, 0x8b, 0x7d, 0xea, 0xdc, 0xf8, 0xf1, 0xcb, 0xad, 0x49, 0x97, 0xc9, 0x98, 0x0b, 0xcf, 0x84, 0x8e, 0x8e, 0xbb, 0x06, 0x2e, 0x54, 0xf5, 0xa7, 0xbd, 0x70, 0x7e, 0x38, 0x69, 0x8d, 0xb0, 0x01, 0x7b, 0x41, 0x80, 0x09, 0x44, 0xfd, 0x7e, 0x21, 0xb4, 0xbe, 0x6b, 0x4a, 0xb7, 0xca, 0x2d, 0x19, 0xfe, 0x6d, 0xd6, 0x11, 0x29, 0xbb, 0xb2, 0x16, 0xf1, 0xe7, 0x92, 0x71, 0xda, 0x7e, 0x68, 0x3a, 0xe0, 0xea, 0x89, 0x8d, 0xe0, 0x44, 0x48, 0x25, 0x92, 0x37, 0x54, 0x26, 0xf2, 0xab, 0xb3, 0x3b, 0xdb, 0xbb, 0x2b, 0x5c, 0xf5, 0xbc, 0xc7, 0x97, 0xdb, 0xc7, 0x49, 0x25, 0x7c, 0xc2, 0x80, 0x02, 0x69, 0xd4, 0xda, 0xda, 0xe1, 0x04, 0xf3, 0x19, 0xb8, 0xc9, 0xb2, 0xfb, 0x1e, 0x47, 0xa9, 0x0c, 0xa3, 0x48, 0xce, 0xc2, 0x9e, 0x3b, 0x28, 0x23, 0x5a, 0x20, 0x44, 0x77, 0x40, 0xe2, 0xd7, 0x20, 0xd5, 0x71, 0x6f, 0xd4, 0x3c, 0x68, 0x38, 0x9b, 0x89, 0x2e, 0x2d, 0xa8, 0x1f, 0x99, 0xb5, 0x8a, 0x66, 0x07, 0x59, 0x75, 0x9e, 0xf8, 0xd9, 0xbe, 0x85, 0x6a, 0x20, 0x92, 0x9d, 0xd2, 0x5e, 0x45, 0xc0, 0x60, 0xbe, 0x85, 0x0b, 0x84, 0x47, 0xf5, 0xa8, 0x43, 0x87, 0xf1, 0x21, 0x21, 0xb0, 0x3b, 0x04, 0x13, 0x16, 0x3e, 0xdf, 0xc3, 0xc6, 0x04, 0x73, 0xcd, 0x92, 0x76, 0xfb, 0xe7, 0x9c, 0xd3, 0x46, 0x11, 0x78, 0xca, 0x12, 0xd9, 0x4a, 0x35, 0xf1, 0x6e, 0x89, 0x8b, 0xe9, 0x7a, 0x04, 0xba, 0x18, 0x25, 0x7c, 0x9e, 0xe6, 0x4f, 0xc2, 0x56, 0x05, 0x72, 0xc3, 0x76, 0xee, 0x7d, 0x77, 0x19, 0x7a, 0x73, 0x2c, 0x81, 0xb8, 0xc7, 0xd9, 0x7f, 0x17, 0x5d, 0x30, 0xda, 0x77, 0x3c, 0x14, 0x88, 0xe8, 0xe4, 0xbf, 0xee, 0x21, 0x1c, 0x29, 0x4e, 0x58, 0xa8, 0x8a, 0x5c, 0xae, 0xa2, 0x1c, 0x7c, 0x25, 0x7c, 0x1c, 0x39, 0xa4, 0x28, 0x4b, 0x78, 0x52, 0xae, 0x2c, 0xbb, 0x5f, 0xbf, 0x51, 0x09, 0x20, 0x76, 0xb2, 0x7d, 0xb1, 0x63, 0x84, 0xc5, 0x49, 0x8a, 0x73, 0xdb, 0x76, 0x1d, 0x25, 0x31, 0xf2, 0x1e, 0x19, 0x38, 0xc8, 0x3b, 0x51, 0x3c, 0x13, 0x52, 0x84, 0xae, 0xc2, 0xe4, 0x8a, 0x57, 0x0d, 0xde, 0x8d, 0x18, 0x48, 0x9a, 0xbd, 0xbf, 0xf3, 0xea, 0x79, 0x17, 0x06, 0x96, 0x72, 0x08, 0x60, 0x95, 0xf9, 0x6f, 0x25, 0x0c, 0xb7, 0x9d, 0x98, 0x23, 0x01, 0xc8, 0x7a, 0xdb, 0x75, 0x63, 0x64, 0x14, 0x5e, 0x10, 0xf5, 0x16, 0x48, 0xbc, 0xc6, 0x7e, 0x24, 0xf3, 0xad, 0x57, 0x3f, 0x7d, 0x6c, 0xab, 0x18, 0x8c, 0x12, 0xc5, 0x0c, 0xd8, 0xb5, 0x1e, 0x43, 0x7c, 0x23, 0x17, 0x48, 0xba, 0x76, 0x3b, 0xd9, 0x2b, 0xae, 0x1b, 0xef, 0x58, 0xfa, 0x87, 0xad, 0x9b, 0x6d, 0xf9, 0xab, 0xa8, 0x3c, 0xfc, 0x59, 0x67, 0xa6, 0x2c, 0xc7, 0x75, 0xa4, 0x97, 0xca, 0x18, 0x18, 0x04, 0x2c, 0xb3, 0x0e, 0xa9, 0x69, 0x33, 0x67, 0xa2, 0xc6, 0xbc, 0x98, 0x48, 0x71, 0x11, 0x05, 0x30, 0xf6, 0xa9, 0x61, 0x40, 0x46, 0xf1, 0x41, 0x37, 0xd0, 0x6b, 0x7c, 0x1f, 0x03, 0x5c, 0xe9, 0xf4, 0x59, 0x1d, 0x35, 0xf0, 0x98, 0x42, 0x4a, 0x92, 0x2a, 0xc3, 0x9a, 0xb8, 0xa5
@@ -0,0 +1,73 @@
%ifdef CONFIG
{
"RegData": {
"XMM1": ["0", "0", "0", "0"],
"XMM7": ["0x2db6b85f6135a9d7", "0xe273f1177f80d2ec", "0", "0"]
},
"Mode": "32BIT",
"Env": { "FEX_HOSTFEATURES" : "enableavx" },
"HostFeatures": ["AVX"]
}
%endif
; 128-bit
; 4x displacement
; 64-bit indexes
; Calculate an address that heavily overflows
lea eax, [rel .data_mid]
mov ebx, -1
sub ebx, eax
sar ebx, 2
mov [rel .index_overflow + 0 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 8
sar ebx, 2
mov [rel .index_overflow + 1 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 16
sar ebx, 2
mov [rel .index_overflow + 2 * 8], ebx
mov ebx, -1
sub ebx, eax
add bx, 16
sar ebx, 2
mov [rel .index_overflow + 3 * 8], ebx
; Calculate new base which offsets from the overflow
lea eax, [rel .data_mid]
shl eax, 1
vmovapd ymm7, [rel .data]
vmovaps xmm0, [rel .index_overflow]
vmovaps xmm1, [rel .mask_1111]
vgatherqpd xmm7, [xmm0 * 4 + eax + 4], xmm1
hlt
align 32
.mask_1111:
dq 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000
; Indexing is a signed 64-bit integer.
.index_overflow:
dq 0, 0, 0, 0
; Random data, 512-byte per line
.data:
db 0x1d, 0x30, 0x09, 0x22, 0x56, 0xa2, 0xcd, 0xf1, 0xb7, 0x62, 0x91, 0x40, 0x67, 0x07, 0x35, 0x0f, 0x59, 0x33, 0x63, 0x52, 0x26, 0xd2, 0x2f, 0x00, 0x8a, 0x36, 0xb7, 0xf7, 0xaf, 0x4f, 0xa1, 0xc0, 0xe3, 0xbe, 0x99, 0x67, 0x5f, 0x80, 0x60, 0xcb, 0x43, 0xfa, 0x5b, 0x86, 0xb1, 0x11, 0xbc, 0xb3, 0x7b, 0x43, 0x5b, 0x45, 0x9e, 0x33, 0x89, 0xb5, 0x1b, 0xb9, 0x33, 0x4f, 0xdb, 0x5d, 0x93, 0xd6, 0x4f, 0xbc, 0x37, 0xde, 0xeb, 0xdb, 0x43, 0x2b, 0x05, 0x60, 0xb8, 0x98, 0x5c, 0xa3, 0xe3, 0x1b, 0x33, 0x03, 0x29, 0x4b, 0x12, 0x4c, 0x1e, 0xe6, 0x5e, 0x0e, 0x6c, 0xa1, 0xb9, 0x36, 0xfa, 0x6c, 0x7f, 0xc6, 0xa8, 0x38, 0x73, 0x2a, 0x0a, 0x25, 0x69, 0xa5, 0x97, 0x3f, 0x24, 0x00, 0x30, 0x4d, 0x27, 0xb3, 0x94, 0x48, 0xef, 0x47, 0x98, 0x71, 0x0d, 0x56, 0x76, 0xec, 0x41, 0x12, 0x9b, 0x7b, 0x9c, 0xf5, 0x85, 0x07, 0x2d, 0x6b, 0xc6, 0xc1, 0x2e, 0x72, 0x22, 0x5a, 0x43, 0xff, 0x1e, 0xec, 0x67, 0x2b, 0x31, 0x96, 0x14, 0x2c, 0xb1, 0x5f, 0x5d, 0x0c, 0xc9, 0xad, 0x15, 0x5f, 0xab, 0x66, 0x14, 0x1c, 0x72, 0xfa, 0x23, 0xef, 0x9f, 0x77, 0xf6, 0x50, 0xb0, 0x70, 0xb8, 0x3c, 0x85, 0x9e, 0x90, 0x69, 0x17, 0x25, 0xae, 0x6e, 0xe2, 0x16, 0x7d, 0x42, 0x38, 0xdf, 0x74, 0x72, 0x7b, 0x97, 0xa9, 0x9e, 0x40, 0x24, 0x85, 0xdc, 0x64, 0xfa, 0xb1, 0x8b, 0x95, 0xe6, 0xe4, 0x13, 0x72, 0xf1, 0x52, 0x2f, 0xa0, 0xd6, 0x52, 0xc0, 0x11, 0xa7, 0xfe, 0xd5, 0x3b, 0x56, 0xca, 0xbc, 0x01, 0xce, 0x3d, 0xd2, 0x30, 0x97, 0x1d, 0xdc, 0xeb, 0x9d, 0xa9, 0x3e, 0x09, 0xef, 0xee, 0x7f, 0x09, 0x7b, 0x82, 0x43, 0x15, 0x2e, 0xa4, 0x2e, 0x97, 0x21, 0x92, 0x7e, 0x69, 0x21, 0x25, 0xda, 0x46, 0x7c, 0x0c, 0xcd, 0x1d, 0xde, 0x42, 0x11, 0xa2, 0xef, 0xa2, 0xc8, 0x32, 0x9a, 0x82, 0xcf, 0x72, 0x7e, 0x22, 0xa6, 0x11, 0xfa, 0xec, 0x0b, 0x77, 0x99, 0x38, 0x03, 0xf6, 0x80, 0xba, 0xea, 0x75, 0x19, 0xb0, 0x48, 0x02, 0xb2, 0x6b, 0xc0, 0x8c, 0xfb, 0xfe, 0xaf, 0x94, 0x4f, 0x6f, 0xb4, 0xcb, 0x1c, 0x27, 0xf0, 0x41, 0xb6, 0x46, 0x41, 0x68, 0x3d, 0x05, 0x79, 0x6b, 0xcd, 0xb7, 0x20, 0xdc, 0x40, 0x81, 0x58, 0xcb, 0x33, 0xa3, 0xf3, 0x34, 0xdc, 0x63, 0x2d, 0xa5, 0xb5, 0xa1, 0xd1, 0xfd, 0x49, 0x5b, 0x46, 0x94, 0x01, 0xa8, 0xf2, 0xd8, 0x93, 0x2c, 0xbb, 0x57, 0xfe, 0x7c, 0x77, 0x3b, 0x19, 0x6f, 0x3c, 0xaa, 0x23, 0x5b, 0xc0, 0xe7, 0x00, 0x41, 0x97, 0x91, 0xe8, 0x00, 0x12, 0xdf, 0xf6, 0x5c, 0x2e, 0xc6, 0x8e, 0xc6, 0x77, 0x59, 0x78, 0x9b, 0xef, 0x63, 0xb0, 0xd7, 0xbb, 0xc4, 0x0b, 0x60, 0x65, 0x3f, 0xfe, 0xbf, 0x04, 0x3e, 0xae, 0xc2, 0xa5, 0x90, 0xe1, 0x2a, 0x56, 0x3f, 0x4c, 0x3f, 0x7a, 0x7d, 0xda, 0x81, 0x50, 0xea, 0x4c, 0xfe, 0xc3, 0xf8, 0x5c, 0x2b, 0x67, 0xb3, 0x9f, 0x8b, 0x95, 0xda, 0x6f, 0x5d, 0xdd, 0x82, 0x7f, 0x52, 0xa2, 0xcc, 0x57, 0xec, 0xc4, 0x14, 0xd2, 0x4f, 0x1b, 0xcb, 0xea, 0xaf, 0x0e, 0x0f, 0x53, 0xaa, 0x56, 0x63, 0xea, 0x36, 0xa6, 0x89, 0x1a, 0x66, 0xc0, 0x4e, 0xf4, 0x1e, 0x02, 0x43, 0xde, 0xde, 0xc8, 0x9e, 0x88, 0x6e, 0x32, 0xd4, 0xcb, 0x47, 0x24, 0x7c, 0x28, 0x38, 0xd4, 0x95, 0xb6, 0xa3, 0x91, 0x69, 0xc7, 0x8d, 0xfd, 0x15, 0xf5, 0xbf, 0xb1, 0x98, 0x8c, 0x57, 0x51, 0xbf, 0x83, 0x6a, 0x35, 0x10, 0x03, 0x50, 0xe5, 0xf7, 0xfa, 0xf8, 0xa5, 0xb0, 0xdb, 0xfb, 0x42, 0x93, 0xbb, 0x17, 0xf7, 0x36, 0xbe, 0x26, 0x66, 0x61, 0xe2
db 0xaf, 0xe0, 0x22, 0x25, 0x2a, 0xae, 0x78, 0x67, 0x8f, 0x7e, 0x9e, 0x59, 0xd7, 0xa3, 0x71, 0xcc, 0x43, 0x85, 0x09, 0xf9, 0x18, 0x52, 0x7b, 0x01, 0x73, 0xcb, 0x31, 0x18, 0x66, 0x79, 0x67, 0x10, 0x67, 0xd8, 0xdf, 0x43, 0xaf, 0x2d, 0x9a, 0x09, 0x9c, 0xd1, 0x37, 0x7e, 0xf5, 0x1c, 0x3c, 0x4f, 0x15, 0xe1, 0x6f, 0xfd, 0x13, 0x3d, 0x53, 0x81, 0xa9, 0x93, 0x5f, 0x92, 0x41, 0x48, 0xec, 0x87, 0x87, 0x1d, 0x0b, 0xaa, 0xaa, 0xd3, 0xc2, 0x98, 0x20, 0xce, 0x28, 0xaf, 0x9d, 0x84, 0x69, 0x4a, 0xfd, 0xc0, 0x9c, 0x2e, 0x50, 0x20, 0xb2, 0x00, 0xc1, 0x81, 0x2a, 0x32, 0x8e, 0x95, 0x20, 0xa7, 0xca, 0x39, 0x28, 0x12, 0x23, 0x0e, 0x43, 0xd3, 0x82, 0x76, 0x73, 0x3c, 0xbf, 0xa9, 0x98, 0xf6, 0x39, 0x6d, 0xd9, 0x15, 0x33, 0x1e, 0x07, 0x7c, 0x08, 0x12, 0x23, 0xbd, 0xd3, 0x34, 0x2d, 0x9a, 0x23, 0x21, 0x46, 0xf3, 0x9a, 0x04, 0x25, 0x62, 0xeb, 0x7e, 0x9a, 0xaa, 0xb6, 0x26, 0xaa, 0x85, 0x01, 0x3a, 0xd8, 0xfc, 0x57, 0x98, 0xb9, 0xe4, 0xc4, 0xe9, 0x11, 0x3e, 0x22, 0x95, 0x3b, 0x41, 0x2b, 0x02, 0x04, 0x6c, 0x75, 0xa5, 0xf2, 0xaa, 0x09, 0x9e, 0x6f, 0xab, 0x1d, 0x2a, 0x5c, 0xde, 0x21, 0xb1, 0x96, 0x2d, 0x86, 0x3f, 0xd0, 0x07, 0x18, 0x1f, 0x87, 0xc2, 0x8f, 0xdf, 0x6a, 0x57, 0x6d, 0x3f, 0x80, 0xc5, 0x08, 0x19, 0xa5, 0x09, 0x65, 0x3d, 0xdc, 0x9e, 0x80, 0x3c, 0x2a, 0x0e, 0x7a, 0x40, 0x04, 0x0b, 0xcc, 0x61, 0xdb, 0x73, 0xfc, 0xa5, 0x0a, 0x42, 0x18, 0xc1, 0xd5, 0xbd, 0x18, 0x78, 0xa1, 0xe4, 0xde, 0x44, 0xec, 0x79, 0xb0, 0x27, 0xaa, 0x45, 0x21, 0x57, 0x19, 0x75, 0x09, 0x5c, 0x58, 0xd5, 0xb9, 0x6f, 0x3b, 0x48, 0x59, 0x41, 0x3e, 0xfd, 0x17, 0x43, 0x27, 0xc3, 0x8d, 0x76, 0x8e, 0x38, 0x47, 0xe7, 0xd2, 0xea, 0x54, 0x73, 0x8a, 0x65, 0x4c, 0x49, 0x91, 0xaf, 0x29, 0x65, 0x0d, 0x81, 0xa4, 0x77, 0xd7, 0x32, 0xd0, 0x69, 0xd9, 0x6b, 0xa3, 0x9b, 0x24, 0xd6, 0x0a, 0xd2, 0x77, 0x38, 0x59, 0x0b, 0xc8, 0x5c, 0xc7, 0x0b, 0x1d, 0xd1, 0xfa, 0xa7, 0x45, 0x3c, 0xeb, 0x5c, 0x8e, 0x25, 0x35, 0x81, 0x6d, 0x6d, 0xfe, 0xb4, 0x63, 0x89, 0xe4, 0xf0, 0xa8, 0xda, 0xb7, 0xd4, 0xff, 0x5d, 0x28, 0x97, 0x11, 0xf9, 0x8d, 0xab, 0x29, 0xd5, 0xd3, 0x1c, 0x70, 0x20, 0x4c, 0x41, 0x16, 0x42, 0xfd, 0xfc, 0x62, 0x82, 0x40, 0x59, 0x34, 0x28, 0xd0, 0xd5, 0xfc, 0xac, 0x97, 0xb8, 0x82, 0x0e, 0x4b, 0xae, 0x51, 0x28, 0x1a, 0xf1, 0x87, 0xd3, 0x20, 0xa3, 0xe7, 0x74, 0x69, 0x3c, 0x54, 0x8d, 0xc5, 0x56, 0x1d, 0xcd, 0x75, 0xae, 0x88, 0x17, 0x30, 0xdf, 0x46, 0x4a, 0xbc, 0x64, 0xff, 0xa2, 0xc1, 0x62, 0xbd, 0x88, 0x7b, 0x3e, 0xa1, 0x0c, 0xa9, 0x13, 0x0e, 0xc1, 0xb4, 0x24, 0xe6, 0x96, 0x1b, 0x9c, 0x9b, 0xac, 0x44, 0x33, 0x5b, 0xda, 0xd5, 0x88, 0x4d, 0xfe, 0x81, 0x09, 0x07, 0x17, 0xcf, 0x14, 0x05, 0xaf, 0xf8, 0x72, 0x14, 0x49, 0x5f, 0x06, 0x62, 0xab, 0xe0, 0x42, 0x70, 0x12, 0x59, 0x41, 0x0f, 0x18, 0x83, 0x68, 0x6d, 0xc6, 0x3c, 0xea, 0xe0, 0x6d, 0xd4, 0xae, 0xa6, 0xf1, 0x63, 0x21, 0x7f, 0xb5, 0x9d, 0x22, 0xf4, 0xd2, 0x49, 0x49, 0xed, 0x07, 0xb1, 0x11, 0xf9, 0x2e, 0x74, 0xbe, 0x35, 0x47, 0xdc, 0xef, 0x85, 0x0b, 0x4d, 0x46, 0xe6, 0x1f, 0x60, 0x6a, 0xa1, 0x8a, 0x4d, 0x46, 0x87, 0x30, 0x8e, 0x9a, 0xba, 0x97, 0x3e, 0x15, 0xb7, 0x33, 0x76, 0x81, 0x69, 0xdb, 0x82, 0x5e, 0xe6, 0x7b, 0xec, 0xd2, 0x80, 0x7f, 0x17, 0xf1, 0x73, 0xe2
.data_mid:
db 0xd7, 0xa9, 0x35, 0x61, 0x5f, 0xb8, 0xb6, 0x2d, 0x29, 0x34, 0x63, 0xbf, 0xe2, 0x1c, 0x34, 0xe9, 0xf5, 0xff, 0x34, 0x8a, 0x2f, 0xea, 0xd4, 0x3f, 0x3b, 0xfe, 0x6e, 0xdf, 0xa6, 0xd8, 0xc6, 0xb8, 0xc5, 0xff, 0x12, 0x97, 0x53, 0xda, 0x86, 0xa8, 0x0a, 0x12, 0x4e, 0x5d, 0x96, 0x65, 0x51, 0x22, 0xe2, 0x9d, 0x08, 0x71, 0x84, 0x19, 0x8b, 0xbf, 0x29, 0xd3, 0x3f, 0xab, 0xde, 0xe4, 0x27, 0x8b, 0x99, 0xcc, 0xb1, 0x7c, 0xa5, 0x71, 0x91, 0x9a, 0x0b, 0xad, 0x75, 0x86, 0xe3, 0x9c, 0x4e, 0x0c, 0x01, 0xb3, 0x12, 0x33, 0x90, 0x81, 0x7c, 0x71, 0x2c, 0x70, 0x61, 0xd5, 0x39, 0x0c, 0x45, 0xfc, 0x27, 0xaf, 0xbb, 0xd9, 0x26, 0x1b, 0x33, 0xb4, 0x0d, 0xf8, 0xd6, 0x2d, 0x09, 0xc7, 0x8c, 0xbf, 0x48, 0x53, 0x14, 0x94, 0x76, 0x25, 0xc7, 0x0c, 0x69, 0x49, 0x82, 0xb4, 0x2f, 0x48, 0x38, 0x44, 0x9d, 0x90, 0x6d, 0x66, 0x35, 0xe9, 0x3e, 0x2f, 0x2a, 0xb7, 0xe1, 0xb1, 0x2b, 0x99, 0x08, 0x6f, 0x5c, 0x6c, 0xdf, 0xdb, 0x10, 0xe2, 0xaa, 0x86, 0xe7, 0xf8, 0x9e, 0x62, 0xde, 0xa5, 0x81, 0x6b, 0x20, 0x47, 0xa9, 0x06, 0x49, 0xc0, 0x78, 0x8c, 0x70, 0x93, 0x7e, 0xda, 0xda, 0x5e, 0x3b, 0x23, 0xf9, 0xcc, 0x87, 0xdf, 0x48, 0x4f, 0xd6, 0x77, 0xce, 0x45, 0xe1, 0xdc, 0x0c, 0x7a, 0x0c, 0x50, 0x15, 0x63, 0x8c, 0x48, 0xd3, 0x8e, 0xfa, 0xcc, 0xac, 0x1a, 0x83, 0xde, 0xb1, 0x87, 0x2a, 0x58, 0x5c, 0xa5, 0x20, 0x3d, 0xaa, 0x1e, 0x5d, 0x71, 0xa6, 0x57, 0x75, 0x82, 0xb7, 0x33, 0x9e, 0x6b, 0xf3, 0x35, 0x02, 0x98, 0x03, 0xe1, 0x3b, 0xd2, 0x9f, 0x7a, 0x06, 0x85, 0xef, 0x7d, 0xd9, 0xf2, 0x0c, 0x9e, 0xce, 0xb9, 0xce, 0x13, 0x4a, 0x9e, 0x8a, 0x29, 0xe6, 0xe5, 0xe4, 0x39, 0xba, 0xfd, 0xa3, 0x33, 0xa8, 0x13, 0x9e, 0xa5, 0x11, 0x37, 0x69, 0xbc, 0xda, 0x11, 0x49, 0x2d, 0x4a, 0xef, 0x20, 0x8b, 0x7a, 0xb8, 0x9c, 0xc3, 0xaf, 0x26, 0x71, 0xd9, 0xa2, 0xf6, 0x0f, 0x85, 0x87, 0xa8, 0x6c, 0xf9, 0x99, 0xa2, 0xb2, 0x36, 0x2d, 0x78, 0x10, 0xe4, 0x33, 0x8d, 0xa4, 0x63, 0xea, 0x02, 0xb9, 0xac, 0x2f, 0x90, 0x39, 0x2d, 0x0e, 0x2e, 0xf5, 0x08, 0xa5, 0x5c, 0x8e, 0x71, 0x30, 0x0d, 0x1b, 0x84, 0x7a, 0xd7, 0xd4, 0xab, 0x81, 0x82, 0x18, 0x37, 0xf3, 0x28, 0x6f, 0x4e, 0x28, 0x71, 0xda, 0xc9, 0x99, 0x46, 0x14, 0x46, 0x77, 0x01, 0x16, 0x21, 0xae, 0x83, 0x93, 0x86, 0x7f, 0x5a, 0xee, 0xd5, 0xdf, 0x48, 0x5b, 0x15, 0xc8, 0x09, 0x30, 0x8f, 0x01, 0xcc, 0x95, 0x30, 0xd9, 0xf7, 0x72, 0x97, 0xfd, 0x9d, 0xec, 0x9f, 0xbf, 0x5c, 0xbf, 0x4f, 0xca, 0x33, 0xb4, 0xd2, 0xa2, 0xb9, 0x08, 0x9c, 0x40, 0x25, 0x3f, 0x86, 0xdc, 0x83, 0x70, 0x2f, 0xfb, 0x2a, 0xf8, 0x61, 0x1f, 0xa1, 0x1f, 0x36, 0x04, 0xe2, 0xef, 0x1c, 0xa4, 0xcd, 0x3c, 0x7f, 0xc5, 0x73, 0x9c, 0x2e, 0xeb, 0x03, 0x79, 0xd1, 0x02, 0xfc, 0x6f, 0xbd, 0x5a, 0x95, 0xb2, 0xf6, 0x25, 0x96, 0xe6, 0x80, 0x0a, 0xc5, 0xc7, 0xca, 0x8d, 0x31, 0xae, 0xf0, 0x49, 0xcf, 0x43, 0x06, 0x27, 0x7f, 0x25, 0xc7, 0x4c, 0xb7, 0xfc, 0x73, 0xd3, 0x04, 0xd3, 0xb9, 0x9f, 0x74, 0xed, 0x9e, 0x3c, 0xf0, 0xcf, 0x26, 0x2b, 0xd9, 0xcb, 0x78, 0x2a, 0xef, 0x72, 0xf7, 0xb6, 0x78, 0x30, 0x2d, 0x8c, 0x83, 0x73, 0x66, 0x74, 0x3d, 0x66, 0x0a, 0x74, 0x5a, 0x3f, 0x9f, 0x6e, 0x56, 0x68, 0x01, 0xc2, 0xca, 0x2b, 0xa1, 0x25, 0x36, 0x9c, 0x3b, 0xa4, 0x5e, 0x44, 0xf1, 0x18, 0x1d, 0xb6, 0x1a, 0x3a, 0xee, 0x8d, 0x67, 0x34, 0x9c
db 0xdd, 0x48, 0x14, 0xc2, 0x5f, 0xd8, 0xe5, 0x71, 0x22, 0xbf, 0xbc, 0x84, 0xda, 0xc1, 0xb1, 0x22, 0x55, 0xa4, 0x63, 0x41, 0x77, 0xac, 0x40, 0x2d, 0x44, 0x73, 0x8c, 0x14, 0xba, 0x5e, 0x63, 0x68, 0x65, 0x61, 0x6d, 0xec, 0xe2, 0x6d, 0x37, 0x22, 0x04, 0xeb, 0xc7, 0xd4, 0xc9, 0x62, 0x56, 0x13, 0x96, 0x29, 0x03, 0xf4, 0x55, 0xe2, 0x58, 0x7d, 0xda, 0x52, 0x2e, 0x94, 0x07, 0xe6, 0xef, 0xc0, 0xee, 0x9e, 0x0b, 0xf7, 0xcd, 0x13, 0x8b, 0x7d, 0xea, 0xdc, 0xf8, 0xf1, 0xcb, 0xad, 0x49, 0x97, 0xc9, 0x98, 0x0b, 0xcf, 0x84, 0x8e, 0x8e, 0xbb, 0x06, 0x2e, 0x54, 0xf5, 0xa7, 0xbd, 0x70, 0x7e, 0x38, 0x69, 0x8d, 0xb0, 0x01, 0x7b, 0x41, 0x80, 0x09, 0x44, 0xfd, 0x7e, 0x21, 0xb4, 0xbe, 0x6b, 0x4a, 0xb7, 0xca, 0x2d, 0x19, 0xfe, 0x6d, 0xd6, 0x11, 0x29, 0xbb, 0xb2, 0x16, 0xf1, 0xe7, 0x92, 0x71, 0xda, 0x7e, 0x68, 0x3a, 0xe0, 0xea, 0x89, 0x8d, 0xe0, 0x44, 0x48, 0x25, 0x92, 0x37, 0x54, 0x26, 0xf2, 0xab, 0xb3, 0x3b, 0xdb, 0xbb, 0x2b, 0x5c, 0xf5, 0xbc, 0xc7, 0x97, 0xdb, 0xc7, 0x49, 0x25, 0x7c, 0xc2, 0x80, 0x02, 0x69, 0xd4, 0xda, 0xda, 0xe1, 0x04, 0xf3, 0x19, 0xb8, 0xc9, 0xb2, 0xfb, 0x1e, 0x47, 0xa9, 0x0c, 0xa3, 0x48, 0xce, 0xc2, 0x9e, 0x3b, 0x28, 0x23, 0x5a, 0x20, 0x44, 0x77, 0x40, 0xe2, 0xd7, 0x20, 0xd5, 0x71, 0x6f, 0xd4, 0x3c, 0x68, 0x38, 0x9b, 0x89, 0x2e, 0x2d, 0xa8, 0x1f, 0x99, 0xb5, 0x8a, 0x66, 0x07, 0x59, 0x75, 0x9e, 0xf8, 0xd9, 0xbe, 0x85, 0x6a, 0x20, 0x92, 0x9d, 0xd2, 0x5e, 0x45, 0xc0, 0x60, 0xbe, 0x85, 0x0b, 0x84, 0x47, 0xf5, 0xa8, 0x43, 0x87, 0xf1, 0x21, 0x21, 0xb0, 0x3b, 0x04, 0x13, 0x16, 0x3e, 0xdf, 0xc3, 0xc6, 0x04, 0x73, 0xcd, 0x92, 0x76, 0xfb, 0xe7, 0x9c, 0xd3, 0x46, 0x11, 0x78, 0xca, 0x12, 0xd9, 0x4a, 0x35, 0xf1, 0x6e, 0x89, 0x8b, 0xe9, 0x7a, 0x04, 0xba, 0x18, 0x25, 0x7c, 0x9e, 0xe6, 0x4f, 0xc2, 0x56, 0x05, 0x72, 0xc3, 0x76, 0xee, 0x7d, 0x77, 0x19, 0x7a, 0x73, 0x2c, 0x81, 0xb8, 0xc7, 0xd9, 0x7f, 0x17, 0x5d, 0x30, 0xda, 0x77, 0x3c, 0x14, 0x88, 0xe8, 0xe4, 0xbf, 0xee, 0x21, 0x1c, 0x29, 0x4e, 0x58, 0xa8, 0x8a, 0x5c, 0xae, 0xa2, 0x1c, 0x7c, 0x25, 0x7c, 0x1c, 0x39, 0xa4, 0x28, 0x4b, 0x78, 0x52, 0xae, 0x2c, 0xbb, 0x5f, 0xbf, 0x51, 0x09, 0x20, 0x76, 0xb2, 0x7d, 0xb1, 0x63, 0x84, 0xc5, 0x49, 0x8a, 0x73, 0xdb, 0x76, 0x1d, 0x25, 0x31, 0xf2, 0x1e, 0x19, 0x38, 0xc8, 0x3b, 0x51, 0x3c, 0x13, 0x52, 0x84, 0xae, 0xc2, 0xe4, 0x8a, 0x57, 0x0d, 0xde, 0x8d, 0x18, 0x48, 0x9a, 0xbd, 0xbf, 0xf3, 0xea, 0x79, 0x17, 0x06, 0x96, 0x72, 0x08, 0x60, 0x95, 0xf9, 0x6f, 0x25, 0x0c, 0xb7, 0x9d, 0x98, 0x23, 0x01, 0xc8, 0x7a, 0xdb, 0x75, 0x63, 0x64, 0x14, 0x5e, 0x10, 0xf5, 0x16, 0x48, 0xbc, 0xc6, 0x7e, 0x24, 0xf3, 0xad, 0x57, 0x3f, 0x7d, 0x6c, 0xab, 0x18, 0x8c, 0x12, 0xc5, 0x0c, 0xd8, 0xb5, 0x1e, 0x43, 0x7c, 0x23, 0x17, 0x48, 0xba, 0x76, 0x3b, 0xd9, 0x2b, 0xae, 0x1b, 0xef, 0x58, 0xfa, 0x87, 0xad, 0x9b, 0x6d, 0xf9, 0xab, 0xa8, 0x3c, 0xfc, 0x59, 0x67, 0xa6, 0x2c, 0xc7, 0x75, 0xa4, 0x97, 0xca, 0x18, 0x18, 0x04, 0x2c, 0xb3, 0x0e, 0xa9, 0x69, 0x33, 0x67, 0xa2, 0xc6, 0xbc, 0x98, 0x48, 0x71, 0x11, 0x05, 0x30, 0xf6, 0xa9, 0x61, 0x40, 0x46, 0xf1, 0x41, 0x37, 0xd0, 0x6b, 0x7c, 0x1f, 0x03, 0x5c, 0xe9, 0xf4, 0x59, 0x1d, 0x35, 0xf0, 0x98, 0x42, 0x4a, 0x92, 0x2a, 0xc3, 0x9a, 0xb8, 0xa5
@@ -0,0 +1,73 @@
%ifdef CONFIG
{
"RegData": {
"XMM1": ["0", "0", "0", "0"],
"XMM7": ["0x2db6b85f6135a9d7", "0xe273f1177f80d2ec", "0", "0"]
},
"Mode": "32BIT",
"Env": { "FEX_HOSTFEATURES" : "enableavx" },
"HostFeatures": ["AVX"]
}
%endif
; 128-bit
; 8x displacement
; 64-bit indexes
; Calculate an address that heavily overflows
lea eax, [rel .data_mid]
mov ebx, -1
sub ebx, eax
sar ebx, 3
mov [rel .index_overflow + 0 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 8
sar ebx, 3
mov [rel .index_overflow + 1 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 16
sar ebx, 3
mov [rel .index_overflow + 2 * 8], ebx
mov ebx, -1
sub ebx, eax
add bx, 16
sar ebx, 3
mov [rel .index_overflow + 3 * 8], ebx
; Calculate new base which offsets from the overflow
lea eax, [rel .data_mid]
shl eax, 1
vmovapd ymm7, [rel .data]
vmovaps xmm0, [rel .index_overflow]
vmovaps xmm1, [rel .mask_1111]
vgatherqpd xmm7, [xmm0 * 8 + eax + 8], xmm1
hlt
align 32
.mask_1111:
dq 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000
; Indexing is a signed 64-bit integer.
.index_overflow:
dq 0, 0, 0, 0
; Random data, 512-byte per line
.data:
db 0x1d, 0x30, 0x09, 0x22, 0x56, 0xa2, 0xcd, 0xf1, 0xb7, 0x62, 0x91, 0x40, 0x67, 0x07, 0x35, 0x0f, 0x59, 0x33, 0x63, 0x52, 0x26, 0xd2, 0x2f, 0x00, 0x8a, 0x36, 0xb7, 0xf7, 0xaf, 0x4f, 0xa1, 0xc0, 0xe3, 0xbe, 0x99, 0x67, 0x5f, 0x80, 0x60, 0xcb, 0x43, 0xfa, 0x5b, 0x86, 0xb1, 0x11, 0xbc, 0xb3, 0x7b, 0x43, 0x5b, 0x45, 0x9e, 0x33, 0x89, 0xb5, 0x1b, 0xb9, 0x33, 0x4f, 0xdb, 0x5d, 0x93, 0xd6, 0x4f, 0xbc, 0x37, 0xde, 0xeb, 0xdb, 0x43, 0x2b, 0x05, 0x60, 0xb8, 0x98, 0x5c, 0xa3, 0xe3, 0x1b, 0x33, 0x03, 0x29, 0x4b, 0x12, 0x4c, 0x1e, 0xe6, 0x5e, 0x0e, 0x6c, 0xa1, 0xb9, 0x36, 0xfa, 0x6c, 0x7f, 0xc6, 0xa8, 0x38, 0x73, 0x2a, 0x0a, 0x25, 0x69, 0xa5, 0x97, 0x3f, 0x24, 0x00, 0x30, 0x4d, 0x27, 0xb3, 0x94, 0x48, 0xef, 0x47, 0x98, 0x71, 0x0d, 0x56, 0x76, 0xec, 0x41, 0x12, 0x9b, 0x7b, 0x9c, 0xf5, 0x85, 0x07, 0x2d, 0x6b, 0xc6, 0xc1, 0x2e, 0x72, 0x22, 0x5a, 0x43, 0xff, 0x1e, 0xec, 0x67, 0x2b, 0x31, 0x96, 0x14, 0x2c, 0xb1, 0x5f, 0x5d, 0x0c, 0xc9, 0xad, 0x15, 0x5f, 0xab, 0x66, 0x14, 0x1c, 0x72, 0xfa, 0x23, 0xef, 0x9f, 0x77, 0xf6, 0x50, 0xb0, 0x70, 0xb8, 0x3c, 0x85, 0x9e, 0x90, 0x69, 0x17, 0x25, 0xae, 0x6e, 0xe2, 0x16, 0x7d, 0x42, 0x38, 0xdf, 0x74, 0x72, 0x7b, 0x97, 0xa9, 0x9e, 0x40, 0x24, 0x85, 0xdc, 0x64, 0xfa, 0xb1, 0x8b, 0x95, 0xe6, 0xe4, 0x13, 0x72, 0xf1, 0x52, 0x2f, 0xa0, 0xd6, 0x52, 0xc0, 0x11, 0xa7, 0xfe, 0xd5, 0x3b, 0x56, 0xca, 0xbc, 0x01, 0xce, 0x3d, 0xd2, 0x30, 0x97, 0x1d, 0xdc, 0xeb, 0x9d, 0xa9, 0x3e, 0x09, 0xef, 0xee, 0x7f, 0x09, 0x7b, 0x82, 0x43, 0x15, 0x2e, 0xa4, 0x2e, 0x97, 0x21, 0x92, 0x7e, 0x69, 0x21, 0x25, 0xda, 0x46, 0x7c, 0x0c, 0xcd, 0x1d, 0xde, 0x42, 0x11, 0xa2, 0xef, 0xa2, 0xc8, 0x32, 0x9a, 0x82, 0xcf, 0x72, 0x7e, 0x22, 0xa6, 0x11, 0xfa, 0xec, 0x0b, 0x77, 0x99, 0x38, 0x03, 0xf6, 0x80, 0xba, 0xea, 0x75, 0x19, 0xb0, 0x48, 0x02, 0xb2, 0x6b, 0xc0, 0x8c, 0xfb, 0xfe, 0xaf, 0x94, 0x4f, 0x6f, 0xb4, 0xcb, 0x1c, 0x27, 0xf0, 0x41, 0xb6, 0x46, 0x41, 0x68, 0x3d, 0x05, 0x79, 0x6b, 0xcd, 0xb7, 0x20, 0xdc, 0x40, 0x81, 0x58, 0xcb, 0x33, 0xa3, 0xf3, 0x34, 0xdc, 0x63, 0x2d, 0xa5, 0xb5, 0xa1, 0xd1, 0xfd, 0x49, 0x5b, 0x46, 0x94, 0x01, 0xa8, 0xf2, 0xd8, 0x93, 0x2c, 0xbb, 0x57, 0xfe, 0x7c, 0x77, 0x3b, 0x19, 0x6f, 0x3c, 0xaa, 0x23, 0x5b, 0xc0, 0xe7, 0x00, 0x41, 0x97, 0x91, 0xe8, 0x00, 0x12, 0xdf, 0xf6, 0x5c, 0x2e, 0xc6, 0x8e, 0xc6, 0x77, 0x59, 0x78, 0x9b, 0xef, 0x63, 0xb0, 0xd7, 0xbb, 0xc4, 0x0b, 0x60, 0x65, 0x3f, 0xfe, 0xbf, 0x04, 0x3e, 0xae, 0xc2, 0xa5, 0x90, 0xe1, 0x2a, 0x56, 0x3f, 0x4c, 0x3f, 0x7a, 0x7d, 0xda, 0x81, 0x50, 0xea, 0x4c, 0xfe, 0xc3, 0xf8, 0x5c, 0x2b, 0x67, 0xb3, 0x9f, 0x8b, 0x95, 0xda, 0x6f, 0x5d, 0xdd, 0x82, 0x7f, 0x52, 0xa2, 0xcc, 0x57, 0xec, 0xc4, 0x14, 0xd2, 0x4f, 0x1b, 0xcb, 0xea, 0xaf, 0x0e, 0x0f, 0x53, 0xaa, 0x56, 0x63, 0xea, 0x36, 0xa6, 0x89, 0x1a, 0x66, 0xc0, 0x4e, 0xf4, 0x1e, 0x02, 0x43, 0xde, 0xde, 0xc8, 0x9e, 0x88, 0x6e, 0x32, 0xd4, 0xcb, 0x47, 0x24, 0x7c, 0x28, 0x38, 0xd4, 0x95, 0xb6, 0xa3, 0x91, 0x69, 0xc7, 0x8d, 0xfd, 0x15, 0xf5, 0xbf, 0xb1, 0x98, 0x8c, 0x57, 0x51, 0xbf, 0x83, 0x6a, 0x35, 0x10, 0x03, 0x50, 0xe5, 0xf7, 0xfa, 0xf8, 0xa5, 0xb0, 0xdb, 0xfb, 0x42, 0x93, 0xbb, 0x17, 0xf7, 0x36, 0xbe, 0x26, 0x66, 0x61, 0xe2
db 0xaf, 0xe0, 0x22, 0x25, 0x2a, 0xae, 0x78, 0x67, 0x8f, 0x7e, 0x9e, 0x59, 0xd7, 0xa3, 0x71, 0xcc, 0x43, 0x85, 0x09, 0xf9, 0x18, 0x52, 0x7b, 0x01, 0x73, 0xcb, 0x31, 0x18, 0x66, 0x79, 0x67, 0x10, 0x67, 0xd8, 0xdf, 0x43, 0xaf, 0x2d, 0x9a, 0x09, 0x9c, 0xd1, 0x37, 0x7e, 0xf5, 0x1c, 0x3c, 0x4f, 0x15, 0xe1, 0x6f, 0xfd, 0x13, 0x3d, 0x53, 0x81, 0xa9, 0x93, 0x5f, 0x92, 0x41, 0x48, 0xec, 0x87, 0x87, 0x1d, 0x0b, 0xaa, 0xaa, 0xd3, 0xc2, 0x98, 0x20, 0xce, 0x28, 0xaf, 0x9d, 0x84, 0x69, 0x4a, 0xfd, 0xc0, 0x9c, 0x2e, 0x50, 0x20, 0xb2, 0x00, 0xc1, 0x81, 0x2a, 0x32, 0x8e, 0x95, 0x20, 0xa7, 0xca, 0x39, 0x28, 0x12, 0x23, 0x0e, 0x43, 0xd3, 0x82, 0x76, 0x73, 0x3c, 0xbf, 0xa9, 0x98, 0xf6, 0x39, 0x6d, 0xd9, 0x15, 0x33, 0x1e, 0x07, 0x7c, 0x08, 0x12, 0x23, 0xbd, 0xd3, 0x34, 0x2d, 0x9a, 0x23, 0x21, 0x46, 0xf3, 0x9a, 0x04, 0x25, 0x62, 0xeb, 0x7e, 0x9a, 0xaa, 0xb6, 0x26, 0xaa, 0x85, 0x01, 0x3a, 0xd8, 0xfc, 0x57, 0x98, 0xb9, 0xe4, 0xc4, 0xe9, 0x11, 0x3e, 0x22, 0x95, 0x3b, 0x41, 0x2b, 0x02, 0x04, 0x6c, 0x75, 0xa5, 0xf2, 0xaa, 0x09, 0x9e, 0x6f, 0xab, 0x1d, 0x2a, 0x5c, 0xde, 0x21, 0xb1, 0x96, 0x2d, 0x86, 0x3f, 0xd0, 0x07, 0x18, 0x1f, 0x87, 0xc2, 0x8f, 0xdf, 0x6a, 0x57, 0x6d, 0x3f, 0x80, 0xc5, 0x08, 0x19, 0xa5, 0x09, 0x65, 0x3d, 0xdc, 0x9e, 0x80, 0x3c, 0x2a, 0x0e, 0x7a, 0x40, 0x04, 0x0b, 0xcc, 0x61, 0xdb, 0x73, 0xfc, 0xa5, 0x0a, 0x42, 0x18, 0xc1, 0xd5, 0xbd, 0x18, 0x78, 0xa1, 0xe4, 0xde, 0x44, 0xec, 0x79, 0xb0, 0x27, 0xaa, 0x45, 0x21, 0x57, 0x19, 0x75, 0x09, 0x5c, 0x58, 0xd5, 0xb9, 0x6f, 0x3b, 0x48, 0x59, 0x41, 0x3e, 0xfd, 0x17, 0x43, 0x27, 0xc3, 0x8d, 0x76, 0x8e, 0x38, 0x47, 0xe7, 0xd2, 0xea, 0x54, 0x73, 0x8a, 0x65, 0x4c, 0x49, 0x91, 0xaf, 0x29, 0x65, 0x0d, 0x81, 0xa4, 0x77, 0xd7, 0x32, 0xd0, 0x69, 0xd9, 0x6b, 0xa3, 0x9b, 0x24, 0xd6, 0x0a, 0xd2, 0x77, 0x38, 0x59, 0x0b, 0xc8, 0x5c, 0xc7, 0x0b, 0x1d, 0xd1, 0xfa, 0xa7, 0x45, 0x3c, 0xeb, 0x5c, 0x8e, 0x25, 0x35, 0x81, 0x6d, 0x6d, 0xfe, 0xb4, 0x63, 0x89, 0xe4, 0xf0, 0xa8, 0xda, 0xb7, 0xd4, 0xff, 0x5d, 0x28, 0x97, 0x11, 0xf9, 0x8d, 0xab, 0x29, 0xd5, 0xd3, 0x1c, 0x70, 0x20, 0x4c, 0x41, 0x16, 0x42, 0xfd, 0xfc, 0x62, 0x82, 0x40, 0x59, 0x34, 0x28, 0xd0, 0xd5, 0xfc, 0xac, 0x97, 0xb8, 0x82, 0x0e, 0x4b, 0xae, 0x51, 0x28, 0x1a, 0xf1, 0x87, 0xd3, 0x20, 0xa3, 0xe7, 0x74, 0x69, 0x3c, 0x54, 0x8d, 0xc5, 0x56, 0x1d, 0xcd, 0x75, 0xae, 0x88, 0x17, 0x30, 0xdf, 0x46, 0x4a, 0xbc, 0x64, 0xff, 0xa2, 0xc1, 0x62, 0xbd, 0x88, 0x7b, 0x3e, 0xa1, 0x0c, 0xa9, 0x13, 0x0e, 0xc1, 0xb4, 0x24, 0xe6, 0x96, 0x1b, 0x9c, 0x9b, 0xac, 0x44, 0x33, 0x5b, 0xda, 0xd5, 0x88, 0x4d, 0xfe, 0x81, 0x09, 0x07, 0x17, 0xcf, 0x14, 0x05, 0xaf, 0xf8, 0x72, 0x14, 0x49, 0x5f, 0x06, 0x62, 0xab, 0xe0, 0x42, 0x70, 0x12, 0x59, 0x41, 0x0f, 0x18, 0x83, 0x68, 0x6d, 0xc6, 0x3c, 0xea, 0xe0, 0x6d, 0xd4, 0xae, 0xa6, 0xf1, 0x63, 0x21, 0x7f, 0xb5, 0x9d, 0x22, 0xf4, 0xd2, 0x49, 0x49, 0xed, 0x07, 0xb1, 0x11, 0xf9, 0x2e, 0x74, 0xbe, 0x35, 0x47, 0xdc, 0xef, 0x85, 0x0b, 0x4d, 0x46, 0xe6, 0x1f, 0x60, 0x6a, 0xa1, 0x8a, 0x4d, 0x46, 0x87, 0x30, 0x8e, 0x9a, 0xba, 0x97, 0x3e, 0x15, 0xb7, 0x33, 0x76, 0x81, 0x69, 0xdb, 0x82, 0x5e, 0xe6, 0x7b, 0xec, 0xd2, 0x80, 0x7f, 0x17, 0xf1, 0x73, 0xe2
.data_mid:
db 0xd7, 0xa9, 0x35, 0x61, 0x5f, 0xb8, 0xb6, 0x2d, 0x29, 0x34, 0x63, 0xbf, 0xe2, 0x1c, 0x34, 0xe9, 0xf5, 0xff, 0x34, 0x8a, 0x2f, 0xea, 0xd4, 0x3f, 0x3b, 0xfe, 0x6e, 0xdf, 0xa6, 0xd8, 0xc6, 0xb8, 0xc5, 0xff, 0x12, 0x97, 0x53, 0xda, 0x86, 0xa8, 0x0a, 0x12, 0x4e, 0x5d, 0x96, 0x65, 0x51, 0x22, 0xe2, 0x9d, 0x08, 0x71, 0x84, 0x19, 0x8b, 0xbf, 0x29, 0xd3, 0x3f, 0xab, 0xde, 0xe4, 0x27, 0x8b, 0x99, 0xcc, 0xb1, 0x7c, 0xa5, 0x71, 0x91, 0x9a, 0x0b, 0xad, 0x75, 0x86, 0xe3, 0x9c, 0x4e, 0x0c, 0x01, 0xb3, 0x12, 0x33, 0x90, 0x81, 0x7c, 0x71, 0x2c, 0x70, 0x61, 0xd5, 0x39, 0x0c, 0x45, 0xfc, 0x27, 0xaf, 0xbb, 0xd9, 0x26, 0x1b, 0x33, 0xb4, 0x0d, 0xf8, 0xd6, 0x2d, 0x09, 0xc7, 0x8c, 0xbf, 0x48, 0x53, 0x14, 0x94, 0x76, 0x25, 0xc7, 0x0c, 0x69, 0x49, 0x82, 0xb4, 0x2f, 0x48, 0x38, 0x44, 0x9d, 0x90, 0x6d, 0x66, 0x35, 0xe9, 0x3e, 0x2f, 0x2a, 0xb7, 0xe1, 0xb1, 0x2b, 0x99, 0x08, 0x6f, 0x5c, 0x6c, 0xdf, 0xdb, 0x10, 0xe2, 0xaa, 0x86, 0xe7, 0xf8, 0x9e, 0x62, 0xde, 0xa5, 0x81, 0x6b, 0x20, 0x47, 0xa9, 0x06, 0x49, 0xc0, 0x78, 0x8c, 0x70, 0x93, 0x7e, 0xda, 0xda, 0x5e, 0x3b, 0x23, 0xf9, 0xcc, 0x87, 0xdf, 0x48, 0x4f, 0xd6, 0x77, 0xce, 0x45, 0xe1, 0xdc, 0x0c, 0x7a, 0x0c, 0x50, 0x15, 0x63, 0x8c, 0x48, 0xd3, 0x8e, 0xfa, 0xcc, 0xac, 0x1a, 0x83, 0xde, 0xb1, 0x87, 0x2a, 0x58, 0x5c, 0xa5, 0x20, 0x3d, 0xaa, 0x1e, 0x5d, 0x71, 0xa6, 0x57, 0x75, 0x82, 0xb7, 0x33, 0x9e, 0x6b, 0xf3, 0x35, 0x02, 0x98, 0x03, 0xe1, 0x3b, 0xd2, 0x9f, 0x7a, 0x06, 0x85, 0xef, 0x7d, 0xd9, 0xf2, 0x0c, 0x9e, 0xce, 0xb9, 0xce, 0x13, 0x4a, 0x9e, 0x8a, 0x29, 0xe6, 0xe5, 0xe4, 0x39, 0xba, 0xfd, 0xa3, 0x33, 0xa8, 0x13, 0x9e, 0xa5, 0x11, 0x37, 0x69, 0xbc, 0xda, 0x11, 0x49, 0x2d, 0x4a, 0xef, 0x20, 0x8b, 0x7a, 0xb8, 0x9c, 0xc3, 0xaf, 0x26, 0x71, 0xd9, 0xa2, 0xf6, 0x0f, 0x85, 0x87, 0xa8, 0x6c, 0xf9, 0x99, 0xa2, 0xb2, 0x36, 0x2d, 0x78, 0x10, 0xe4, 0x33, 0x8d, 0xa4, 0x63, 0xea, 0x02, 0xb9, 0xac, 0x2f, 0x90, 0x39, 0x2d, 0x0e, 0x2e, 0xf5, 0x08, 0xa5, 0x5c, 0x8e, 0x71, 0x30, 0x0d, 0x1b, 0x84, 0x7a, 0xd7, 0xd4, 0xab, 0x81, 0x82, 0x18, 0x37, 0xf3, 0x28, 0x6f, 0x4e, 0x28, 0x71, 0xda, 0xc9, 0x99, 0x46, 0x14, 0x46, 0x77, 0x01, 0x16, 0x21, 0xae, 0x83, 0x93, 0x86, 0x7f, 0x5a, 0xee, 0xd5, 0xdf, 0x48, 0x5b, 0x15, 0xc8, 0x09, 0x30, 0x8f, 0x01, 0xcc, 0x95, 0x30, 0xd9, 0xf7, 0x72, 0x97, 0xfd, 0x9d, 0xec, 0x9f, 0xbf, 0x5c, 0xbf, 0x4f, 0xca, 0x33, 0xb4, 0xd2, 0xa2, 0xb9, 0x08, 0x9c, 0x40, 0x25, 0x3f, 0x86, 0xdc, 0x83, 0x70, 0x2f, 0xfb, 0x2a, 0xf8, 0x61, 0x1f, 0xa1, 0x1f, 0x36, 0x04, 0xe2, 0xef, 0x1c, 0xa4, 0xcd, 0x3c, 0x7f, 0xc5, 0x73, 0x9c, 0x2e, 0xeb, 0x03, 0x79, 0xd1, 0x02, 0xfc, 0x6f, 0xbd, 0x5a, 0x95, 0xb2, 0xf6, 0x25, 0x96, 0xe6, 0x80, 0x0a, 0xc5, 0xc7, 0xca, 0x8d, 0x31, 0xae, 0xf0, 0x49, 0xcf, 0x43, 0x06, 0x27, 0x7f, 0x25, 0xc7, 0x4c, 0xb7, 0xfc, 0x73, 0xd3, 0x04, 0xd3, 0xb9, 0x9f, 0x74, 0xed, 0x9e, 0x3c, 0xf0, 0xcf, 0x26, 0x2b, 0xd9, 0xcb, 0x78, 0x2a, 0xef, 0x72, 0xf7, 0xb6, 0x78, 0x30, 0x2d, 0x8c, 0x83, 0x73, 0x66, 0x74, 0x3d, 0x66, 0x0a, 0x74, 0x5a, 0x3f, 0x9f, 0x6e, 0x56, 0x68, 0x01, 0xc2, 0xca, 0x2b, 0xa1, 0x25, 0x36, 0x9c, 0x3b, 0xa4, 0x5e, 0x44, 0xf1, 0x18, 0x1d, 0xb6, 0x1a, 0x3a, 0xee, 0x8d, 0x67, 0x34, 0x9c
db 0xdd, 0x48, 0x14, 0xc2, 0x5f, 0xd8, 0xe5, 0x71, 0x22, 0xbf, 0xbc, 0x84, 0xda, 0xc1, 0xb1, 0x22, 0x55, 0xa4, 0x63, 0x41, 0x77, 0xac, 0x40, 0x2d, 0x44, 0x73, 0x8c, 0x14, 0xba, 0x5e, 0x63, 0x68, 0x65, 0x61, 0x6d, 0xec, 0xe2, 0x6d, 0x37, 0x22, 0x04, 0xeb, 0xc7, 0xd4, 0xc9, 0x62, 0x56, 0x13, 0x96, 0x29, 0x03, 0xf4, 0x55, 0xe2, 0x58, 0x7d, 0xda, 0x52, 0x2e, 0x94, 0x07, 0xe6, 0xef, 0xc0, 0xee, 0x9e, 0x0b, 0xf7, 0xcd, 0x13, 0x8b, 0x7d, 0xea, 0xdc, 0xf8, 0xf1, 0xcb, 0xad, 0x49, 0x97, 0xc9, 0x98, 0x0b, 0xcf, 0x84, 0x8e, 0x8e, 0xbb, 0x06, 0x2e, 0x54, 0xf5, 0xa7, 0xbd, 0x70, 0x7e, 0x38, 0x69, 0x8d, 0xb0, 0x01, 0x7b, 0x41, 0x80, 0x09, 0x44, 0xfd, 0x7e, 0x21, 0xb4, 0xbe, 0x6b, 0x4a, 0xb7, 0xca, 0x2d, 0x19, 0xfe, 0x6d, 0xd6, 0x11, 0x29, 0xbb, 0xb2, 0x16, 0xf1, 0xe7, 0x92, 0x71, 0xda, 0x7e, 0x68, 0x3a, 0xe0, 0xea, 0x89, 0x8d, 0xe0, 0x44, 0x48, 0x25, 0x92, 0x37, 0x54, 0x26, 0xf2, 0xab, 0xb3, 0x3b, 0xdb, 0xbb, 0x2b, 0x5c, 0xf5, 0xbc, 0xc7, 0x97, 0xdb, 0xc7, 0x49, 0x25, 0x7c, 0xc2, 0x80, 0x02, 0x69, 0xd4, 0xda, 0xda, 0xe1, 0x04, 0xf3, 0x19, 0xb8, 0xc9, 0xb2, 0xfb, 0x1e, 0x47, 0xa9, 0x0c, 0xa3, 0x48, 0xce, 0xc2, 0x9e, 0x3b, 0x28, 0x23, 0x5a, 0x20, 0x44, 0x77, 0x40, 0xe2, 0xd7, 0x20, 0xd5, 0x71, 0x6f, 0xd4, 0x3c, 0x68, 0x38, 0x9b, 0x89, 0x2e, 0x2d, 0xa8, 0x1f, 0x99, 0xb5, 0x8a, 0x66, 0x07, 0x59, 0x75, 0x9e, 0xf8, 0xd9, 0xbe, 0x85, 0x6a, 0x20, 0x92, 0x9d, 0xd2, 0x5e, 0x45, 0xc0, 0x60, 0xbe, 0x85, 0x0b, 0x84, 0x47, 0xf5, 0xa8, 0x43, 0x87, 0xf1, 0x21, 0x21, 0xb0, 0x3b, 0x04, 0x13, 0x16, 0x3e, 0xdf, 0xc3, 0xc6, 0x04, 0x73, 0xcd, 0x92, 0x76, 0xfb, 0xe7, 0x9c, 0xd3, 0x46, 0x11, 0x78, 0xca, 0x12, 0xd9, 0x4a, 0x35, 0xf1, 0x6e, 0x89, 0x8b, 0xe9, 0x7a, 0x04, 0xba, 0x18, 0x25, 0x7c, 0x9e, 0xe6, 0x4f, 0xc2, 0x56, 0x05, 0x72, 0xc3, 0x76, 0xee, 0x7d, 0x77, 0x19, 0x7a, 0x73, 0x2c, 0x81, 0xb8, 0xc7, 0xd9, 0x7f, 0x17, 0x5d, 0x30, 0xda, 0x77, 0x3c, 0x14, 0x88, 0xe8, 0xe4, 0xbf, 0xee, 0x21, 0x1c, 0x29, 0x4e, 0x58, 0xa8, 0x8a, 0x5c, 0xae, 0xa2, 0x1c, 0x7c, 0x25, 0x7c, 0x1c, 0x39, 0xa4, 0x28, 0x4b, 0x78, 0x52, 0xae, 0x2c, 0xbb, 0x5f, 0xbf, 0x51, 0x09, 0x20, 0x76, 0xb2, 0x7d, 0xb1, 0x63, 0x84, 0xc5, 0x49, 0x8a, 0x73, 0xdb, 0x76, 0x1d, 0x25, 0x31, 0xf2, 0x1e, 0x19, 0x38, 0xc8, 0x3b, 0x51, 0x3c, 0x13, 0x52, 0x84, 0xae, 0xc2, 0xe4, 0x8a, 0x57, 0x0d, 0xde, 0x8d, 0x18, 0x48, 0x9a, 0xbd, 0xbf, 0xf3, 0xea, 0x79, 0x17, 0x06, 0x96, 0x72, 0x08, 0x60, 0x95, 0xf9, 0x6f, 0x25, 0x0c, 0xb7, 0x9d, 0x98, 0x23, 0x01, 0xc8, 0x7a, 0xdb, 0x75, 0x63, 0x64, 0x14, 0x5e, 0x10, 0xf5, 0x16, 0x48, 0xbc, 0xc6, 0x7e, 0x24, 0xf3, 0xad, 0x57, 0x3f, 0x7d, 0x6c, 0xab, 0x18, 0x8c, 0x12, 0xc5, 0x0c, 0xd8, 0xb5, 0x1e, 0x43, 0x7c, 0x23, 0x17, 0x48, 0xba, 0x76, 0x3b, 0xd9, 0x2b, 0xae, 0x1b, 0xef, 0x58, 0xfa, 0x87, 0xad, 0x9b, 0x6d, 0xf9, 0xab, 0xa8, 0x3c, 0xfc, 0x59, 0x67, 0xa6, 0x2c, 0xc7, 0x75, 0xa4, 0x97, 0xca, 0x18, 0x18, 0x04, 0x2c, 0xb3, 0x0e, 0xa9, 0x69, 0x33, 0x67, 0xa2, 0xc6, 0xbc, 0x98, 0x48, 0x71, 0x11, 0x05, 0x30, 0xf6, 0xa9, 0x61, 0x40, 0x46, 0xf1, 0x41, 0x37, 0xd0, 0x6b, 0x7c, 0x1f, 0x03, 0x5c, 0xe9, 0xf4, 0x59, 0x1d, 0x35, 0xf0, 0x98, 0x42, 0x4a, 0x92, 0x2a, 0xc3, 0x9a, 0xb8, 0xa5
@@ -0,0 +1,69 @@
%ifdef CONFIG
{
"RegData": {
"XMM1": ["0", "0", "0", "0"],
"XMM7": ["0x2db6b85f6135a9d7", "0xe273f1177f80d2ec", "0x7be65e82db698176", "0x3fd4ea2f8a34fff5"]
},
"Mode": "32BIT",
"Env": { "FEX_HOSTFEATURES" : "enableavx" },
"HostFeatures": ["AVX"]
}
%endif
; 256-bit
; 1x displacement
; 64-bit indexes
; Calculate an address that heavily overflows
lea eax, [rel .data_mid]
mov ebx, -1
sub ebx, eax
mov [rel .index_overflow + 0 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 8
mov [rel .index_overflow + 1 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 16
mov [rel .index_overflow + 2 * 8], ebx
mov ebx, -1
sub ebx, eax
add bx, 16
mov [rel .index_overflow + 3 * 8], ebx
; Calculate new base which offsets from the overflow
lea eax, [rel .data_mid]
shl eax, 1
vmovapd ymm7, [rel .data]
vmovaps ymm0, [rel .index_overflow]
vmovaps ymm1, [rel .mask_1111]
vgatherqpd ymm7, [ymm0 * 1 + eax + 1], ymm1
hlt
align 32
.mask_1111:
dq 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000
; Indexing is a signed 64-bit integer.
.index_overflow:
dq 0, 0, 0, 0
; Random data, 512-byte per line
.data:
db 0x1d, 0x30, 0x09, 0x22, 0x56, 0xa2, 0xcd, 0xf1, 0xb7, 0x62, 0x91, 0x40, 0x67, 0x07, 0x35, 0x0f, 0x59, 0x33, 0x63, 0x52, 0x26, 0xd2, 0x2f, 0x00, 0x8a, 0x36, 0xb7, 0xf7, 0xaf, 0x4f, 0xa1, 0xc0, 0xe3, 0xbe, 0x99, 0x67, 0x5f, 0x80, 0x60, 0xcb, 0x43, 0xfa, 0x5b, 0x86, 0xb1, 0x11, 0xbc, 0xb3, 0x7b, 0x43, 0x5b, 0x45, 0x9e, 0x33, 0x89, 0xb5, 0x1b, 0xb9, 0x33, 0x4f, 0xdb, 0x5d, 0x93, 0xd6, 0x4f, 0xbc, 0x37, 0xde, 0xeb, 0xdb, 0x43, 0x2b, 0x05, 0x60, 0xb8, 0x98, 0x5c, 0xa3, 0xe3, 0x1b, 0x33, 0x03, 0x29, 0x4b, 0x12, 0x4c, 0x1e, 0xe6, 0x5e, 0x0e, 0x6c, 0xa1, 0xb9, 0x36, 0xfa, 0x6c, 0x7f, 0xc6, 0xa8, 0x38, 0x73, 0x2a, 0x0a, 0x25, 0x69, 0xa5, 0x97, 0x3f, 0x24, 0x00, 0x30, 0x4d, 0x27, 0xb3, 0x94, 0x48, 0xef, 0x47, 0x98, 0x71, 0x0d, 0x56, 0x76, 0xec, 0x41, 0x12, 0x9b, 0x7b, 0x9c, 0xf5, 0x85, 0x07, 0x2d, 0x6b, 0xc6, 0xc1, 0x2e, 0x72, 0x22, 0x5a, 0x43, 0xff, 0x1e, 0xec, 0x67, 0x2b, 0x31, 0x96, 0x14, 0x2c, 0xb1, 0x5f, 0x5d, 0x0c, 0xc9, 0xad, 0x15, 0x5f, 0xab, 0x66, 0x14, 0x1c, 0x72, 0xfa, 0x23, 0xef, 0x9f, 0x77, 0xf6, 0x50, 0xb0, 0x70, 0xb8, 0x3c, 0x85, 0x9e, 0x90, 0x69, 0x17, 0x25, 0xae, 0x6e, 0xe2, 0x16, 0x7d, 0x42, 0x38, 0xdf, 0x74, 0x72, 0x7b, 0x97, 0xa9, 0x9e, 0x40, 0x24, 0x85, 0xdc, 0x64, 0xfa, 0xb1, 0x8b, 0x95, 0xe6, 0xe4, 0x13, 0x72, 0xf1, 0x52, 0x2f, 0xa0, 0xd6, 0x52, 0xc0, 0x11, 0xa7, 0xfe, 0xd5, 0x3b, 0x56, 0xca, 0xbc, 0x01, 0xce, 0x3d, 0xd2, 0x30, 0x97, 0x1d, 0xdc, 0xeb, 0x9d, 0xa9, 0x3e, 0x09, 0xef, 0xee, 0x7f, 0x09, 0x7b, 0x82, 0x43, 0x15, 0x2e, 0xa4, 0x2e, 0x97, 0x21, 0x92, 0x7e, 0x69, 0x21, 0x25, 0xda, 0x46, 0x7c, 0x0c, 0xcd, 0x1d, 0xde, 0x42, 0x11, 0xa2, 0xef, 0xa2, 0xc8, 0x32, 0x9a, 0x82, 0xcf, 0x72, 0x7e, 0x22, 0xa6, 0x11, 0xfa, 0xec, 0x0b, 0x77, 0x99, 0x38, 0x03, 0xf6, 0x80, 0xba, 0xea, 0x75, 0x19, 0xb0, 0x48, 0x02, 0xb2, 0x6b, 0xc0, 0x8c, 0xfb, 0xfe, 0xaf, 0x94, 0x4f, 0x6f, 0xb4, 0xcb, 0x1c, 0x27, 0xf0, 0x41, 0xb6, 0x46, 0x41, 0x68, 0x3d, 0x05, 0x79, 0x6b, 0xcd, 0xb7, 0x20, 0xdc, 0x40, 0x81, 0x58, 0xcb, 0x33, 0xa3, 0xf3, 0x34, 0xdc, 0x63, 0x2d, 0xa5, 0xb5, 0xa1, 0xd1, 0xfd, 0x49, 0x5b, 0x46, 0x94, 0x01, 0xa8, 0xf2, 0xd8, 0x93, 0x2c, 0xbb, 0x57, 0xfe, 0x7c, 0x77, 0x3b, 0x19, 0x6f, 0x3c, 0xaa, 0x23, 0x5b, 0xc0, 0xe7, 0x00, 0x41, 0x97, 0x91, 0xe8, 0x00, 0x12, 0xdf, 0xf6, 0x5c, 0x2e, 0xc6, 0x8e, 0xc6, 0x77, 0x59, 0x78, 0x9b, 0xef, 0x63, 0xb0, 0xd7, 0xbb, 0xc4, 0x0b, 0x60, 0x65, 0x3f, 0xfe, 0xbf, 0x04, 0x3e, 0xae, 0xc2, 0xa5, 0x90, 0xe1, 0x2a, 0x56, 0x3f, 0x4c, 0x3f, 0x7a, 0x7d, 0xda, 0x81, 0x50, 0xea, 0x4c, 0xfe, 0xc3, 0xf8, 0x5c, 0x2b, 0x67, 0xb3, 0x9f, 0x8b, 0x95, 0xda, 0x6f, 0x5d, 0xdd, 0x82, 0x7f, 0x52, 0xa2, 0xcc, 0x57, 0xec, 0xc4, 0x14, 0xd2, 0x4f, 0x1b, 0xcb, 0xea, 0xaf, 0x0e, 0x0f, 0x53, 0xaa, 0x56, 0x63, 0xea, 0x36, 0xa6, 0x89, 0x1a, 0x66, 0xc0, 0x4e, 0xf4, 0x1e, 0x02, 0x43, 0xde, 0xde, 0xc8, 0x9e, 0x88, 0x6e, 0x32, 0xd4, 0xcb, 0x47, 0x24, 0x7c, 0x28, 0x38, 0xd4, 0x95, 0xb6, 0xa3, 0x91, 0x69, 0xc7, 0x8d, 0xfd, 0x15, 0xf5, 0xbf, 0xb1, 0x98, 0x8c, 0x57, 0x51, 0xbf, 0x83, 0x6a, 0x35, 0x10, 0x03, 0x50, 0xe5, 0xf7, 0xfa, 0xf8, 0xa5, 0xb0, 0xdb, 0xfb, 0x42, 0x93, 0xbb, 0x17, 0xf7, 0x36, 0xbe, 0x26, 0x66, 0x61, 0xe2
db 0xaf, 0xe0, 0x22, 0x25, 0x2a, 0xae, 0x78, 0x67, 0x8f, 0x7e, 0x9e, 0x59, 0xd7, 0xa3, 0x71, 0xcc, 0x43, 0x85, 0x09, 0xf9, 0x18, 0x52, 0x7b, 0x01, 0x73, 0xcb, 0x31, 0x18, 0x66, 0x79, 0x67, 0x10, 0x67, 0xd8, 0xdf, 0x43, 0xaf, 0x2d, 0x9a, 0x09, 0x9c, 0xd1, 0x37, 0x7e, 0xf5, 0x1c, 0x3c, 0x4f, 0x15, 0xe1, 0x6f, 0xfd, 0x13, 0x3d, 0x53, 0x81, 0xa9, 0x93, 0x5f, 0x92, 0x41, 0x48, 0xec, 0x87, 0x87, 0x1d, 0x0b, 0xaa, 0xaa, 0xd3, 0xc2, 0x98, 0x20, 0xce, 0x28, 0xaf, 0x9d, 0x84, 0x69, 0x4a, 0xfd, 0xc0, 0x9c, 0x2e, 0x50, 0x20, 0xb2, 0x00, 0xc1, 0x81, 0x2a, 0x32, 0x8e, 0x95, 0x20, 0xa7, 0xca, 0x39, 0x28, 0x12, 0x23, 0x0e, 0x43, 0xd3, 0x82, 0x76, 0x73, 0x3c, 0xbf, 0xa9, 0x98, 0xf6, 0x39, 0x6d, 0xd9, 0x15, 0x33, 0x1e, 0x07, 0x7c, 0x08, 0x12, 0x23, 0xbd, 0xd3, 0x34, 0x2d, 0x9a, 0x23, 0x21, 0x46, 0xf3, 0x9a, 0x04, 0x25, 0x62, 0xeb, 0x7e, 0x9a, 0xaa, 0xb6, 0x26, 0xaa, 0x85, 0x01, 0x3a, 0xd8, 0xfc, 0x57, 0x98, 0xb9, 0xe4, 0xc4, 0xe9, 0x11, 0x3e, 0x22, 0x95, 0x3b, 0x41, 0x2b, 0x02, 0x04, 0x6c, 0x75, 0xa5, 0xf2, 0xaa, 0x09, 0x9e, 0x6f, 0xab, 0x1d, 0x2a, 0x5c, 0xde, 0x21, 0xb1, 0x96, 0x2d, 0x86, 0x3f, 0xd0, 0x07, 0x18, 0x1f, 0x87, 0xc2, 0x8f, 0xdf, 0x6a, 0x57, 0x6d, 0x3f, 0x80, 0xc5, 0x08, 0x19, 0xa5, 0x09, 0x65, 0x3d, 0xdc, 0x9e, 0x80, 0x3c, 0x2a, 0x0e, 0x7a, 0x40, 0x04, 0x0b, 0xcc, 0x61, 0xdb, 0x73, 0xfc, 0xa5, 0x0a, 0x42, 0x18, 0xc1, 0xd5, 0xbd, 0x18, 0x78, 0xa1, 0xe4, 0xde, 0x44, 0xec, 0x79, 0xb0, 0x27, 0xaa, 0x45, 0x21, 0x57, 0x19, 0x75, 0x09, 0x5c, 0x58, 0xd5, 0xb9, 0x6f, 0x3b, 0x48, 0x59, 0x41, 0x3e, 0xfd, 0x17, 0x43, 0x27, 0xc3, 0x8d, 0x76, 0x8e, 0x38, 0x47, 0xe7, 0xd2, 0xea, 0x54, 0x73, 0x8a, 0x65, 0x4c, 0x49, 0x91, 0xaf, 0x29, 0x65, 0x0d, 0x81, 0xa4, 0x77, 0xd7, 0x32, 0xd0, 0x69, 0xd9, 0x6b, 0xa3, 0x9b, 0x24, 0xd6, 0x0a, 0xd2, 0x77, 0x38, 0x59, 0x0b, 0xc8, 0x5c, 0xc7, 0x0b, 0x1d, 0xd1, 0xfa, 0xa7, 0x45, 0x3c, 0xeb, 0x5c, 0x8e, 0x25, 0x35, 0x81, 0x6d, 0x6d, 0xfe, 0xb4, 0x63, 0x89, 0xe4, 0xf0, 0xa8, 0xda, 0xb7, 0xd4, 0xff, 0x5d, 0x28, 0x97, 0x11, 0xf9, 0x8d, 0xab, 0x29, 0xd5, 0xd3, 0x1c, 0x70, 0x20, 0x4c, 0x41, 0x16, 0x42, 0xfd, 0xfc, 0x62, 0x82, 0x40, 0x59, 0x34, 0x28, 0xd0, 0xd5, 0xfc, 0xac, 0x97, 0xb8, 0x82, 0x0e, 0x4b, 0xae, 0x51, 0x28, 0x1a, 0xf1, 0x87, 0xd3, 0x20, 0xa3, 0xe7, 0x74, 0x69, 0x3c, 0x54, 0x8d, 0xc5, 0x56, 0x1d, 0xcd, 0x75, 0xae, 0x88, 0x17, 0x30, 0xdf, 0x46, 0x4a, 0xbc, 0x64, 0xff, 0xa2, 0xc1, 0x62, 0xbd, 0x88, 0x7b, 0x3e, 0xa1, 0x0c, 0xa9, 0x13, 0x0e, 0xc1, 0xb4, 0x24, 0xe6, 0x96, 0x1b, 0x9c, 0x9b, 0xac, 0x44, 0x33, 0x5b, 0xda, 0xd5, 0x88, 0x4d, 0xfe, 0x81, 0x09, 0x07, 0x17, 0xcf, 0x14, 0x05, 0xaf, 0xf8, 0x72, 0x14, 0x49, 0x5f, 0x06, 0x62, 0xab, 0xe0, 0x42, 0x70, 0x12, 0x59, 0x41, 0x0f, 0x18, 0x83, 0x68, 0x6d, 0xc6, 0x3c, 0xea, 0xe0, 0x6d, 0xd4, 0xae, 0xa6, 0xf1, 0x63, 0x21, 0x7f, 0xb5, 0x9d, 0x22, 0xf4, 0xd2, 0x49, 0x49, 0xed, 0x07, 0xb1, 0x11, 0xf9, 0x2e, 0x74, 0xbe, 0x35, 0x47, 0xdc, 0xef, 0x85, 0x0b, 0x4d, 0x46, 0xe6, 0x1f, 0x60, 0x6a, 0xa1, 0x8a, 0x4d, 0x46, 0x87, 0x30, 0x8e, 0x9a, 0xba, 0x97, 0x3e, 0x15, 0xb7, 0x33, 0x76, 0x81, 0x69, 0xdb, 0x82, 0x5e, 0xe6, 0x7b, 0xec, 0xd2, 0x80, 0x7f, 0x17, 0xf1, 0x73, 0xe2
.data_mid:
db 0xd7, 0xa9, 0x35, 0x61, 0x5f, 0xb8, 0xb6, 0x2d, 0x29, 0x34, 0x63, 0xbf, 0xe2, 0x1c, 0x34, 0xe9, 0xf5, 0xff, 0x34, 0x8a, 0x2f, 0xea, 0xd4, 0x3f, 0x3b, 0xfe, 0x6e, 0xdf, 0xa6, 0xd8, 0xc6, 0xb8, 0xc5, 0xff, 0x12, 0x97, 0x53, 0xda, 0x86, 0xa8, 0x0a, 0x12, 0x4e, 0x5d, 0x96, 0x65, 0x51, 0x22, 0xe2, 0x9d, 0x08, 0x71, 0x84, 0x19, 0x8b, 0xbf, 0x29, 0xd3, 0x3f, 0xab, 0xde, 0xe4, 0x27, 0x8b, 0x99, 0xcc, 0xb1, 0x7c, 0xa5, 0x71, 0x91, 0x9a, 0x0b, 0xad, 0x75, 0x86, 0xe3, 0x9c, 0x4e, 0x0c, 0x01, 0xb3, 0x12, 0x33, 0x90, 0x81, 0x7c, 0x71, 0x2c, 0x70, 0x61, 0xd5, 0x39, 0x0c, 0x45, 0xfc, 0x27, 0xaf, 0xbb, 0xd9, 0x26, 0x1b, 0x33, 0xb4, 0x0d, 0xf8, 0xd6, 0x2d, 0x09, 0xc7, 0x8c, 0xbf, 0x48, 0x53, 0x14, 0x94, 0x76, 0x25, 0xc7, 0x0c, 0x69, 0x49, 0x82, 0xb4, 0x2f, 0x48, 0x38, 0x44, 0x9d, 0x90, 0x6d, 0x66, 0x35, 0xe9, 0x3e, 0x2f, 0x2a, 0xb7, 0xe1, 0xb1, 0x2b, 0x99, 0x08, 0x6f, 0x5c, 0x6c, 0xdf, 0xdb, 0x10, 0xe2, 0xaa, 0x86, 0xe7, 0xf8, 0x9e, 0x62, 0xde, 0xa5, 0x81, 0x6b, 0x20, 0x47, 0xa9, 0x06, 0x49, 0xc0, 0x78, 0x8c, 0x70, 0x93, 0x7e, 0xda, 0xda, 0x5e, 0x3b, 0x23, 0xf9, 0xcc, 0x87, 0xdf, 0x48, 0x4f, 0xd6, 0x77, 0xce, 0x45, 0xe1, 0xdc, 0x0c, 0x7a, 0x0c, 0x50, 0x15, 0x63, 0x8c, 0x48, 0xd3, 0x8e, 0xfa, 0xcc, 0xac, 0x1a, 0x83, 0xde, 0xb1, 0x87, 0x2a, 0x58, 0x5c, 0xa5, 0x20, 0x3d, 0xaa, 0x1e, 0x5d, 0x71, 0xa6, 0x57, 0x75, 0x82, 0xb7, 0x33, 0x9e, 0x6b, 0xf3, 0x35, 0x02, 0x98, 0x03, 0xe1, 0x3b, 0xd2, 0x9f, 0x7a, 0x06, 0x85, 0xef, 0x7d, 0xd9, 0xf2, 0x0c, 0x9e, 0xce, 0xb9, 0xce, 0x13, 0x4a, 0x9e, 0x8a, 0x29, 0xe6, 0xe5, 0xe4, 0x39, 0xba, 0xfd, 0xa3, 0x33, 0xa8, 0x13, 0x9e, 0xa5, 0x11, 0x37, 0x69, 0xbc, 0xda, 0x11, 0x49, 0x2d, 0x4a, 0xef, 0x20, 0x8b, 0x7a, 0xb8, 0x9c, 0xc3, 0xaf, 0x26, 0x71, 0xd9, 0xa2, 0xf6, 0x0f, 0x85, 0x87, 0xa8, 0x6c, 0xf9, 0x99, 0xa2, 0xb2, 0x36, 0x2d, 0x78, 0x10, 0xe4, 0x33, 0x8d, 0xa4, 0x63, 0xea, 0x02, 0xb9, 0xac, 0x2f, 0x90, 0x39, 0x2d, 0x0e, 0x2e, 0xf5, 0x08, 0xa5, 0x5c, 0x8e, 0x71, 0x30, 0x0d, 0x1b, 0x84, 0x7a, 0xd7, 0xd4, 0xab, 0x81, 0x82, 0x18, 0x37, 0xf3, 0x28, 0x6f, 0x4e, 0x28, 0x71, 0xda, 0xc9, 0x99, 0x46, 0x14, 0x46, 0x77, 0x01, 0x16, 0x21, 0xae, 0x83, 0x93, 0x86, 0x7f, 0x5a, 0xee, 0xd5, 0xdf, 0x48, 0x5b, 0x15, 0xc8, 0x09, 0x30, 0x8f, 0x01, 0xcc, 0x95, 0x30, 0xd9, 0xf7, 0x72, 0x97, 0xfd, 0x9d, 0xec, 0x9f, 0xbf, 0x5c, 0xbf, 0x4f, 0xca, 0x33, 0xb4, 0xd2, 0xa2, 0xb9, 0x08, 0x9c, 0x40, 0x25, 0x3f, 0x86, 0xdc, 0x83, 0x70, 0x2f, 0xfb, 0x2a, 0xf8, 0x61, 0x1f, 0xa1, 0x1f, 0x36, 0x04, 0xe2, 0xef, 0x1c, 0xa4, 0xcd, 0x3c, 0x7f, 0xc5, 0x73, 0x9c, 0x2e, 0xeb, 0x03, 0x79, 0xd1, 0x02, 0xfc, 0x6f, 0xbd, 0x5a, 0x95, 0xb2, 0xf6, 0x25, 0x96, 0xe6, 0x80, 0x0a, 0xc5, 0xc7, 0xca, 0x8d, 0x31, 0xae, 0xf0, 0x49, 0xcf, 0x43, 0x06, 0x27, 0x7f, 0x25, 0xc7, 0x4c, 0xb7, 0xfc, 0x73, 0xd3, 0x04, 0xd3, 0xb9, 0x9f, 0x74, 0xed, 0x9e, 0x3c, 0xf0, 0xcf, 0x26, 0x2b, 0xd9, 0xcb, 0x78, 0x2a, 0xef, 0x72, 0xf7, 0xb6, 0x78, 0x30, 0x2d, 0x8c, 0x83, 0x73, 0x66, 0x74, 0x3d, 0x66, 0x0a, 0x74, 0x5a, 0x3f, 0x9f, 0x6e, 0x56, 0x68, 0x01, 0xc2, 0xca, 0x2b, 0xa1, 0x25, 0x36, 0x9c, 0x3b, 0xa4, 0x5e, 0x44, 0xf1, 0x18, 0x1d, 0xb6, 0x1a, 0x3a, 0xee, 0x8d, 0x67, 0x34, 0x9c
db 0xdd, 0x48, 0x14, 0xc2, 0x5f, 0xd8, 0xe5, 0x71, 0x22, 0xbf, 0xbc, 0x84, 0xda, 0xc1, 0xb1, 0x22, 0x55, 0xa4, 0x63, 0x41, 0x77, 0xac, 0x40, 0x2d, 0x44, 0x73, 0x8c, 0x14, 0xba, 0x5e, 0x63, 0x68, 0x65, 0x61, 0x6d, 0xec, 0xe2, 0x6d, 0x37, 0x22, 0x04, 0xeb, 0xc7, 0xd4, 0xc9, 0x62, 0x56, 0x13, 0x96, 0x29, 0x03, 0xf4, 0x55, 0xe2, 0x58, 0x7d, 0xda, 0x52, 0x2e, 0x94, 0x07, 0xe6, 0xef, 0xc0, 0xee, 0x9e, 0x0b, 0xf7, 0xcd, 0x13, 0x8b, 0x7d, 0xea, 0xdc, 0xf8, 0xf1, 0xcb, 0xad, 0x49, 0x97, 0xc9, 0x98, 0x0b, 0xcf, 0x84, 0x8e, 0x8e, 0xbb, 0x06, 0x2e, 0x54, 0xf5, 0xa7, 0xbd, 0x70, 0x7e, 0x38, 0x69, 0x8d, 0xb0, 0x01, 0x7b, 0x41, 0x80, 0x09, 0x44, 0xfd, 0x7e, 0x21, 0xb4, 0xbe, 0x6b, 0x4a, 0xb7, 0xca, 0x2d, 0x19, 0xfe, 0x6d, 0xd6, 0x11, 0x29, 0xbb, 0xb2, 0x16, 0xf1, 0xe7, 0x92, 0x71, 0xda, 0x7e, 0x68, 0x3a, 0xe0, 0xea, 0x89, 0x8d, 0xe0, 0x44, 0x48, 0x25, 0x92, 0x37, 0x54, 0x26, 0xf2, 0xab, 0xb3, 0x3b, 0xdb, 0xbb, 0x2b, 0x5c, 0xf5, 0xbc, 0xc7, 0x97, 0xdb, 0xc7, 0x49, 0x25, 0x7c, 0xc2, 0x80, 0x02, 0x69, 0xd4, 0xda, 0xda, 0xe1, 0x04, 0xf3, 0x19, 0xb8, 0xc9, 0xb2, 0xfb, 0x1e, 0x47, 0xa9, 0x0c, 0xa3, 0x48, 0xce, 0xc2, 0x9e, 0x3b, 0x28, 0x23, 0x5a, 0x20, 0x44, 0x77, 0x40, 0xe2, 0xd7, 0x20, 0xd5, 0x71, 0x6f, 0xd4, 0x3c, 0x68, 0x38, 0x9b, 0x89, 0x2e, 0x2d, 0xa8, 0x1f, 0x99, 0xb5, 0x8a, 0x66, 0x07, 0x59, 0x75, 0x9e, 0xf8, 0xd9, 0xbe, 0x85, 0x6a, 0x20, 0x92, 0x9d, 0xd2, 0x5e, 0x45, 0xc0, 0x60, 0xbe, 0x85, 0x0b, 0x84, 0x47, 0xf5, 0xa8, 0x43, 0x87, 0xf1, 0x21, 0x21, 0xb0, 0x3b, 0x04, 0x13, 0x16, 0x3e, 0xdf, 0xc3, 0xc6, 0x04, 0x73, 0xcd, 0x92, 0x76, 0xfb, 0xe7, 0x9c, 0xd3, 0x46, 0x11, 0x78, 0xca, 0x12, 0xd9, 0x4a, 0x35, 0xf1, 0x6e, 0x89, 0x8b, 0xe9, 0x7a, 0x04, 0xba, 0x18, 0x25, 0x7c, 0x9e, 0xe6, 0x4f, 0xc2, 0x56, 0x05, 0x72, 0xc3, 0x76, 0xee, 0x7d, 0x77, 0x19, 0x7a, 0x73, 0x2c, 0x81, 0xb8, 0xc7, 0xd9, 0x7f, 0x17, 0x5d, 0x30, 0xda, 0x77, 0x3c, 0x14, 0x88, 0xe8, 0xe4, 0xbf, 0xee, 0x21, 0x1c, 0x29, 0x4e, 0x58, 0xa8, 0x8a, 0x5c, 0xae, 0xa2, 0x1c, 0x7c, 0x25, 0x7c, 0x1c, 0x39, 0xa4, 0x28, 0x4b, 0x78, 0x52, 0xae, 0x2c, 0xbb, 0x5f, 0xbf, 0x51, 0x09, 0x20, 0x76, 0xb2, 0x7d, 0xb1, 0x63, 0x84, 0xc5, 0x49, 0x8a, 0x73, 0xdb, 0x76, 0x1d, 0x25, 0x31, 0xf2, 0x1e, 0x19, 0x38, 0xc8, 0x3b, 0x51, 0x3c, 0x13, 0x52, 0x84, 0xae, 0xc2, 0xe4, 0x8a, 0x57, 0x0d, 0xde, 0x8d, 0x18, 0x48, 0x9a, 0xbd, 0xbf, 0xf3, 0xea, 0x79, 0x17, 0x06, 0x96, 0x72, 0x08, 0x60, 0x95, 0xf9, 0x6f, 0x25, 0x0c, 0xb7, 0x9d, 0x98, 0x23, 0x01, 0xc8, 0x7a, 0xdb, 0x75, 0x63, 0x64, 0x14, 0x5e, 0x10, 0xf5, 0x16, 0x48, 0xbc, 0xc6, 0x7e, 0x24, 0xf3, 0xad, 0x57, 0x3f, 0x7d, 0x6c, 0xab, 0x18, 0x8c, 0x12, 0xc5, 0x0c, 0xd8, 0xb5, 0x1e, 0x43, 0x7c, 0x23, 0x17, 0x48, 0xba, 0x76, 0x3b, 0xd9, 0x2b, 0xae, 0x1b, 0xef, 0x58, 0xfa, 0x87, 0xad, 0x9b, 0x6d, 0xf9, 0xab, 0xa8, 0x3c, 0xfc, 0x59, 0x67, 0xa6, 0x2c, 0xc7, 0x75, 0xa4, 0x97, 0xca, 0x18, 0x18, 0x04, 0x2c, 0xb3, 0x0e, 0xa9, 0x69, 0x33, 0x67, 0xa2, 0xc6, 0xbc, 0x98, 0x48, 0x71, 0x11, 0x05, 0x30, 0xf6, 0xa9, 0x61, 0x40, 0x46, 0xf1, 0x41, 0x37, 0xd0, 0x6b, 0x7c, 0x1f, 0x03, 0x5c, 0xe9, 0xf4, 0x59, 0x1d, 0x35, 0xf0, 0x98, 0x42, 0x4a, 0x92, 0x2a, 0xc3, 0x9a, 0xb8, 0xa5
@@ -0,0 +1,73 @@
%ifdef CONFIG
{
"RegData": {
"XMM1": ["0", "0", "0", "0"],
"XMM7": ["0x2db6b85f6135a9d7", "0xe273f1177f80d2ec", "0x7be65e82db698176", "0x3fd4ea2f8a34fff5"]
},
"Mode": "32BIT",
"Env": { "FEX_HOSTFEATURES" : "enableavx" },
"HostFeatures": ["AVX"]
}
%endif
; 256-bit
; 2x displacement
; 64-bit indexes
; Calculate an address that heavily overflows
lea eax, [rel .data_mid]
mov ebx, -1
sub ebx, eax
sar ebx, 1
mov [rel .index_overflow + 0 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 8
sar ebx, 1
mov [rel .index_overflow + 1 * 8], ebx
mov ebx, -1
sub ebx, eax
sub ebx, 16
sar ebx, 1
mov [rel .index_overflow + 2 * 8], ebx
mov ebx, -1
sub ebx, eax
add bx, 16
sar ebx, 1
mov [rel .index_overflow + 3 * 8], ebx
; Calculate new base which offsets from the overflow
lea eax, [rel .data_mid]
shl eax, 1
vmovapd ymm7, [rel .data]
vmovaps ymm0, [rel .index_overflow]
vmovaps ymm1, [rel .mask_1111]
vgatherqpd ymm7, [ymm0 * 2 + eax + 2], ymm1
hlt
align 32
.mask_1111:
dq 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000
; Indexing is a signed 64-bit integer.
.index_overflow:
dq 0, 0, 0, 0
; Random data, 512-byte per line
.data:
db 0x1d, 0x30, 0x09, 0x22, 0x56, 0xa2, 0xcd, 0xf1, 0xb7, 0x62, 0x91, 0x40, 0x67, 0x07, 0x35, 0x0f, 0x59, 0x33, 0x63, 0x52, 0x26, 0xd2, 0x2f, 0x00, 0x8a, 0x36, 0xb7, 0xf7, 0xaf, 0x4f, 0xa1, 0xc0, 0xe3, 0xbe, 0x99, 0x67, 0x5f, 0x80, 0x60, 0xcb, 0x43, 0xfa, 0x5b, 0x86, 0xb1, 0x11, 0xbc, 0xb3, 0x7b, 0x43, 0x5b, 0x45, 0x9e, 0x33, 0x89, 0xb5, 0x1b, 0xb9, 0x33, 0x4f, 0xdb, 0x5d, 0x93, 0xd6, 0x4f, 0xbc, 0x37, 0xde, 0xeb, 0xdb, 0x43, 0x2b, 0x05, 0x60, 0xb8, 0x98, 0x5c, 0xa3, 0xe3, 0x1b, 0x33, 0x03, 0x29, 0x4b, 0x12, 0x4c, 0x1e, 0xe6, 0x5e, 0x0e, 0x6c, 0xa1, 0xb9, 0x36, 0xfa, 0x6c, 0x7f, 0xc6, 0xa8, 0x38, 0x73, 0x2a, 0x0a, 0x25, 0x69, 0xa5, 0x97, 0x3f, 0x24, 0x00, 0x30, 0x4d, 0x27, 0xb3, 0x94, 0x48, 0xef, 0x47, 0x98, 0x71, 0x0d, 0x56, 0x76, 0xec, 0x41, 0x12, 0x9b, 0x7b, 0x9c, 0xf5, 0x85, 0x07, 0x2d, 0x6b, 0xc6, 0xc1, 0x2e, 0x72, 0x22, 0x5a, 0x43, 0xff, 0x1e, 0xec, 0x67, 0x2b, 0x31, 0x96, 0x14, 0x2c, 0xb1, 0x5f, 0x5d, 0x0c, 0xc9, 0xad, 0x15, 0x5f, 0xab, 0x66, 0x14, 0x1c, 0x72, 0xfa, 0x23, 0xef, 0x9f, 0x77, 0xf6, 0x50, 0xb0, 0x70, 0xb8, 0x3c, 0x85, 0x9e, 0x90, 0x69, 0x17, 0x25, 0xae, 0x6e, 0xe2, 0x16, 0x7d, 0x42, 0x38, 0xdf, 0x74, 0x72, 0x7b, 0x97, 0xa9, 0x9e, 0x40, 0x24, 0x85, 0xdc, 0x64, 0xfa, 0xb1, 0x8b, 0x95, 0xe6, 0xe4, 0x13, 0x72, 0xf1, 0x52, 0x2f, 0xa0, 0xd6, 0x52, 0xc0, 0x11, 0xa7, 0xfe, 0xd5, 0x3b, 0x56, 0xca, 0xbc, 0x01, 0xce, 0x3d, 0xd2, 0x30, 0x97, 0x1d, 0xdc, 0xeb, 0x9d, 0xa9, 0x3e, 0x09, 0xef, 0xee, 0x7f, 0x09, 0x7b, 0x82, 0x43, 0x15, 0x2e, 0xa4, 0x2e, 0x97, 0x21, 0x92, 0x7e, 0x69, 0x21, 0x25, 0xda, 0x46, 0x7c, 0x0c, 0xcd, 0x1d, 0xde, 0x42, 0x11, 0xa2, 0xef, 0xa2, 0xc8, 0x32, 0x9a, 0x82, 0xcf, 0x72, 0x7e, 0x22, 0xa6, 0x11, 0xfa, 0xec, 0x0b, 0x77, 0x99, 0x38, 0x03, 0xf6, 0x80, 0xba, 0xea, 0x75, 0x19, 0xb0, 0x48, 0x02, 0xb2, 0x6b, 0xc0, 0x8c, 0xfb, 0xfe, 0xaf, 0x94, 0x4f, 0x6f, 0xb4, 0xcb, 0x1c, 0x27, 0xf0, 0x41, 0xb6, 0x46, 0x41, 0x68, 0x3d, 0x05, 0x79, 0x6b, 0xcd, 0xb7, 0x20, 0xdc, 0x40, 0x81, 0x58, 0xcb, 0x33, 0xa3, 0xf3, 0x34, 0xdc, 0x63, 0x2d, 0xa5, 0xb5, 0xa1, 0xd1, 0xfd, 0x49, 0x5b, 0x46, 0x94, 0x01, 0xa8, 0xf2, 0xd8, 0x93, 0x2c, 0xbb, 0x57, 0xfe, 0x7c, 0x77, 0x3b, 0x19, 0x6f, 0x3c, 0xaa, 0x23, 0x5b, 0xc0, 0xe7, 0x00, 0x41, 0x97, 0x91, 0xe8, 0x00, 0x12, 0xdf, 0xf6, 0x5c, 0x2e, 0xc6, 0x8e, 0xc6, 0x77, 0x59, 0x78, 0x9b, 0xef, 0x63, 0xb0, 0xd7, 0xbb, 0xc4, 0x0b, 0x60, 0x65, 0x3f, 0xfe, 0xbf, 0x04, 0x3e, 0xae, 0xc2, 0xa5, 0x90, 0xe1, 0x2a, 0x56, 0x3f, 0x4c, 0x3f, 0x7a, 0x7d, 0xda, 0x81, 0x50, 0xea, 0x4c, 0xfe, 0xc3, 0xf8, 0x5c, 0x2b, 0x67, 0xb3, 0x9f, 0x8b, 0x95, 0xda, 0x6f, 0x5d, 0xdd, 0x82, 0x7f, 0x52, 0xa2, 0xcc, 0x57, 0xec, 0xc4, 0x14, 0xd2, 0x4f, 0x1b, 0xcb, 0xea, 0xaf, 0x0e, 0x0f, 0x53, 0xaa, 0x56, 0x63, 0xea, 0x36, 0xa6, 0x89, 0x1a, 0x66, 0xc0, 0x4e, 0xf4, 0x1e, 0x02, 0x43, 0xde, 0xde, 0xc8, 0x9e, 0x88, 0x6e, 0x32, 0xd4, 0xcb, 0x47, 0x24, 0x7c, 0x28, 0x38, 0xd4, 0x95, 0xb6, 0xa3, 0x91, 0x69, 0xc7, 0x8d, 0xfd, 0x15, 0xf5, 0xbf, 0xb1, 0x98, 0x8c, 0x57, 0x51, 0xbf, 0x83, 0x6a, 0x35, 0x10, 0x03, 0x50, 0xe5, 0xf7, 0xfa, 0xf8, 0xa5, 0xb0, 0xdb, 0xfb, 0x42, 0x93, 0xbb, 0x17, 0xf7, 0x36, 0xbe, 0x26, 0x66, 0x61, 0xe2
db 0xaf, 0xe0, 0x22, 0x25, 0x2a, 0xae, 0x78, 0x67, 0x8f, 0x7e, 0x9e, 0x59, 0xd7, 0xa3, 0x71, 0xcc, 0x43, 0x85, 0x09, 0xf9, 0x18, 0x52, 0x7b, 0x01, 0x73, 0xcb, 0x31, 0x18, 0x66, 0x79, 0x67, 0x10, 0x67, 0xd8, 0xdf, 0x43, 0xaf, 0x2d, 0x9a, 0x09, 0x9c, 0xd1, 0x37, 0x7e, 0xf5, 0x1c, 0x3c, 0x4f, 0x15, 0xe1, 0x6f, 0xfd, 0x13, 0x3d, 0x53, 0x81, 0xa9, 0x93, 0x5f, 0x92, 0x41, 0x48, 0xec, 0x87, 0x87, 0x1d, 0x0b, 0xaa, 0xaa, 0xd3, 0xc2, 0x98, 0x20, 0xce, 0x28, 0xaf, 0x9d, 0x84, 0x69, 0x4a, 0xfd, 0xc0, 0x9c, 0x2e, 0x50, 0x20, 0xb2, 0x00, 0xc1, 0x81, 0x2a, 0x32, 0x8e, 0x95, 0x20, 0xa7, 0xca, 0x39, 0x28, 0x12, 0x23, 0x0e, 0x43, 0xd3, 0x82, 0x76, 0x73, 0x3c, 0xbf, 0xa9, 0x98, 0xf6, 0x39, 0x6d, 0xd9, 0x15, 0x33, 0x1e, 0x07, 0x7c, 0x08, 0x12, 0x23, 0xbd, 0xd3, 0x34, 0x2d, 0x9a, 0x23, 0x21, 0x46, 0xf3, 0x9a, 0x04, 0x25, 0x62, 0xeb, 0x7e, 0x9a, 0xaa, 0xb6, 0x26, 0xaa, 0x85, 0x01, 0x3a, 0xd8, 0xfc, 0x57, 0x98, 0xb9, 0xe4, 0xc4, 0xe9, 0x11, 0x3e, 0x22, 0x95, 0x3b, 0x41, 0x2b, 0x02, 0x04, 0x6c, 0x75, 0xa5, 0xf2, 0xaa, 0x09, 0x9e, 0x6f, 0xab, 0x1d, 0x2a, 0x5c, 0xde, 0x21, 0xb1, 0x96, 0x2d, 0x86, 0x3f, 0xd0, 0x07, 0x18, 0x1f, 0x87, 0xc2, 0x8f, 0xdf, 0x6a, 0x57, 0x6d, 0x3f, 0x80, 0xc5, 0x08, 0x19, 0xa5, 0x09, 0x65, 0x3d, 0xdc, 0x9e, 0x80, 0x3c, 0x2a, 0x0e, 0x7a, 0x40, 0x04, 0x0b, 0xcc, 0x61, 0xdb, 0x73, 0xfc, 0xa5, 0x0a, 0x42, 0x18, 0xc1, 0xd5, 0xbd, 0x18, 0x78, 0xa1, 0xe4, 0xde, 0x44, 0xec, 0x79, 0xb0, 0x27, 0xaa, 0x45, 0x21, 0x57, 0x19, 0x75, 0x09, 0x5c, 0x58, 0xd5, 0xb9, 0x6f, 0x3b, 0x48, 0x59, 0x41, 0x3e, 0xfd, 0x17, 0x43, 0x27, 0xc3, 0x8d, 0x76, 0x8e, 0x38, 0x47, 0xe7, 0xd2, 0xea, 0x54, 0x73, 0x8a, 0x65, 0x4c, 0x49, 0x91, 0xaf, 0x29, 0x65, 0x0d, 0x81, 0xa4, 0x77, 0xd7, 0x32, 0xd0, 0x69, 0xd9, 0x6b, 0xa3, 0x9b, 0x24, 0xd6, 0x0a, 0xd2, 0x77, 0x38, 0x59, 0x0b, 0xc8, 0x5c, 0xc7, 0x0b, 0x1d, 0xd1, 0xfa, 0xa7, 0x45, 0x3c, 0xeb, 0x5c, 0x8e, 0x25, 0x35, 0x81, 0x6d, 0x6d, 0xfe, 0xb4, 0x63, 0x89, 0xe4, 0xf0, 0xa8, 0xda, 0xb7, 0xd4, 0xff, 0x5d, 0x28, 0x97, 0x11, 0xf9, 0x8d, 0xab, 0x29, 0xd5, 0xd3, 0x1c, 0x70, 0x20, 0x4c, 0x41, 0x16, 0x42, 0xfd, 0xfc, 0x62, 0x82, 0x40, 0x59, 0x34, 0x28, 0xd0, 0xd5, 0xfc, 0xac, 0x97, 0xb8, 0x82, 0x0e, 0x4b, 0xae, 0x51, 0x28, 0x1a, 0xf1, 0x87, 0xd3, 0x20, 0xa3, 0xe7, 0x74, 0x69, 0x3c, 0x54, 0x8d, 0xc5, 0x56, 0x1d, 0xcd, 0x75, 0xae, 0x88, 0x17, 0x30, 0xdf, 0x46, 0x4a, 0xbc, 0x64, 0xff, 0xa2, 0xc1, 0x62, 0xbd, 0x88, 0x7b, 0x3e, 0xa1, 0x0c, 0xa9, 0x13, 0x0e, 0xc1, 0xb4, 0x24, 0xe6, 0x96, 0x1b, 0x9c, 0x9b, 0xac, 0x44, 0x33, 0x5b, 0xda, 0xd5, 0x88, 0x4d, 0xfe, 0x81, 0x09, 0x07, 0x17, 0xcf, 0x14, 0x05, 0xaf, 0xf8, 0x72, 0x14, 0x49, 0x5f, 0x06, 0x62, 0xab, 0xe0, 0x42, 0x70, 0x12, 0x59, 0x41, 0x0f, 0x18, 0x83, 0x68, 0x6d, 0xc6, 0x3c, 0xea, 0xe0, 0x6d, 0xd4, 0xae, 0xa6, 0xf1, 0x63, 0x21, 0x7f, 0xb5, 0x9d, 0x22, 0xf4, 0xd2, 0x49, 0x49, 0xed, 0x07, 0xb1, 0x11, 0xf9, 0x2e, 0x74, 0xbe, 0x35, 0x47, 0xdc, 0xef, 0x85, 0x0b, 0x4d, 0x46, 0xe6, 0x1f, 0x60, 0x6a, 0xa1, 0x8a, 0x4d, 0x46, 0x87, 0x30, 0x8e, 0x9a, 0xba, 0x97, 0x3e, 0x15, 0xb7, 0x33, 0x76, 0x81, 0x69, 0xdb, 0x82, 0x5e, 0xe6, 0x7b, 0xec, 0xd2, 0x80, 0x7f, 0x17, 0xf1, 0x73, 0xe2
.data_mid:
db 0xd7, 0xa9, 0x35, 0x61, 0x5f, 0xb8, 0xb6, 0x2d, 0x29, 0x34, 0x63, 0xbf, 0xe2, 0x1c, 0x34, 0xe9, 0xf5, 0xff, 0x34, 0x8a, 0x2f, 0xea, 0xd4, 0x3f, 0x3b, 0xfe, 0x6e, 0xdf, 0xa6, 0xd8, 0xc6, 0xb8, 0xc5, 0xff, 0x12, 0x97, 0x53, 0xda, 0x86, 0xa8, 0x0a, 0x12, 0x4e, 0x5d, 0x96, 0x65, 0x51, 0x22, 0xe2, 0x9d, 0x08, 0x71, 0x84, 0x19, 0x8b, 0xbf, 0x29, 0xd3, 0x3f, 0xab, 0xde, 0xe4, 0x27, 0x8b, 0x99, 0xcc, 0xb1, 0x7c, 0xa5, 0x71, 0x91, 0x9a, 0x0b, 0xad, 0x75, 0x86, 0xe3, 0x9c, 0x4e, 0x0c, 0x01, 0xb3, 0x12, 0x33, 0x90, 0x81, 0x7c, 0x71, 0x2c, 0x70, 0x61, 0xd5, 0x39, 0x0c, 0x45, 0xfc, 0x27, 0xaf, 0xbb, 0xd9, 0x26, 0x1b, 0x33, 0xb4, 0x0d, 0xf8, 0xd6, 0x2d, 0x09, 0xc7, 0x8c, 0xbf, 0x48, 0x53, 0x14, 0x94, 0x76, 0x25, 0xc7, 0x0c, 0x69, 0x49, 0x82, 0xb4, 0x2f, 0x48, 0x38, 0x44, 0x9d, 0x90, 0x6d, 0x66, 0x35, 0xe9, 0x3e, 0x2f, 0x2a, 0xb7, 0xe1, 0xb1, 0x2b, 0x99, 0x08, 0x6f, 0x5c, 0x6c, 0xdf, 0xdb, 0x10, 0xe2, 0xaa, 0x86, 0xe7, 0xf8, 0x9e, 0x62, 0xde, 0xa5, 0x81, 0x6b, 0x20, 0x47, 0xa9, 0x06, 0x49, 0xc0, 0x78, 0x8c, 0x70, 0x93, 0x7e, 0xda, 0xda, 0x5e, 0x3b, 0x23, 0xf9, 0xcc, 0x87, 0xdf, 0x48, 0x4f, 0xd6, 0x77, 0xce, 0x45, 0xe1, 0xdc, 0x0c, 0x7a, 0x0c, 0x50, 0x15, 0x63, 0x8c, 0x48, 0xd3, 0x8e, 0xfa, 0xcc, 0xac, 0x1a, 0x83, 0xde, 0xb1, 0x87, 0x2a, 0x58, 0x5c, 0xa5, 0x20, 0x3d, 0xaa, 0x1e, 0x5d, 0x71, 0xa6, 0x57, 0x75, 0x82, 0xb7, 0x33, 0x9e, 0x6b, 0xf3, 0x35, 0x02, 0x98, 0x03, 0xe1, 0x3b, 0xd2, 0x9f, 0x7a, 0x06, 0x85, 0xef, 0x7d, 0xd9, 0xf2, 0x0c, 0x9e, 0xce, 0xb9, 0xce, 0x13, 0x4a, 0x9e, 0x8a, 0x29, 0xe6, 0xe5, 0xe4, 0x39, 0xba, 0xfd, 0xa3, 0x33, 0xa8, 0x13, 0x9e, 0xa5, 0x11, 0x37, 0x69, 0xbc, 0xda, 0x11, 0x49, 0x2d, 0x4a, 0xef, 0x20, 0x8b, 0x7a, 0xb8, 0x9c, 0xc3, 0xaf, 0x26, 0x71, 0xd9, 0xa2, 0xf6, 0x0f, 0x85, 0x87, 0xa8, 0x6c, 0xf9, 0x99, 0xa2, 0xb2, 0x36, 0x2d, 0x78, 0x10, 0xe4, 0x33, 0x8d, 0xa4, 0x63, 0xea, 0x02, 0xb9, 0xac, 0x2f, 0x90, 0x39, 0x2d, 0x0e, 0x2e, 0xf5, 0x08, 0xa5, 0x5c, 0x8e, 0x71, 0x30, 0x0d, 0x1b, 0x84, 0x7a, 0xd7, 0xd4, 0xab, 0x81, 0x82, 0x18, 0x37, 0xf3, 0x28, 0x6f, 0x4e, 0x28, 0x71, 0xda, 0xc9, 0x99, 0x46, 0x14, 0x46, 0x77, 0x01, 0x16, 0x21, 0xae, 0x83, 0x93, 0x86, 0x7f, 0x5a, 0xee, 0xd5, 0xdf, 0x48, 0x5b, 0x15, 0xc8, 0x09, 0x30, 0x8f, 0x01, 0xcc, 0x95, 0x30, 0xd9, 0xf7, 0x72, 0x97, 0xfd, 0x9d, 0xec, 0x9f, 0xbf, 0x5c, 0xbf, 0x4f, 0xca, 0x33, 0xb4, 0xd2, 0xa2, 0xb9, 0x08, 0x9c, 0x40, 0x25, 0x3f, 0x86, 0xdc, 0x83, 0x70, 0x2f, 0xfb, 0x2a, 0xf8, 0x61, 0x1f, 0xa1, 0x1f, 0x36, 0x04, 0xe2, 0xef, 0x1c, 0xa4, 0xcd, 0x3c, 0x7f, 0xc5, 0x73, 0x9c, 0x2e, 0xeb, 0x03, 0x79, 0xd1, 0x02, 0xfc, 0x6f, 0xbd, 0x5a, 0x95, 0xb2, 0xf6, 0x25, 0x96, 0xe6, 0x80, 0x0a, 0xc5, 0xc7, 0xca, 0x8d, 0x31, 0xae, 0xf0, 0x49, 0xcf, 0x43, 0x06, 0x27, 0x7f, 0x25, 0xc7, 0x4c, 0xb7, 0xfc, 0x73, 0xd3, 0x04, 0xd3, 0xb9, 0x9f, 0x74, 0xed, 0x9e, 0x3c, 0xf0, 0xcf, 0x26, 0x2b, 0xd9, 0xcb, 0x78, 0x2a, 0xef, 0x72, 0xf7, 0xb6, 0x78, 0x30, 0x2d, 0x8c, 0x83, 0x73, 0x66, 0x74, 0x3d, 0x66, 0x0a, 0x74, 0x5a, 0x3f, 0x9f, 0x6e, 0x56, 0x68, 0x01, 0xc2, 0xca, 0x2b, 0xa1, 0x25, 0x36, 0x9c, 0x3b, 0xa4, 0x5e, 0x44, 0xf1, 0x18, 0x1d, 0xb6, 0x1a, 0x3a, 0xee, 0x8d, 0x67, 0x34, 0x9c
db 0xdd, 0x48, 0x14, 0xc2, 0x5f, 0xd8, 0xe5, 0x71, 0x22, 0xbf, 0xbc, 0x84, 0xda, 0xc1, 0xb1, 0x22, 0x55, 0xa4, 0x63, 0x41, 0x77, 0xac, 0x40, 0x2d, 0x44, 0x73, 0x8c, 0x14, 0xba, 0x5e, 0x63, 0x68, 0x65, 0x61, 0x6d, 0xec, 0xe2, 0x6d, 0x37, 0x22, 0x04, 0xeb, 0xc7, 0xd4, 0xc9, 0x62, 0x56, 0x13, 0x96, 0x29, 0x03, 0xf4, 0x55, 0xe2, 0x58, 0x7d, 0xda, 0x52, 0x2e, 0x94, 0x07, 0xe6, 0xef, 0xc0, 0xee, 0x9e, 0x0b, 0xf7, 0xcd, 0x13, 0x8b, 0x7d, 0xea, 0xdc, 0xf8, 0xf1, 0xcb, 0xad, 0x49, 0x97, 0xc9, 0x98, 0x0b, 0xcf, 0x84, 0x8e, 0x8e, 0xbb, 0x06, 0x2e, 0x54, 0xf5, 0xa7, 0xbd, 0x70, 0x7e, 0x38, 0x69, 0x8d, 0xb0, 0x01, 0x7b, 0x41, 0x80, 0x09, 0x44, 0xfd, 0x7e, 0x21, 0xb4, 0xbe, 0x6b, 0x4a, 0xb7, 0xca, 0x2d, 0x19, 0xfe, 0x6d, 0xd6, 0x11, 0x29, 0xbb, 0xb2, 0x16, 0xf1, 0xe7, 0x92, 0x71, 0xda, 0x7e, 0x68, 0x3a, 0xe0, 0xea, 0x89, 0x8d, 0xe0, 0x44, 0x48, 0x25, 0x92, 0x37, 0x54, 0x26, 0xf2, 0xab, 0xb3, 0x3b, 0xdb, 0xbb, 0x2b, 0x5c, 0xf5, 0xbc, 0xc7, 0x97, 0xdb, 0xc7, 0x49, 0x25, 0x7c, 0xc2, 0x80, 0x02, 0x69, 0xd4, 0xda, 0xda, 0xe1, 0x04, 0xf3, 0x19, 0xb8, 0xc9, 0xb2, 0xfb, 0x1e, 0x47, 0xa9, 0x0c, 0xa3, 0x48, 0xce, 0xc2, 0x9e, 0x3b, 0x28, 0x23, 0x5a, 0x20, 0x44, 0x77, 0x40, 0xe2, 0xd7, 0x20, 0xd5, 0x71, 0x6f, 0xd4, 0x3c, 0x68, 0x38, 0x9b, 0x89, 0x2e, 0x2d, 0xa8, 0x1f, 0x99, 0xb5, 0x8a, 0x66, 0x07, 0x59, 0x75, 0x9e, 0xf8, 0xd9, 0xbe, 0x85, 0x6a, 0x20, 0x92, 0x9d, 0xd2, 0x5e, 0x45, 0xc0, 0x60, 0xbe, 0x85, 0x0b, 0x84, 0x47, 0xf5, 0xa8, 0x43, 0x87, 0xf1, 0x21, 0x21, 0xb0, 0x3b, 0x04, 0x13, 0x16, 0x3e, 0xdf, 0xc3, 0xc6, 0x04, 0x73, 0xcd, 0x92, 0x76, 0xfb, 0xe7, 0x9c, 0xd3, 0x46, 0x11, 0x78, 0xca, 0x12, 0xd9, 0x4a, 0x35, 0xf1, 0x6e, 0x89, 0x8b, 0xe9, 0x7a, 0x04, 0xba, 0x18, 0x25, 0x7c, 0x9e, 0xe6, 0x4f, 0xc2, 0x56, 0x05, 0x72, 0xc3, 0x76, 0xee, 0x7d, 0x77, 0x19, 0x7a, 0x73, 0x2c, 0x81, 0xb8, 0xc7, 0xd9, 0x7f, 0x17, 0x5d, 0x30, 0xda, 0x77, 0x3c, 0x14, 0x88, 0xe8, 0xe4, 0xbf, 0xee, 0x21, 0x1c, 0x29, 0x4e, 0x58, 0xa8, 0x8a, 0x5c, 0xae, 0xa2, 0x1c, 0x7c, 0x25, 0x7c, 0x1c, 0x39, 0xa4, 0x28, 0x4b, 0x78, 0x52, 0xae, 0x2c, 0xbb, 0x5f, 0xbf, 0x51, 0x09, 0x20, 0x76, 0xb2, 0x7d, 0xb1, 0x63, 0x84, 0xc5, 0x49, 0x8a, 0x73, 0xdb, 0x76, 0x1d, 0x25, 0x31, 0xf2, 0x1e, 0x19, 0x38, 0xc8, 0x3b, 0x51, 0x3c, 0x13, 0x52, 0x84, 0xae, 0xc2, 0xe4, 0x8a, 0x57, 0x0d, 0xde, 0x8d, 0x18, 0x48, 0x9a, 0xbd, 0xbf, 0xf3, 0xea, 0x79, 0x17, 0x06, 0x96, 0x72, 0x08, 0x60, 0x95, 0xf9, 0x6f, 0x25, 0x0c, 0xb7, 0x9d, 0x98, 0x23, 0x01, 0xc8, 0x7a, 0xdb, 0x75, 0x63, 0x64, 0x14, 0x5e, 0x10, 0xf5, 0x16, 0x48, 0xbc, 0xc6, 0x7e, 0x24, 0xf3, 0xad, 0x57, 0x3f, 0x7d, 0x6c, 0xab, 0x18, 0x8c, 0x12, 0xc5, 0x0c, 0xd8, 0xb5, 0x1e, 0x43, 0x7c, 0x23, 0x17, 0x48, 0xba, 0x76, 0x3b, 0xd9, 0x2b, 0xae, 0x1b, 0xef, 0x58, 0xfa, 0x87, 0xad, 0x9b, 0x6d, 0xf9, 0xab, 0xa8, 0x3c, 0xfc, 0x59, 0x67, 0xa6, 0x2c, 0xc7, 0x75, 0xa4, 0x97, 0xca, 0x18, 0x18, 0x04, 0x2c, 0xb3, 0x0e, 0xa9, 0x69, 0x33, 0x67, 0xa2, 0xc6, 0xbc, 0x98, 0x48, 0x71, 0x11, 0x05, 0x30, 0xf6, 0xa9, 0x61, 0x40, 0x46, 0xf1, 0x41, 0x37, 0xd0, 0x6b, 0x7c, 0x1f, 0x03, 0x5c, 0xe9, 0xf4, 0x59, 0x1d, 0x35, 0xf0, 0x98, 0x42, 0x4a, 0x92, 0x2a, 0xc3, 0x9a, 0xb8, 0xa5
Loaded 100 of 163 files, more files were not shown because too many files have changed in this diff. Show more